2 Commits
main ... dev

110 changed files with 2246 additions and 2446 deletions

View File

@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>net.miarma.api</groupId> <groupId>net.miarma.api</groupId>
<artifactId>backlib</artifactId> <artifactId>backlib</artifactId>
<version>1.2.1</version> <version>2.0.0</version>
<properties> <properties>
<maven.compiler.source>23</maven.compiler.source> <maven.compiler.source>23</maven.compiler.source>
@@ -24,11 +24,11 @@
<distributionManagement> <distributionManagement>
<repository> <repository>
<id>gitea</id> <id>MiarmaGit</id>
<url>https://git.miarma.net/api/packages/Gallardo7761/maven</url> <url>https://git.miarma.net/api/packages/Gallardo7761/maven</url>
</repository> </repository>
<snapshotRepository> <snapshotRepository>
<id>gitea</id> <id>MiarmaGit</id>
<url>https://git.miarma.net/api/packages/Gallardo7761/maven</url> <url>https://git.miarma.net/api/packages/Gallardo7761/maven</url>
</snapshotRepository> </snapshotRepository>
</distributionManagement> </distributionManagement>

View File

@@ -1,469 +0,0 @@
package net.miarma.api.backlib;
import java.time.LocalDateTime;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.vertx.core.json.JsonObject;
import net.miarma.api.backlib.gson.APIDontReturnExclusionStrategy;
import net.miarma.api.backlib.gson.JsonObjectTypeAdapter;
import net.miarma.api.backlib.gson.LocalDateTimeAdapter;
import net.miarma.api.backlib.gson.ValuableEnumDeserializer;
import net.miarma.api.backlib.gson.ValuableEnumTypeAdapter;
import net.miarma.api.backlib.interfaces.IUserRole;
/**
* Clase que contiene constantes y enumeraciones utilizadas en la API de MiarmaCore.
* @author José Manuel Amador Gallardo
*/
public class Constants {
public static final String APP_NAME = "MiarmaCoreAPI";
public static final String BASE_PREFIX = "/api";
public static final String CORE_PREFIX = BASE_PREFIX + "/core/v1"; // tabla de usuarios central
public static final String AUTH_PREFIX = "/auth/v1";
public static final String HUERTOS_PREFIX = BASE_PREFIX + "/huertos/v1";
public static final String MMC_PREFIX = BASE_PREFIX + "/mmc/v1";
public static final String CINE_PREFIX = BASE_PREFIX + "/cine/v1";
public static final String MPASTE_PREFIX = BASE_PREFIX + "/mpaste/v1";
public static final String AUTH_EVENT_BUS = "auth.eventbus";
public static final String CORE_EVENT_BUS = "core.eventbus";
public static final String HUERTOS_EVENT_BUS = "huertos.eventbus";
public static final String MMC_EVENT_BUS = "mmc.eventbus";
public static final String CINE_EVENT_BUS = "cine.eventbus";
public static final String MPASTE_EVENT_BUS = "mpaste.eventbus";
public static final List<String> HUERTOS_ALLOWED_FOLDERS =
List.of("INBOX", "Drafts", "Sent", "Spam", "Trash");
public static final Logger LOGGER = LoggerFactory.getLogger(Constants.APP_NAME);
public static final Gson GSON = new GsonBuilder()
.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter())
.registerTypeAdapter(JsonObject.class, new JsonObjectTypeAdapter())
.registerTypeHierarchyAdapter(ValuableEnum.class, new ValuableEnumTypeAdapter())
.registerTypeAdapter(CoreUserGlobalStatus.class, new ValuableEnumDeserializer())
.registerTypeAdapter(CoreUserRole.class, new ValuableEnumDeserializer())
.registerTypeAdapter(HuertosUserType.class, new ValuableEnumDeserializer())
.registerTypeAdapter(HuertosUserStatus.class, new ValuableEnumDeserializer())
.registerTypeAdapter(HuertosUserRole.class, new ValuableEnumDeserializer())
.registerTypeAdapter(HuertosRequestStatus.class, new ValuableEnumDeserializer())
.registerTypeAdapter(HuertosRequestType.class, new ValuableEnumDeserializer())
.registerTypeAdapter(HuertosPaymentType.class, new ValuableEnumDeserializer())
.registerTypeAdapter(HuertosPaymentFrequency.class, new ValuableEnumDeserializer())
.registerTypeAdapter(HuertosAnnouncePriority.class, new ValuableEnumDeserializer())
.registerTypeAdapter(MMCUserStatus.class, new ValuableEnumDeserializer())
.registerTypeAdapter(MMCUserRole.class, new ValuableEnumDeserializer())
.registerTypeAdapter(CoreFileContext.class, new ValuableEnumDeserializer())
.registerTypeAdapter(MMCModStatus.class, new ValuableEnumDeserializer())
.registerTypeAdapter(CineUserRole.class, new ValuableEnumDeserializer())
.registerTypeAdapter(CineUserStatus.class, new ValuableEnumDeserializer())
.addSerializationExclusionStrategy(new APIDontReturnExclusionStrategy())
.create();
public enum CoreUserRole implements IUserRole {
USER(0),
ADMIN(1);
private final int value;
CoreUserRole(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static CoreUserRole fromInt(int i) {
for (CoreUserRole role : values()) {
if (role.value == i) return role;
}
throw new IllegalArgumentException("Invalid CoreUserRole value: " + i);
}
}
public enum CoreUserGlobalStatus implements ValuableEnum {
INACTIVE(0),
ACTIVE(1);
private final int value;
CoreUserGlobalStatus(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static CoreUserGlobalStatus fromInt(int i) {
for (CoreUserGlobalStatus status : values()) {
if (status.value == i) return status;
}
throw new IllegalArgumentException("Invalid CoreUserGlobalStatus value: " + i);
}
}
public enum CoreFileContext implements ValuableEnum {
CORE(0),
HUERTOS(1),
MMC(2),
CINE(3);
private final int value;
CoreFileContext(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public String toCtxString() {
return switch(this) {
case CORE -> "core";
case HUERTOS -> "huertos";
case MMC -> "miarmacraft";
case CINE -> "cine";
};
}
public static CoreFileContext fromInt(int i) {
for (CoreFileContext context : values()) {
if (context.value == i) return context;
}
throw new IllegalArgumentException("Invalid CoreFileContext value: " + i);
}
}
public enum HuertosUserRole implements IUserRole {
USER(0),
ADMIN(1),
DEV(2);
private final int value;
HuertosUserRole(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static HuertosUserRole fromInt(int i) {
for (HuertosUserRole role : values()) {
if (role.value == i) return role;
}
throw new IllegalArgumentException("Invalid HuertosUserRole value: " + i);
}
}
public enum HuertosUserType implements ValuableEnum {
WAIT_LIST(0),
MEMBER(1),
WITH_GREENHOUSE(2),
COLLABORATOR(3),
DEVELOPER(5),
SUBSIDY(4);
private final int value;
HuertosUserType(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static HuertosUserType fromInt(int i) {
for (HuertosUserType type : values()) {
if (type.value == i) return type;
}
throw new IllegalArgumentException("Invalid HuertosUserType value: " + i);
}
}
public enum HuertosUserStatus implements ValuableEnum {
INACTIVE(0),
ACTIVE(1);
private final int value;
HuertosUserStatus(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static HuertosUserStatus fromInt(int i) {
for (HuertosUserStatus status : values()) {
if (status.value == i) return status;
}
throw new IllegalArgumentException("Invalid HuertosUserStatus value: " + i);
}
}
public enum HuertosRequestStatus implements ValuableEnum {
PENDING(0),
APPROVED(1),
REJECTED(2);
private final int value;
HuertosRequestStatus(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static HuertosRequestStatus fromInt(int i) {
for (HuertosRequestStatus status : values()) {
if (status.value == i) return status;
}
throw new IllegalArgumentException("Invalid HuertoRequestStatus value: " + i);
}
}
public enum HuertosPaymentType implements ValuableEnum {
BANK(0),
CASH(1);
private final int value;
HuertosPaymentType(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static HuertosPaymentType fromInt(int i) {
for (HuertosPaymentType type : values()) {
if (type.value == i) return type;
}
throw new IllegalArgumentException("Invalid HuertoPaymentType value: " + i);
}
}
public enum HuertosRequestType implements ValuableEnum {
REGISTER(0),
UNREGISTER(1),
ADD_COLLABORATOR(2),
REMOVE_COLLABORATOR(3),
ADD_GREENHOUSE(4),
REMOVE_GREENHOUSE(5);
private final int value;
HuertosRequestType(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static HuertosRequestType fromInt(int i) {
for (HuertosRequestType type : values()) {
if (type.value == i) return type;
}
throw new IllegalArgumentException("Invalid HuertoRequestType value: " + i);
}
}
public enum HuertosAnnouncePriority implements ValuableEnum {
LOW(0),
MEDIUM(1),
HIGH(2);
private final int value;
HuertosAnnouncePriority(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static HuertosAnnouncePriority fromInt(int i) {
for (HuertosAnnouncePriority priority : values()) {
if (priority.value == i) return priority;
}
throw new IllegalArgumentException("Invalid HuertoAnnouncePriority value: " + i);
}
}
public enum HuertosPaymentFrequency implements ValuableEnum {
BIYEARLY(0),
YEARLY(1);
private final int value;
HuertosPaymentFrequency(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static HuertosPaymentFrequency fromInt(int i) {
for (HuertosPaymentFrequency frequency : values()) {
if (frequency.value == i) return frequency;
}
throw new IllegalArgumentException("Invalid HuertoPaymentFrequency value: " + i);
}
}
public enum MMCUserRole implements IUserRole {
PLAYER(0),
ADMIN(1);
private final int value;
MMCUserRole(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static MMCUserRole fromInt(int i) {
for (MMCUserRole role : values()) {
if (role.value == i) return role;
}
throw new IllegalArgumentException("Invalid MMCUserRole value: " + i);
}
}
public enum MMCUserStatus implements ValuableEnum {
INACTIVE(0),
ACTIVE(1);
private final int value;
MMCUserStatus(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static MMCUserStatus fromInt(int i) {
for (MMCUserStatus status : values()) {
if (status.value == i) return status;
}
throw new IllegalArgumentException("Invalid MMCUserStatus value: " + i);
}
}
public enum MMCModStatus implements ValuableEnum {
ACTIVE(0),
INACTIVE(1);
private final int value;
MMCModStatus(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static MMCModStatus fromInt(int i) {
for (MMCModStatus status : values()) {
if (status.value == i) return status;
}
throw new IllegalArgumentException("Invalid MiarmacraftModStatus value: " + i);
}
}
public enum CineUserStatus implements ValuableEnum {
ACTIVE(1),
INACTIVE(0);
private final int value;
CineUserStatus(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static CineUserStatus fromInt(int i) {
for (CineUserStatus status : values()) {
if (status.value == i) return status;
}
throw new IllegalArgumentException("Invalid CineUserStatus value: " + i);
}
}
public enum CineUserRole implements IUserRole {
USER(0),
ADMIN(1);
private final int value;
CineUserRole(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static CineUserRole fromInt(int i) {
for (CineUserRole role : values()) {
if (role.value == i) return role;
}
throw new IllegalArgumentException("Invalid CineUserRole value: " + i);
}
}
// Private constructor to prevent instantiation
private Constants() {
throw new AssertionError("Utility class cannot be instantiated.");
}
}

View File

@@ -1,4 +1,4 @@
package net.miarma.api.backlib; package net.miarma.api.backlib.config;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@@ -8,6 +8,10 @@ import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Properties; import java.util.Properties;
import org.slf4j.Logger;
import net.miarma.api.backlib.log.LoggerProvider;
/** /**
* Gestión de toda la configuración de la aplicación. * Gestión de toda la configuración de la aplicación.
* Se encarga de cargar, guardar y proporcionar acceso a las propiedades de configuración. * Se encarga de cargar, guardar y proporcionar acceso a las propiedades de configuración.
@@ -19,14 +23,16 @@ import java.util.Properties;
*/ */
public class ConfigManager { public class ConfigManager {
private static ConfigManager instance; private static ConfigManager instance;
private final String appName;
private final File configFile; private final File configFile;
private final Properties config; private final Properties config;
private static final String CONFIG_FILE_NAME = "config.properties"; private static final String CONFIG_FILE_NAME = "config.properties";
private final Logger LOGGER = LoggerProvider.getLogger();
private ConfigManager() { private ConfigManager() {
String path = getBaseDir() + CONFIG_FILE_NAME; this.appName = System.getenv().getOrDefault("APP_NAME", "miarma-backend");
this.configFile = new File(path);
this.config = new Properties(); this.config = new Properties();
this.configFile = new File(getBaseDir() + CONFIG_FILE_NAME);
} }
public static synchronized ConfigManager getInstance() { public static synchronized ConfigManager getInstance() {
@@ -41,7 +47,7 @@ public class ConfigManager {
InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8)) { InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8)) {
config.load(isr); config.load(isr);
} catch (IOException e) { } catch (IOException e) {
Constants.LOGGER.error("Error loading configuration file: ", e); LOGGER.error("Error loading configuration file: ", e);
} }
} }
public File getConfigFile() { public File getConfigFile() {
@@ -118,6 +124,11 @@ public class ConfigManager {
return Boolean.parseBoolean(System.getenv("RUNNING_IN_DOCKER")); return Boolean.parseBoolean(System.getenv("RUNNING_IN_DOCKER"));
} }
public String getApiPrefix(String domain) {
return getStringProperty("api." + domain + ".prefix").replace("${app.version}",
String.valueOf(getStringProperty("app.version")));
}
public String getStringProperty(String key) { public String getStringProperty(String key) {
return config.getProperty(key); return config.getProperty(key);
} }
@@ -138,9 +149,9 @@ public class ConfigManager {
private void saveConfig() { private void saveConfig() {
try (FileOutputStream fos = new FileOutputStream(configFile)) { try (FileOutputStream fos = new FileOutputStream(configFile)) {
config.store(fos, "Configuration for: " + Constants.APP_NAME); config.store(fos, "Configuration for: " + this.appName);
} catch (IOException e) { } catch (IOException e) {
Constants.LOGGER.error("Error saving configuration file: ", e); LOGGER.error("Error saving configuration file: ", e);
} }
} }
} }

View File

@@ -1,4 +1,4 @@
package net.miarma.api.backlib; package net.miarma.api.backlib.config;
/** /**
* Enum que representa los diferentes tipos de sistemas operativos soportados * Enum que representa los diferentes tipos de sistemas operativos soportados

View File

@@ -1,83 +0,0 @@
package net.miarma.api.backlib.core.entities;
import io.vertx.sqlclient.Row;
import net.miarma.api.backlib.Constants.CoreFileContext;
import net.miarma.api.backlib.annotations.Table;
import net.miarma.api.backlib.db.AbstractEntity;
import java.time.LocalDateTime;
@Table("files")
public class FileEntity extends AbstractEntity {
private Integer file_id;
private String file_name;
private String file_path;
private String mime_type;
private Integer uploaded_by;
private CoreFileContext context;
private LocalDateTime uploaded_at;
public FileEntity() {
super();
}
public FileEntity(Row row) {
super(row);
}
public Integer getFile_id() {
return file_id;
}
public void setFile_id(Integer file_id) {
this.file_id = file_id;
}
public String getFile_name() {
return file_name;
}
public void setFile_name(String file_name) {
this.file_name = file_name;
}
public String getFile_path() {
return file_path;
}
public void setFile_path(String file_path) {
this.file_path = file_path;
}
public String getMime_type() {
return mime_type;
}
public void setMime_type(String mime_type) {
this.mime_type = mime_type;
}
public Integer getUploaded_by() {
return uploaded_by;
}
public void setUploaded_by(Integer uploaded_by) {
this.uploaded_by = uploaded_by;
}
public CoreFileContext getContext() {
return context;
}
public void setContext(CoreFileContext context) {
this.context = context;
}
public LocalDateTime getUploaded_at() {
return uploaded_at;
}
public void setUploaded_at(LocalDateTime uploaded_at) {
this.uploaded_at = uploaded_at;
}
}

View File

@@ -1,63 +0,0 @@
package net.miarma.api.backlib.core.entities;
import java.time.LocalDateTime;
import io.vertx.sqlclient.Row;
import net.miarma.api.backlib.Constants.CoreUserGlobalStatus;
import net.miarma.api.backlib.Constants.CoreUserRole;
import net.miarma.api.backlib.annotations.APIDontReturn;
import net.miarma.api.backlib.annotations.Table;
import net.miarma.api.backlib.db.AbstractEntity;
import net.miarma.api.backlib.interfaces.IUser;
@Table("users")
public class UserEntity extends AbstractEntity implements IUser {
private Integer user_id;
private String user_name;
private String email;
private String display_name;
@APIDontReturn
private String password;
private String avatar;
private CoreUserGlobalStatus global_status;
private CoreUserRole role;
private LocalDateTime created_at;
private LocalDateTime updated_at;
public UserEntity() { }
public UserEntity(Row row) { super(row); }
public Integer getUser_id() { return user_id; }
public void setUser_id(Integer user_id) { this.user_id = user_id; }
public String getUser_name() { return user_name; }
public void setUser_name(String user_name) { this.user_name = user_name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getDisplay_name() { return display_name; }
public void setDisplay_name(String display_name) { this.display_name = display_name; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getAvatar() { return avatar; }
public void setAvatar(String avatar) { this.avatar = avatar; }
public CoreUserGlobalStatus getGlobal_status() { return global_status; }
public void setGlobal_status(CoreUserGlobalStatus global_status) { this.global_status = global_status; }
public CoreUserRole getGlobal_role() { return role; }
public void setGlobal_role(CoreUserRole role) { this.role = role; }
public LocalDateTime getCreated_at() { return created_at; }
public void setCreated_at(LocalDateTime created_at) { this.created_at = created_at; }
public LocalDateTime getUpdated_at() { return updated_at; }
public void setUpdated_at(LocalDateTime updated_at) { this.updated_at = updated_at; }
public static UserEntity from(IUser user) {
UserEntity entity = new UserEntity();
entity.setUser_id(user.getUser_id());
entity.setUser_name(user.getUser_name());
entity.setDisplay_name(user.getDisplay_name());
entity.setEmail(user.getEmail());
entity.setPassword(user.getPassword());
entity.setAvatar(user.getAvatar());
entity.setGlobal_status(user.getGlobal_status());
entity.setGlobal_role(user.getGlobal_role());
return entity;
}
}

View File

@@ -1,70 +0,0 @@
package net.miarma.api.backlib.core.handlers;
import io.vertx.ext.web.RoutingContext;
import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.http.ApiStatus;
import net.miarma.api.backlib.http.QueryParams;
import net.miarma.api.backlib.util.JsonUtil;
import net.miarma.api.backlib.core.entities.UserEntity;
import net.miarma.api.backlib.core.services.UserService;
@SuppressWarnings("unused")
public class UserDataHandler {
private final UserService userService;
public UserDataHandler(Pool pool) {
this.userService = new UserService(pool);
}
public void getAll(RoutingContext ctx) {
QueryParams params = QueryParams.from(ctx);
userService.getAll(params)
.onSuccess(users -> JsonUtil.sendJson(ctx, ApiStatus.OK, users)).onFailure(err -> {
ApiStatus status = ApiStatus.fromException(err);
JsonUtil.sendJson(ctx, status, null, err.getMessage());
});
}
public void getById(RoutingContext ctx) {
Integer userId = Integer.parseInt(ctx.pathParam("user_id"));
userService.getById(userId)
.onSuccess(user -> JsonUtil.sendJson(ctx, ApiStatus.OK, user)).onFailure(err -> {
ApiStatus status = ApiStatus.fromException(err);
JsonUtil.sendJson(ctx, status, null, err.getMessage());
});
}
public void create(RoutingContext ctx) {
UserEntity user = Constants.GSON.fromJson(ctx.body().asString(), UserEntity.class);
userService.register(user)
.onSuccess(result -> JsonUtil.sendJson(ctx, ApiStatus.CREATED, result)).onFailure(err -> {
ApiStatus status = ApiStatus.fromException(err);
JsonUtil.sendJson(ctx, status, null, err.getMessage());
});
}
public void update(RoutingContext ctx) {
UserEntity user = Constants.GSON.fromJson(ctx.body().asString(), UserEntity.class);
userService.update(user)
.onSuccess(result -> JsonUtil.sendJson(ctx, ApiStatus.NO_CONTENT, result)).onFailure(err -> {
ApiStatus status = ApiStatus.fromException(err);
JsonUtil.sendJson(ctx, status, null, err.getMessage());
});
}
public void delete(RoutingContext ctx) {
Integer userId = Integer.parseInt(ctx.pathParam("user_id"));
userService.delete(userId)
.onSuccess(result -> JsonUtil.sendJson(ctx, ApiStatus.NO_CONTENT, result)).onFailure(err -> {
ApiStatus status = ApiStatus.fromException(err);
JsonUtil.sendJson(ctx, status, null, err.getMessage());
});
}
}

View File

@@ -1,286 +0,0 @@
package net.miarma.api.backlib.core.handlers;
import com.auth0.jwt.interfaces.DecodedJWT;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.http.ApiStatus;
import net.miarma.api.backlib.security.JWTManager;
import net.miarma.api.backlib.util.EventBusUtil;
import net.miarma.api.backlib.util.JsonUtil;
import net.miarma.api.backlib.core.entities.UserEntity;
public class UserLogicHandler {
private final Vertx vertx;
public UserLogicHandler(Vertx vertx) {
this.vertx = vertx;
}
public void login(RoutingContext ctx) {
JsonObject body = ctx.body().asJsonObject();
JsonObject request = new JsonObject()
.put("action", "login")
.put("email", body.getString("email", null))
.put("userName", body.getString("userName", null))
.put("password", body.getString("password"))
.put("keepLoggedIn", body.getBoolean("keepLoggedIn", false));
vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonObject result = (JsonObject) ar.result().body();
result.put("tokenTime", System.currentTimeMillis());
JsonUtil.sendJson(ctx, ApiStatus.OK, result);
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void loginValidate(RoutingContext ctx) {
JsonObject body = ctx.body().asJsonObject();
JsonObject request = new JsonObject()
.put("action", "loginValidate")
.put("userId", body.getInteger("userId"))
.put("password", body.getString("password"));
vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void register(RoutingContext ctx) {
JsonObject body = ctx.body().asJsonObject();
JsonObject request = new JsonObject()
.put("action", "register")
.put("userName", body.getString("userName"))
.put("email", body.getString("email"))
.put("displayName", body.getString("displayName"))
.put("password", body.getString("password"));
vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.CREATED, null);
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void changePassword(RoutingContext ctx) {
JsonObject body = ctx.body().asJsonObject();
JsonObject request = new JsonObject()
.put("action", "changePassword")
.put("userId", body.getInteger("userId"))
.put("newPassword", body.getString("newPassword"));
vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, true, "Updated");
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void validateToken(RoutingContext ctx) {
String authHeader = ctx.request().getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
JsonObject request = new JsonObject()
.put("action", "validateToken")
.put("token", token);
vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
if (ar.succeeded() && Boolean.TRUE.equals(ar.result().body())) {
JsonUtil.sendJson(ctx, ApiStatus.OK, true, "Valid token");
} else {
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, false, "Invalid token");
}
});
} else {
JsonUtil.sendJson(ctx, ApiStatus.BAD_REQUEST, null, "Missing or invalid Authorization header");
}
}
public void refreshToken(RoutingContext ctx) {
String tokenHeader = ctx.request().getHeader("Authorization");
if (tokenHeader == null || !tokenHeader.startsWith("Bearer ")) {
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, null, "Missing or invalid Authorization header");
return;
}
String token = tokenHeader.substring("Bearer ".length());
JWTManager jwt = JWTManager.getInstance();
try {
DecodedJWT decoded = jwt.decodeWithoutVerification(token);
int userId = decoded.getClaim("userId").asInt();
if (userId == -1) {
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, null, "Invalid token");
return;
}
vertx.eventBus().request(Constants.AUTH_EVENT_BUS, new JsonObject()
.put("action", "getUserById")
.put("userId", userId), ar -> {
if (ar.succeeded()) {
JsonObject userJson = (JsonObject) ar.result().body();
UserEntity user = Constants.GSON.fromJson(userJson.encode(), UserEntity.class);
String newToken = jwt.generateToken(user.getUser_name(), user.getUser_id(), user.getGlobal_role(), false);
JsonUtil.sendJson(ctx, ApiStatus.OK, new JsonObject().put("token", newToken));
} else {
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, null, "User not found");
}
});
} catch (Exception e) {
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, null, "Invalid token");
}
}
public void getInfo(RoutingContext ctx) {
String authHeader = ctx.request().getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, null, "Unauthorized");
return;
}
String token = authHeader.substring(7);
int userId = net.miarma.api.backlib.security.JWTManager.getInstance().getUserId(token);
if (userId <= 0) {
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, null, "Invalid token");
return;
}
JsonObject request = new JsonObject()
.put("action", "getInfo")
.put("userId", userId);
vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void exists(RoutingContext ctx) {
int userId = Integer.parseInt(ctx.pathParam("user_id"));
JsonObject request = new JsonObject()
.put("action", "userExists")
.put("userId", userId);
vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void getStatus(RoutingContext ctx) {
int userId = Integer.parseInt(ctx.pathParam("user_id"));
JsonObject request = new JsonObject()
.put("action", "getStatus")
.put("userId", userId);
vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void getRole(RoutingContext ctx) {
int userId = Integer.parseInt(ctx.pathParam("user_id"));
JsonObject request = new JsonObject()
.put("action", "getRole")
.put("userId", userId);
vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void getAvatar(RoutingContext ctx) {
int userId = Integer.parseInt(ctx.pathParam("user_id"));
JsonObject request = new JsonObject()
.put("action", "getAvatar")
.put("userId", userId);
vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void updateStatus(RoutingContext ctx) {
JsonObject body = ctx.body().asJsonObject();
JsonObject request = new JsonObject()
.put("action", "updateStatus")
.put("userId", body.getInteger("userId"))
.put("status", body.getInteger("status"));
vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.NO_CONTENT, null);
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void updateRole(RoutingContext ctx) {
JsonObject body = ctx.body().asJsonObject();
JsonObject request = new JsonObject()
.put("action", "updateRole")
.put("userId", body.getInteger("userId"))
.put("role", body.getInteger("role"));
vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.NO_CONTENT, null);
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
}

View File

@@ -1,209 +0,0 @@
package net.miarma.api.backlib.core.services;
import java.util.List;
import io.vertx.core.Future;
import io.vertx.core.json.JsonObject;
import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.Constants.CoreUserGlobalStatus;
import net.miarma.api.backlib.Constants.CoreUserRole;
import net.miarma.api.backlib.exceptions.AlreadyExistsException;
import net.miarma.api.backlib.exceptions.BadRequestException;
import net.miarma.api.backlib.exceptions.ForbiddenException;
import net.miarma.api.backlib.exceptions.NotFoundException;
import net.miarma.api.backlib.exceptions.UnauthorizedException;
import net.miarma.api.backlib.exceptions.ValidationException;
import net.miarma.api.backlib.http.QueryParams;
import net.miarma.api.backlib.security.JWTManager;
import net.miarma.api.backlib.security.PasswordHasher;
import net.miarma.api.backlib.core.dao.UserDAO;
import net.miarma.api.backlib.core.entities.UserEntity;
import net.miarma.api.backlib.core.validators.UserValidator;
public class UserService {
private final UserDAO userDAO;
private final UserValidator userValidator;
public UserService(Pool pool) {
this.userDAO = new UserDAO(pool);
this.userValidator = new UserValidator();
}
/* AUTHENTICATION */
public Future<JsonObject> login(String emailOrUsername, String plainPassword, boolean keepLoggedIn) {
return getByEmail(emailOrUsername).compose(user -> {
if (user == null) {
return getByUserName(emailOrUsername).compose(user2 -> {
if (user2 == null) {
return Future.succeededFuture(null);
}
return Future.succeededFuture(user2);
});
}
return Future.succeededFuture(user);
}).compose(user -> {
if (user == null) {
return Future.failedFuture(new BadRequestException("Invalid credentials"));
}
if (user.getGlobal_status() != Constants.CoreUserGlobalStatus.ACTIVE) {
return Future.failedFuture(new ForbiddenException("User is not active"));
}
if (!PasswordHasher.verify(plainPassword, user.getPassword())) {
return Future.failedFuture(new BadRequestException("Invalid credentials"));
}
JWTManager jwtManager = JWTManager.getInstance();
String token = jwtManager.generateToken(user.getUser_name(), user.getUser_id(), user.getGlobal_role(), keepLoggedIn);
JsonObject response = new JsonObject()
.put("token", token)
.put("loggedUser", new JsonObject(user.encode()));
return Future.succeededFuture(response);
});
}
public Future<JsonObject> loginValidate(Integer userId, String password) {
return getById(userId).compose(user -> {
if (user == null) {
return Future.failedFuture(new NotFoundException("User not found"));
}
if (!PasswordHasher.verify(password, user.getPassword())) {
return Future.failedFuture(new BadRequestException("Invalid credentials"));
}
JsonObject response = new JsonObject()
.put("valid", true);
return Future.succeededFuture(response);
});
}
public Future<UserEntity> register(UserEntity user) {
return getByEmail(user.getEmail()).compose(existing -> {
if (existing != null) {
return Future.failedFuture(new AlreadyExistsException("Email already exists"));
}
user.setPassword(PasswordHasher.hash(user.getPassword()));
user.setGlobal_role(CoreUserRole.USER);
user.setGlobal_status(CoreUserGlobalStatus.ACTIVE);
return userValidator.validate(user).compose(validation -> {
if (!validation.isValid()) {
return Future.failedFuture(new ValidationException(Constants.GSON.toJson(validation.getErrors())));
}
return userDAO.insert(user);
});
});
}
public Future<UserEntity> changePassword(int userId, String newPassword) {
return getById(userId).compose(user -> {
if (user == null) {
return Future.failedFuture(new NotFoundException("User not found"));
}
user.setPassword(PasswordHasher.hash(newPassword));
return userDAO.update(user);
});
}
public Future<Boolean> validateToken(String token) {
JWTManager jwtManager = JWTManager.getInstance();
return jwtManager.isValid(token) ?
Future.succeededFuture(true) :
Future.failedFuture(new UnauthorizedException("Invalid token"));
}
/* USERS OPERATIONS */
public Future<List<UserEntity>> getAll(QueryParams params) {
return userDAO.getAll(params);
}
public Future<UserEntity> getById(Integer id) {
return userDAO.getById(id).compose(user -> {
if (user == null) {
return Future.failedFuture(new NotFoundException("User not found in the database"));
}
return Future.succeededFuture(user);
});
}
public Future<UserEntity> getByEmail(String email) {
return userDAO.getByEmail(email);
}
public Future<UserEntity> getByUserName(String userName) {
return userDAO.getByUserName(userName);
}
public Future<UserEntity> updateRole(Integer userId, CoreUserRole role) {
return getById(userId).compose(user -> {
if (user == null) {
return Future.failedFuture(new NotFoundException("User not found in the database"));
}
user.setGlobal_role(role);
return userDAO.update(user);
});
}
public Future<UserEntity> updateStatus(Integer userId, CoreUserGlobalStatus status) {
return getById(userId).compose(user -> {
if (user == null) {
return Future.failedFuture(new NotFoundException("User not found in the database"));
}
user.setGlobal_status(status);
return userDAO.update(user);
});
}
/* CRUD OPERATIONS */
public Future<UserEntity> create(UserEntity user) {
return userValidator.validate(user).compose(validation -> {
if (!validation.isValid()) {
return Future.failedFuture(new ValidationException(Constants.GSON.toJson(validation.getErrors())));
}
return userDAO.insert(user);
});
}
public Future<UserEntity> update(UserEntity user) {
return userValidator.validate(user).compose(validation -> {
if (!validation.isValid()) {
return Future.failedFuture(new ValidationException(Constants.GSON.toJson(validation.getErrors())));
}
if (user.getPassword() == null || user.getPassword().isEmpty()) {
user.setPassword(null);
}
return userDAO.update(user);
});
}
public Future<UserEntity> upsert(UserEntity user) {
return userValidator.validate(user).compose(validation -> {
if (!validation.isValid()) {
return Future.failedFuture(new ValidationException(Constants.GSON.toJson(validation.getErrors())));
}
if (user.getPassword() != null && !user.getPassword().isEmpty()) {
user.setPassword(PasswordHasher.hash(user.getPassword()));
}
return userDAO.upsert(user, "user_id", "email", "user_name");
});
}
public Future<Boolean> delete(Integer id) {
return getById(id).compose(user -> {
if (user == null) {
return Future.failedFuture(new NotFoundException("User not found in the database"));
}
return userDAO.delete(id);
});
}
}

View File

@@ -1,84 +0,0 @@
package net.miarma.api.backlib.core.validators;
import io.vertx.core.Future;
import net.miarma.api.backlib.validation.ValidationResult;
import net.miarma.api.backlib.core.entities.FileEntity;
public class FileValidator {
public Future<ValidationResult> validate(FileEntity file, int size) {
ValidationResult result = new ValidationResult();
if (file == null) {
return Future.succeededFuture(result.addError("file", "El archivo no puede ser nulo"));
}
if (file.getFile_name() == null || file.getFile_name().isBlank()) {
result.addError("file_name", "El nombre del archivo es obligatorio");
}
if (file.getMime_type() == null || file.getMime_type().isBlank()) {
result.addError("mime_type", "El tipo MIME es obligatorio");
}
if (file.getContext() == null) {
result.addError("context", "El contexto del archivo es obligatorio");
}
if (file.getUploaded_by() == null || file.getUploaded_by() <= 0) {
result.addError("uploaded_by", "El ID del usuario que subió el archivo es obligatorio y debe ser válido");
}
if (size <= 0) {
result.addError("size", "El archivo debe pesar más de 0 bytes");
}
if (file.getFile_name() != null && file.getFile_name().length() > 255) {
result.addError("file_name", "El nombre del archivo es demasiado largo");
}
if (size > 10485760) { // 10 MB limit
result.addError("size", "El archivo no puede pesar más de 10 MB");
}
return Future.succeededFuture(result);
}
public Future<ValidationResult> validate(FileEntity file) {
ValidationResult result = new ValidationResult();
if (file == null) {
return Future.succeededFuture(result.addError("file", "File cannot be null"));
}
if (file.getFile_name() == null || file.getFile_name().isBlank()) {
result.addError("file_name", "File name is required");
}
if (file.getFile_path() == null || file.getFile_path().isBlank()) {
result.addError("file_path", "File path is required");
}
if (file.getMime_type() == null || file.getMime_type().isBlank()) {
result.addError("mime_type", "MIME type is required");
}
if (file.getContext() == null) {
result.addError("context", "File context is required");
}
if (file.getUploaded_by() == null || file.getUploaded_by() <= 0) {
result.addError("uploaded_by", "Uploader user ID is required and must be valid");
}
if (file.getFile_name() != null && file.getFile_name().length() > 255) {
result.addError("file_name", "File name is too long");
}
if (file.getFile_path() != null && file.getFile_path().length() > 255) {
result.addError("file_path", "File path is too long");
}
return Future.succeededFuture(result);
}
}

View File

@@ -1,44 +0,0 @@
package net.miarma.api.backlib.core.validators;
import io.vertx.core.Future;
import net.miarma.api.backlib.validation.ValidationResult;
import net.miarma.api.backlib.core.entities.UserEntity;
public class UserValidator {
public Future<ValidationResult> validate(UserEntity user) {
ValidationResult result = new ValidationResult();
if (user == null) {
return Future.succeededFuture(result.addError("user", "El usuario no puede ser nulo"));
}
if (user.getUser_name() == null || user.getUser_name().isBlank()) {
result.addError("user_name", "El nombre de usuario es obligatorio");
}
if (user.getDisplay_name() == null || user.getDisplay_name().isBlank()) {
result.addError("display_name", "El nombre para mostrar es obligatorio");
}
if (user.getEmail() != null && !user.getEmail().isBlank()) {
if (!user.getEmail().matches("^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$")) {
result.addError("email", "El correo electrónico no es válido");
}
}
if (user.getPassword() == null || user.getPassword().isBlank()) {
result.addError("password", "La contraseña es obligatoria");
}
if (user.getGlobal_status() == null) {
result.addError("global_status", "El estado global del usuario es obligatorio");
}
if (user.getGlobal_role() == null) {
result.addError("role", "El rol del usuario es obligatorio");
}
return Future.succeededFuture(result);
}
}

View File

@@ -1,64 +1,45 @@
package net.miarma.api.backlib.db; package net.miarma.api.backlib.db;
import java.lang.reflect.Field;
import java.util.UUID;
import com.google.gson.annotations.SerializedName;
import org.slf4j.Logger;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject; import io.vertx.core.json.JsonObject;
import io.vertx.sqlclient.Row; import io.vertx.sqlclient.Row;
import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.ValuableEnum;
import net.miarma.api.backlib.annotations.APIDontReturn; import net.miarma.api.backlib.annotations.APIDontReturn;
import net.miarma.api.backlib.interfaces.IValuableEnum;
import net.miarma.api.backlib.log.LoggerProvider;
import net.miarma.api.backlib.util.BufferUtil;
import java.lang.reflect.Field;
/**
* Clase base para todas las entidades persistentes del sistema.
* <p>
* Proporciona utilidades para:
* <ul>
* <li>Construir una entidad a partir de una fila de base de datos ({@link Row})</li>
* <li>Serializar una entidad a {@link JsonObject}</li>
* <li>Generar una representación en texto</li>
* </ul>
*
* Los campos se mapean por reflexión, lo que permite extender fácilmente las entidades
* sin necesidad de escribir lógica de parsing repetitiva.
*
* @author José Manuel Amador Gallardo
*/
public abstract class AbstractEntity { public abstract class AbstractEntity {
private final Logger LOGGER = LoggerProvider.getLogger();
/** public AbstractEntity() {}
* Constructor por defecto. Requerido para instanciación sin datos.
*/
public AbstractEntity() {}
/**
* Constructor que inicializa los campos de la entidad a partir de una fila de base de datos.
*
* @param row Fila SQL proporcionada por Vert.x.
*/
public AbstractEntity(Row row) { public AbstractEntity(Row row) {
populateFromRow(row); populateFromRow(row);
} }
/** private String getColumnName(Field field) {
* Rellena los campos del objeto usando reflexión a partir de una {@link Row} de Vert.x. if (field.isAnnotationPresent(SerializedName.class)) {
* Se soportan tipos básicos (String, int, boolean, etc.), enums con método estático {@code fromInt(int)}, return field.getAnnotation(SerializedName.class).value();
* y {@link java.math.BigDecimal} (a través del tipo {@code Numeric} de Vert.x). }
* <p> return field.getName();
* Si un tipo no está soportado, se registra un error en el log y se ignora ese campo. }
*
* @param row Fila de datos de la que extraer los valores.
*/
private void populateFromRow(Row row) { private void populateFromRow(Row row) {
Field[] fields = this.getClass().getDeclaredFields(); Field[] fields = this.getClass().getDeclaredFields();
for (Field field : fields) { for (Field field : fields) {
try { try {
field.setAccessible(true); field.setAccessible(true);
Class<?> type = field.getType(); Class<?> type = field.getType();
String name = field.getName(); String columnName = getColumnName(field);
Object value; Object value;
if (type.isEnum()) { if (type.isEnum()) {
Integer intValue = row.getInteger(name); Integer intValue = row.getInteger(columnName);
if (intValue != null) { if (intValue != null) {
try { try {
var method = type.getMethod("fromInt", int.class); var method = type.getMethod("fromInt", int.class);
@@ -70,43 +51,44 @@ public abstract class AbstractEntity {
value = null; value = null;
} }
} else { } else {
value = switch (type.getSimpleName()) { value = switch (type.getSimpleName()) {
case "Integer", "int" -> row.getInteger(name); case "Integer", "int" -> row.getInteger(columnName);
case "String" -> row.getString(name); case "String" -> row.getString(columnName);
case "double", "Double" -> row.getDouble(name); case "double", "Double" -> row.getDouble(columnName);
case "long", "Long" -> row.getLong(name); case "long", "Long" -> row.getLong(columnName);
case "boolean", "Boolean" -> row.getBoolean(name); case "boolean", "Boolean" -> row.getBoolean(columnName);
case "LocalDateTime" -> row.getLocalDateTime(name); case "LocalDateTime" -> row.getLocalDateTime(columnName);
case "BigDecimal" -> { case "UUID" -> {
try { Object raw = row.getValue(columnName);
var numeric = row.get(io.vertx.sqlclient.data.Numeric.class, row.getColumnIndex(name)); if (raw instanceof UUID) yield raw;
yield numeric != null ? numeric.bigDecimalValue() : null; if (raw instanceof String s) yield UUID.fromString(s);
} catch (Exception e) { if (raw instanceof Buffer b) {
yield null; yield BufferUtil.uuidFromBuffer(b);
} }
} yield null;
default -> { }
Constants.LOGGER.error("Type not supported yet: {} for field {}", type.getName(), name); case "BigDecimal" -> {
yield null; try {
} var numeric = row.get(io.vertx.sqlclient.data.Numeric.class, row.getColumnIndex(columnName));
}; yield numeric != null ? numeric.bigDecimalValue() : null;
} catch (Exception e) { yield null; }
}
default -> {
LOGGER.error("Type not supported yet: {} for field {}", type.getName(), field.getName());
yield null;
}
};
} }
field.set(this, value); if (value != null) {
field.set(this, value);
}
} catch (Exception e) { } catch (Exception e) {
Constants.LOGGER.error("Error populating field {}: {}", field.getName(), e.getMessage()); LOGGER.error("Error populating field {}: {}", field.getName(), e.getMessage());
} }
} }
} }
/**
* Codifica esta entidad como un objeto JSON, omitiendo los campos anotados con {@link APIDontReturn}.
*
* <p>Si un campo implementa {@link ValuableEnum}, se usará su valor en lugar del nombre del enum.</p>
*
* @return Representación JSON de esta entidad.
*/
public String encode() { public String encode() {
JsonObject json = new JsonObject(); JsonObject json = new JsonObject();
Class<?> clazz = this.getClass(); Class<?> clazz = this.getClass();
@@ -114,47 +96,38 @@ public abstract class AbstractEntity {
while (clazz != null) { while (clazz != null) {
for (Field field : clazz.getDeclaredFields()) { for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(APIDontReturn.class)) continue; if (field.isAnnotationPresent(APIDontReturn.class)) continue;
field.setAccessible(true); field.setAccessible(true);
try { try {
Object value = field.get(this); Object value = field.get(this);
if (value instanceof IValuableEnum ve) {
if (value instanceof ValuableEnum ve) {
json.put(field.getName(), ve.getValue()); json.put(field.getName(), ve.getValue());
} else if (value instanceof UUID u) {
json.put(field.getName(), u.toString());
} else { } else {
json.put(field.getName(), value); json.put(field.getName(), value);
} }
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
Constants.LOGGER.error("Error accessing field {}: {}", field.getName(), e.getMessage()); LOGGER.error("Error accessing field {}: {}", field.getName(), e.getMessage());
} }
} }
clazz = clazz.getSuperclass(); clazz = clazz.getSuperclass();
} }
return json.encode(); return json.encode();
} }
/**
* Devuelve una representación en texto de la entidad, mostrando todos los campos y sus valores.
*
* <p>Útil para logs y debugging.</p>
*
* @return Cadena de texto con el nombre de la clase y todos los campos.
*/
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(this.getClass().getSimpleName()).append(" [ "); sb.append(this.getClass().getSimpleName()).append(" [ ");
Field[] fields = this.getClass().getDeclaredFields(); Field[] fields = this.getClass().getDeclaredFields();
for (Field field : fields) { for (Field field : fields) {
field.setAccessible(true); field.setAccessible(true);
try { try {
sb.append(field.getName()).append("= ").append(field.get(this)).append(", "); sb.append(field.getName()).append("=").append(field.get(this)).append(", ");
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
Constants.LOGGER.error("Error stringing field {}: {}", field.getName(), e.getMessage()); LOGGER.error("Error stringing field {}: {}", field.getName(), e.getMessage());
} }
} }
sb.append("]"); sb.append("]");
return sb.toString(); return sb.toString();
} }
} }

View File

@@ -1,16 +1,18 @@
package net.miarma.api.backlib.db; package net.miarma.api.backlib.db;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import io.vertx.core.Future; import io.vertx.core.Future;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.Row; import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.RowSet; import io.vertx.sqlclient.RowSet;
import net.miarma.api.backlib.Constants; import net.miarma.api.backlib.log.LoggerProvider;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
/** /**
* Gestor centralizado de acceso a la base de datos utilizando Vert.x SQL Client. * Gestor centralizado de acceso a la base de datos utilizando Vert.x SQL Client.
@@ -23,6 +25,7 @@ import java.util.List;
*/ */
public class DatabaseManager { public class DatabaseManager {
private final Logger LOGGER = LoggerProvider.getLogger();
private static DatabaseManager instance; private static DatabaseManager instance;
private final Pool pool; private final Pool pool;
@@ -86,7 +89,7 @@ public class DatabaseManager {
results.add(constructor.newInstance(row)); results.add(constructor.newInstance(row));
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException } catch (NoSuchMethodException | InstantiationException | IllegalAccessException
| InvocationTargetException e) { | InvocationTargetException e) {
Constants.LOGGER.error("Error instantiating class: {}", e.getMessage()); LOGGER.error("Error instantiating class: {}", e.getMessage());
} }
} }
return results; return results;
@@ -116,7 +119,7 @@ public class DatabaseManager {
Constructor<T> constructor = clazz.getConstructor(Row.class); Constructor<T> constructor = clazz.getConstructor(Row.class);
return constructor.newInstance(row); return constructor.newInstance(row);
} catch (Exception e) { } catch (Exception e) {
Constants.LOGGER.error("Error instantiating class: {}", e.getMessage()); LOGGER.error("Error instantiating class: {}", e.getMessage());
} }
} }
return null; // Si no hay filas return null; // Si no hay filas

View File

@@ -4,7 +4,7 @@ import io.vertx.core.Vertx;
import io.vertx.mysqlclient.MySQLConnectOptions; import io.vertx.mysqlclient.MySQLConnectOptions;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.PoolOptions; import io.vertx.sqlclient.PoolOptions;
import net.miarma.api.backlib.ConfigManager; import net.miarma.api.backlib.config.ConfigManager;
/** /**
* Factoría de {@link Pool} para conexiones MySQL usando Vert.x. * Factoría de {@link Pool} para conexiones MySQL usando Vert.x.

View File

@@ -1,64 +1,42 @@
package net.miarma.api.backlib.db; package net.miarma.api.backlib.db;
import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.annotations.Table;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** import org.slf4j.Logger;
* Clase utilitaria para construir queries SQL dinámicamente mediante reflexión, import com.google.gson.annotations.SerializedName;
* usando entidades anotadas con {@link Table}. import net.miarma.api.backlib.annotations.Table;
* <p> import net.miarma.api.backlib.log.LoggerProvider;
* Soporta operaciones SELECT, INSERT, UPDATE (con y sin valores nulos), y UPSERT.
* También permite aplicar filtros desde un mapa o directamente desde un objeto.
* <p>
* ¡Ojo! No ejecuta la query, solo la construye.
*
* @author José Manuel Amador Gallardo
*/
public class QueryBuilder { public class QueryBuilder {
private static final Logger LOGGER = LoggerProvider.getLogger();
private final StringBuilder query; private final StringBuilder query;
private String sort; private String orderByClause;
private String order; private String limitClause;
private String limit; private String offsetClause;
private Class<?> entityClass; private Class<?> entityClass;
public QueryBuilder() { public QueryBuilder() {
this.query = new StringBuilder(); this.query = new StringBuilder();
} }
/**
* Obtiene el nombre de la tabla desde la anotación @Table de la clase dada.
*/
private static <T> String getTableName(Class<T> clazz) { private static <T> String getTableName(Class<T> clazz) {
if (clazz == null) { if (clazz == null) throw new IllegalArgumentException("Class cannot be null");
throw new IllegalArgumentException("Class cannot be null"); if (!clazz.isAnnotationPresent(Table.class)) throw new IllegalArgumentException("Class does not have @Table annotation");
} return clazz.getAnnotation(Table.class).value();
if (clazz.isAnnotationPresent(Table.class)) {
Table annotation = clazz.getAnnotation(Table.class);
return annotation.value();
}
throw new IllegalArgumentException("Class does not have @Table annotation");
} }
/** private static String getColumnName(Field field) {
* Devuelve la consulta SQL construida hasta el momento. SerializedName annotation = field.getAnnotation(SerializedName.class);
*/ return annotation != null ? annotation.value() : field.getName();
public String getQuery() {
return query.toString();
} }
/**
* Extrae el valor de un campo, manejando enums y tipos especiales.
* Si es un Enum y tiene getValue(), lo usa; si no, devuelve el name().
* Si es un LocalDateTime, lo convierte a String en formato SQL.
*/
private static Object extractValue(Object fieldValue) { private static Object extractValue(Object fieldValue) {
if (fieldValue == null) return null;
if (fieldValue instanceof Enum<?>) { if (fieldValue instanceof Enum<?>) {
try { try {
var method = fieldValue.getClass().getMethod("getValue"); var method = fieldValue.getClass().getMethod("getValue");
@@ -67,199 +45,137 @@ public class QueryBuilder {
return ((Enum<?>) fieldValue).name(); return ((Enum<?>) fieldValue).name();
} }
} }
if (fieldValue instanceof LocalDateTime ldt) { if (fieldValue instanceof LocalDateTime ldt) {
return ldt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); return ldt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
} }
return fieldValue; return fieldValue;
} }
/** private String formatValue(Object value) {
* Escapa los caracteres especiales en una cadena para evitar inyecciones SQL. if (value == null) return "NULL";
* @param value the string value to escape if (value instanceof UUID uuid) {
* @return the escaped string return "UNHEX(REPLACE('" + uuid.toString() + "','-',''))";
*/ }
if (value instanceof String || value instanceof LocalDateTime) {
return "'" + escapeSql(value.toString()) + "'";
}
return value.toString();
}
private static String escapeSql(String value) { private static String escapeSql(String value) {
return value.replace("'", "''"); return value.replace("'", "''");
} }
/** /**
* Construye una consulta SELECT para la clase dada, con columnas opcionales. * @param clazz La clase de la entidad a seleccionar
* @param clazz the entity class to query * @param columns Columnas a seleccionar, si no se pasan selecciona "*"
* @param columns optional columns to select; if empty, selects all columns
* @return the current QueryBuilder instance
* @param <T> the type of the entity class
*/ */
public static <T> QueryBuilder select(Class<T> clazz, String... columns) { public static <T> QueryBuilder select(Class<T> clazz, String... columns) {
if (clazz == null) {
throw new IllegalArgumentException("Class cannot be null");
}
QueryBuilder qb = new QueryBuilder(); QueryBuilder qb = new QueryBuilder();
qb.entityClass = clazz; qb.entityClass = clazz;
String tableName = getTableName(clazz);
qb.query.append("SELECT "); qb.query.append("SELECT ");
if (columns.length == 0) qb.query.append("* ");
if (columns.length == 0) { else qb.query.append(String.join(", ", columns)).append(" ");
qb.query.append("* "); qb.query.append("FROM ").append(getTableName(clazz)).append(" ");
} else {
StringJoiner joiner = new StringJoiner(", ");
for (String column : columns) {
if (column != null) {
joiner.add(column);
}
}
qb.query.append(joiner).append(" ");
}
qb.query.append("FROM ").append(tableName).append(" ");
return qb; return qb;
} }
/** /**
* Añade una cláusula WHERE a la consulta actual, filtrando por los campos del mapa. * @param filters Mapa clave = valor para el WHERE. Detecta UUID y Strings tipo IN
* Los valores pueden ser números o cadenas, y se manejan adecuadamente.
*
* @param filters un mapa de filtros donde la clave es el nombre del campo y el valor es el valor a filtrar
* @return el QueryBuilder actual para encadenar más métodos
*/ */
public QueryBuilder where(Map<String, String> filters) { public QueryBuilder where(Map<String, Object> filters) {
if (filters == null || filters.isEmpty()) { if (filters == null || filters.isEmpty() || entityClass == null) return this;
return this;
Map<String, String> javaToSql = new HashMap<>();
Map<String, String> sqlToSql = new HashMap<>();
for (Field f : entityClass.getDeclaredFields()) {
String sqlCol = getColumnName(f);
javaToSql.put(f.getName(), sqlCol);
sqlToSql.put(sqlCol, sqlCol);
} }
Set<String> validFields = entityClass != null
? Arrays.stream(entityClass.getDeclaredFields()).map(Field::getName).collect(Collectors.toSet())
: Collections.emptySet();
List<String> conditions = new ArrayList<>(); List<String> conditions = new ArrayList<>();
for (Map.Entry<String, Object> entry : filters.entrySet()) {
for (Map.Entry<String, String> entry : filters.entrySet()) {
String key = entry.getKey(); String key = entry.getKey();
String value = entry.getValue(); String sqlCol = javaToSql.getOrDefault(key, sqlToSql.get(key));
if (sqlCol == null) {
if (!validFields.contains(key)) { LOGGER.warn("[QueryBuilder] Ignorando campo invalido en WHERE: {}", key);
Constants.LOGGER.warn("[QueryBuilder] Ignorando campo invalido en WHERE: {}", key);
continue; continue;
} }
Object val = entry.getValue();
if (val == null) continue;
if (val instanceof String s && s.startsWith("(") && s.endsWith(")")) conditions.add(sqlCol + " IN " + s);
else conditions.add(sqlCol + " = " + formatValue(val));
}
if (value.startsWith("(") && value.endsWith(")")) { if (!conditions.isEmpty()) {
conditions.add(key + " IN " + value); String prefix = query.toString().contains("WHERE") ? "AND " : "WHERE ";
} else if (value.matches("-?\\d+(\\.\\d+)?")) { query.append(prefix).append(String.join(" AND ", conditions)).append(" ");
conditions.add(key + " = " + value); }
} else { return this;
conditions.add(key + " = '" + value + "'"); }
/**
* @param object Objeto con campos para WHERE. Soporta UUID y LocalDateTime
*/
public <T> QueryBuilder where(T object) {
if (object == null) throw new IllegalArgumentException("Object cannot be null");
if (entityClass == null) return this;
Set<String> validColumns = Arrays.stream(entityClass.getDeclaredFields())
.map(QueryBuilder::getColumnName)
.collect(Collectors.toSet());
List<String> conditions = new ArrayList<>();
for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
Object value = field.get(object);
if (value != null) {
String col = getColumnName(field);
if (!validColumns.contains(col)) continue;
Object extracted = extractValue(value);
conditions.add(col + " = " + formatValue(extracted));
}
} catch (IllegalAccessException e) {
LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
} }
} }
if (!conditions.isEmpty()) { if (!conditions.isEmpty()) {
query.append("WHERE ").append(String.join(" AND ", conditions)).append(" "); String prefix = query.toString().contains("WHERE") ? "AND " : "WHERE ";
query.append(prefix).append(String.join(" AND ", conditions)).append(" ");
} }
return this; return this;
} }
/**
* Añade una cláusula WHERE a la consulta actual, filtrando por los campos del objeto.
* Los valores se extraen mediante reflexión y se manejan adecuadamente.
*
* @param object el objeto del cual se extraerán los campos para filtrar
* @return el QueryBuilder actual para encadenar más métodos
*/
public <T> QueryBuilder where(T object) {
if (object == null) {
throw new IllegalArgumentException("Object cannot be null");
}
Set<String> validFields = entityClass != null
? Arrays.stream(entityClass.getDeclaredFields()).map(Field::getName).collect(Collectors.toSet())
: Collections.emptySet();
this.query.append("WHERE ");
StringJoiner joiner = new StringJoiner(" AND ");
for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
Object fieldValue = field.get(object);
if (fieldValue != null) {
String key = field.getName();
if (!validFields.contains(key)) {
Constants.LOGGER.warn("[QueryBuilder] Ignorando campo invalido en WHERE: {}", key);
continue;
}
Object value = extractValue(fieldValue);
if (value instanceof String || value instanceof LocalDateTime) {
joiner.add(key + " = '" + value + "'");
} else {
joiner.add(key + " = " + value.toString());
}
}
} catch (IllegalArgumentException | IllegalAccessException e) {
Constants.LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
}
}
this.query.append(joiner).append(" ");
return this;
}
/**
* Construye una consulta INSERT para el objeto dado, insertando todos sus campos.
* Los valores se extraen mediante reflexión y se manejan adecuadamente.
*
* @param object el objeto a insertar
* @return el QueryBuilder actual para encadenar más métodos
* @param <T> el tipo del objeto a insertar
*/
public static <T> QueryBuilder insert(T object) { public static <T> QueryBuilder insert(T object) {
if (object == null) { if (object == null) throw new IllegalArgumentException("Object cannot be null");
throw new IllegalArgumentException("Object cannot be null");
}
QueryBuilder qb = new QueryBuilder(); QueryBuilder qb = new QueryBuilder();
String table = getTableName(object.getClass()); String table = getTableName(object.getClass());
qb.query.append("INSERT INTO ").append(table).append(" "); qb.query.append("INSERT INTO ").append(table).append(" ");
qb.query.append("(");
StringJoiner columns = new StringJoiner(", "); StringJoiner cols = new StringJoiner(", ");
StringJoiner values = new StringJoiner(", "); StringJoiner vals = new StringJoiner(", ");
for (Field field : object.getClass().getDeclaredFields()) { for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true); field.setAccessible(true);
try { try {
columns.add(field.getName()); cols.add(getColumnName(field));
Object fieldValue = field.get(object); Object value = extractValue(field.get(object));
if (fieldValue != null) { vals.add(qb.formatValue(value));
Object value = extractValue(fieldValue); } catch (IllegalAccessException e) {
if (value instanceof String || value instanceof LocalDateTime) { LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
values.add("'" + escapeSql((String) value) + "'");
} else {
values.add(value.toString());
}
} else {
values.add("NULL");
}
} catch (IllegalArgumentException | IllegalAccessException e) {
Constants.LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
} }
} }
qb.query.append(columns).append(") ");
qb.query.append("VALUES (").append(values).append(") RETURNING * "); qb.query.append("(").append(cols).append(") VALUES (").append(vals).append(") RETURNING * ");
return qb; return qb;
} }
/** private static <T> QueryBuilder buildUpdate(T object, boolean includeNulls) {
* Construye una consulta UPDATE para el objeto dado, actualizando todos sus campos. if (object == null) throw new IllegalArgumentException("Object cannot be null");
* Los valores se extraen mediante reflexión y se manejan adecuadamente.
* Requiere que el objeto tenga un campo ID (terminado en _id) para la cláusula WHERE.
*
* @param object el objeto a actualizar
* @return el QueryBuilder actual para encadenar más métodos
* @param <T> el tipo del objeto a actualizar
*/
public static <T> QueryBuilder update(T object) {
if (object == null) {
throw new IllegalArgumentException("Object cannot be null");
}
QueryBuilder qb = new QueryBuilder(); QueryBuilder qb = new QueryBuilder();
String table = getTableName(object.getClass()); String table = getTableName(object.getClass());
@@ -267,103 +183,36 @@ public class QueryBuilder {
StringJoiner setJoiner = new StringJoiner(", "); StringJoiner setJoiner = new StringJoiner(", ");
StringJoiner whereJoiner = new StringJoiner(" AND "); StringJoiner whereJoiner = new StringJoiner(" AND ");
Field idField = null; Field idField = null;
for (Field field : object.getClass().getDeclaredFields()) { for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true); field.setAccessible(true);
try { try {
Object fieldValue = field.get(object); String col = getColumnName(field);
if (fieldValue == null) continue; Object value = extractValue(field.get(object));
if (col.endsWith("_id")) {
String fieldName = field.getName();
Object value = extractValue(fieldValue);
if (fieldName.endsWith("_id")) {
idField = field; idField = field;
whereJoiner.add(fieldName + " = " + (value instanceof String if (value != null) whereJoiner.add(col + " = " + qb.formatValue(value));
|| value instanceof LocalDateTime ? "'" + value + "'" : value)); else throw new IllegalArgumentException("ID field cannot be null");
continue; continue;
} }
if (value != null) setJoiner.add(col + " = " + qb.formatValue(value));
setJoiner.add(fieldName + " = " + (value instanceof String else if (includeNulls) setJoiner.add(col + " = NULL");
|| value instanceof LocalDateTime ? "'" + value + "'" : value)); } catch (IllegalAccessException e) {
} catch (Exception e) { LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
Constants.LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
} }
} }
if (idField == null) { if (idField == null) throw new IllegalArgumentException("No ID field (ending with _id) found for WHERE clause");
throw new IllegalArgumentException("No ID field (ending with _id) found for WHERE clause");
}
qb.query.append(setJoiner).append(" WHERE ").append(whereJoiner); qb.query.append(setJoiner).append(" WHERE ").append(whereJoiner).append(" ");
return qb; return qb;
} }
/** public static <T> QueryBuilder update(T object) { return buildUpdate(object, false); }
* Construye una consulta UPDATE que establece los campos a NULL si son nulos.
* Requiere que el objeto tenga un campo ID (terminado en _id) para la cláusula WHERE.
*
* @param object el objeto a actualizar
* @return el QueryBuilder actual para encadenar más métodos
* @param <T> el tipo del objeto a actualizar
*/
public static <T> QueryBuilder updateWithNulls(T object) {
if (object == null) {
throw new IllegalArgumentException("Object cannot be null");
}
QueryBuilder qb = new QueryBuilder(); public static <T> QueryBuilder updateWithNulls(T object) { return buildUpdate(object, true); }
String table = getTableName(object.getClass());
qb.query.append("UPDATE ").append(table).append(" SET ");
StringJoiner setJoiner = new StringJoiner(", ");
StringJoiner whereJoiner = new StringJoiner(" AND ");
Field idField = null;
for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
String fieldName = field.getName();
Object fieldValue = field.get(object);
if (fieldName.endsWith("_id")) {
idField = field;
Object value = extractValue(fieldValue);
whereJoiner.add(fieldName + " = " + (value instanceof String || value instanceof LocalDateTime ? "'" + value + "'" : value));
continue;
}
if (fieldValue == null) {
setJoiner.add(fieldName + " = NULL");
} else {
Object value = extractValue(fieldValue);
setJoiner.add(fieldName + " = " + (value instanceof String || value instanceof LocalDateTime ? "'" + value + "'" : value));
}
} catch (Exception e) {
Constants.LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
}
}
if (idField == null) {
throw new IllegalArgumentException("No ID field (ending with _id) found for WHERE clause");
}
qb.query.append(setJoiner).append(" WHERE ").append(whereJoiner);
return qb;
}
/**
* Construye una consulta UPSERT (INSERT o UPDATE) para el objeto dado.
* Si hay claves de conflicto, se actualizan los campos excepto las claves duplicadas.
*
* @param object el objeto a insertar o actualizar
* @param conflictKeys las claves que causan conflictos y no deben actualizarse
* @return el QueryBuilder actual para encadenar más métodos
* @param <T> el tipo del objeto a insertar o actualizar
*/
public static <T> QueryBuilder upsert(T object, String... conflictKeys) { public static <T> QueryBuilder upsert(T object, String... conflictKeys) {
if (object == null) throw new IllegalArgumentException("Object cannot be null"); if (object == null) throw new IllegalArgumentException("Object cannot be null");
@@ -371,146 +220,75 @@ public class QueryBuilder {
String table = getTableName(object.getClass()); String table = getTableName(object.getClass());
qb.query.append("INSERT INTO ").append(table).append(" "); qb.query.append("INSERT INTO ").append(table).append(" ");
StringJoiner columns = new StringJoiner(", "); StringJoiner cols = new StringJoiner(", ");
StringJoiner values = new StringJoiner(", "); StringJoiner vals = new StringJoiner(", ");
Map<String, String> updates = new HashMap<>(); Map<String, String> updates = new HashMap<>();
for (Field field : object.getClass().getDeclaredFields()) { for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true); field.setAccessible(true);
try { try {
Object fieldValue = field.get(object); String col = getColumnName(field);
String columnName = field.getName(); Object value = extractValue(field.get(object));
columns.add(columnName); String formatted = qb.formatValue(value);
cols.add(col);
Object value = extractValue(fieldValue); vals.add(formatted);
String valueStr = value == null ? "NULL" if (!Arrays.asList(conflictKeys).contains(col)) updates.put(col, formatted);
: (value instanceof String || value instanceof LocalDateTime ? "'" + value + "'" : value.toString()); } catch (IllegalAccessException e) {
values.add(valueStr); LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
// no actualizamos la clave duplicada
boolean isConflictKey = Arrays.asList(conflictKeys).contains(columnName);
if (!isConflictKey) {
updates.put(columnName, valueStr);
}
} catch (Exception e) {
Constants.LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
} }
} }
qb.query.append("(").append(columns).append(") VALUES (").append(values).append(")"); qb.query.append("(").append(cols).append(") VALUES (").append(vals).append(")");
if (!updates.isEmpty() && conflictKeys.length > 0) {
if (conflictKeys.length > 0 && !updates.isEmpty()) {
qb.query.append(" ON DUPLICATE KEY UPDATE "); qb.query.append(" ON DUPLICATE KEY UPDATE ");
StringJoiner updateSet = new StringJoiner(", "); qb.query.append(updates.entrySet().stream()
updates.forEach((k, v) -> updateSet.add(k + " = " + v)); .map(e -> e.getKey() + " = " + e.getValue())
qb.query.append(updateSet); .collect(Collectors.joining(", ")));
} }
return qb; return qb;
} }
/**
* Construye una consulta DELETE para el objeto dado, eliminando registros que coincidan con sus campos.
* Los valores se extraen mediante reflexión y se manejan adecuadamente.
*
* @param object el objeto a eliminar
* @return el QueryBuilder actual para encadenar más métodos
* @param <T> el tipo del objeto a eliminar
*/
public static <T> QueryBuilder delete(T object) { public static <T> QueryBuilder delete(T object) {
if (object == null) throw new IllegalArgumentException("Object cannot be null"); if (object == null) throw new IllegalArgumentException("Object cannot be null");
QueryBuilder qb = new QueryBuilder(); QueryBuilder qb = new QueryBuilder();
String table = getTableName(object.getClass()); qb.entityClass = object.getClass();
qb.query.append("DELETE FROM ").append(table).append(" WHERE "); String table = getTableName(qb.entityClass);
qb.query.append("DELETE FROM ").append(table).append(" ");
StringJoiner joiner = new StringJoiner(" AND "); return qb.where(object);
for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
Object fieldValue = field.get(object);
if (fieldValue != null) {
Object value = extractValue(fieldValue);
joiner.add(field.getName() + " = " + (value instanceof String
|| value instanceof LocalDateTime ? "'" + value + "'" : value.toString()));
}
} catch (Exception e) {
Constants.LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
}
}
qb.query.append(joiner).append(" ");
return qb;
} }
/**
* Añade una cláusula ORDER BY a la consulta actual, ordenando por la columna y el orden especificados.
* Si la columna no es válida, se ignora.
*
* @param column la columna por la que ordenar
* @param order el orden (ASC o DESC); si no se especifica, se asume ASC
* @return el QueryBuilder actual para encadenar más métodos
*/
public QueryBuilder orderBy(Optional<String> column, Optional<String> order) { public QueryBuilder orderBy(Optional<String> column, Optional<String> order) {
column.ifPresent(c -> { column.ifPresent(c -> {
if (entityClass != null) { if (entityClass != null) {
boolean isValid = Arrays.stream(entityClass.getDeclaredFields()) boolean valid = Arrays.stream(entityClass.getDeclaredFields())
.map(Field::getName) .map(QueryBuilder::getColumnName)
.anyMatch(f -> f.equals(c)); .anyMatch(f -> f.equals(c));
if (!valid) {
if (!isValid) { LOGGER.warn("[QueryBuilder] Ignorando campo invalido en ORDER BY: {}", c);
Constants.LOGGER.warn("[QueryBuilder] Ignorando campo invalido en ORDER BY: {}", c);
return; return;
} }
} }
orderByClause = "ORDER BY " + c + " " + order.orElse("ASC") + " ";
sort = "ORDER BY " + c + " ";
order.ifPresent(o -> sort += o.equalsIgnoreCase("asc") ? "ASC" : "DESC" + " ");
}); });
return this; return this;
} }
/**
* Añade una cláusula LIMIT a la consulta actual, limitando el número de resultados.
* Si se especifica un offset, se añade también.
*
* @param limitParam el número máximo de resultados a devolver; si no se especifica, no se aplica límite
* @return el QueryBuilder actual para encadenar más métodos
*/
public QueryBuilder limit(Optional<Integer> limitParam) { public QueryBuilder limit(Optional<Integer> limitParam) {
limitParam.ifPresent(param -> limit = "LIMIT " + param + " "); limitParam.ifPresent(l -> limitClause = "LIMIT " + l + " ");
return this; return this;
} }
/**
* Añade una cláusula OFFSET a la consulta actual, desplazando el inicio de los resultados.
* Si se especifica un offset, se añade también.
*
* @param offsetParam el número de resultados a omitir antes de empezar a devolver resultados; si no se especifica, no se aplica offset
* @return el QueryBuilder actual para encadenar más métodos
*/
public QueryBuilder offset(Optional<Integer> offsetParam) { public QueryBuilder offset(Optional<Integer> offsetParam) {
offsetParam.ifPresent(param -> limit += "OFFSET " + param + " "); offsetParam.ifPresent(o -> offsetClause = "OFFSET " + o + " ");
return this; return this;
} }
/**
* Construye y devuelve la consulta SQL completa.
* Si no se han añadido cláusulas ORDER BY, LIMIT o OFFSET, las omite.
*
* @return la consulta SQL construida
*/
public String build() { public String build() {
if (order != null && !order.isEmpty()) { if (orderByClause != null) query.append(orderByClause);
query.append(order); if (limitClause != null) query.append(limitClause);
} if (offsetClause != null) query.append(offsetClause);
if (sort != null && !sort.isEmpty()) {
query.append(sort);
}
if (limit != null && !limit.isEmpty()) {
query.append(limit);
}
return query.toString().trim() + ";"; return query.toString().trim() + ";";
} }
} }

View File

@@ -0,0 +1,27 @@
package net.miarma.api.backlib.gson;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.vertx.core.json.JsonObject;
import net.miarma.api.backlib.interfaces.IValuableEnum;
import java.time.LocalDateTime;
public class GsonProvider {
private static final Gson INSTANCE = createGson();
private static Gson createGson() {
return new GsonBuilder()
.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter())
.registerTypeAdapter(JsonObject.class, new JsonObjectTypeAdapter())
.registerTypeHierarchyAdapter(IValuableEnum.class, new ValuableEnumDeserializer())
.registerTypeHierarchyAdapter(IValuableEnum.class, new ValuableEnumTypeAdapter())
.addSerializationExclusionStrategy(new APIDontReturnExclusionStrategy())
.create();
}
public static Gson get() {
return INSTANCE;
}
private GsonProvider() {}
}

View File

@@ -4,7 +4,8 @@ import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer; import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonParseException; import com.google.gson.JsonParseException;
import net.miarma.api.backlib.ValuableEnum;
import net.miarma.api.backlib.interfaces.IValuableEnum;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Arrays; import java.util.Arrays;
@@ -15,15 +16,15 @@ import java.util.Arrays;
* *
* @author José Manuel Amador Gallardo * @author José Manuel Amador Gallardo
*/ */
public class ValuableEnumDeserializer implements JsonDeserializer<ValuableEnum> { public class ValuableEnumDeserializer implements JsonDeserializer<IValuableEnum> {
@Override @Override
public ValuableEnum deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { public IValuableEnum deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Class<?> enumClass = (Class<?>) typeOfT; Class<?> enumClass = (Class<?>) typeOfT;
int value = json.getAsInt(); int value = json.getAsInt();
return (ValuableEnum) Arrays.stream(enumClass.getEnumConstants()) return (IValuableEnum) Arrays.stream(enumClass.getEnumConstants())
.filter(e -> ((ValuableEnum) e).getValue() == value) .filter(e -> ((IValuableEnum) e).getValue() == value)
.findFirst() .findFirst()
.orElseThrow(() -> new JsonParseException("Invalid enum value: " + value)); .orElseThrow(() -> new JsonParseException("Invalid enum value: " + value));
} }

View File

@@ -4,7 +4,8 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive; import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer; import com.google.gson.JsonSerializer;
import net.miarma.api.backlib.ValuableEnum;
import net.miarma.api.backlib.interfaces.IValuableEnum;
import java.lang.reflect.Type; import java.lang.reflect.Type;
@@ -14,10 +15,10 @@ import java.lang.reflect.Type;
* *
* @author José Manuel Amador Gallardo * @author José Manuel Amador Gallardo
*/ */
public class ValuableEnumTypeAdapter implements JsonSerializer<ValuableEnum> { public class ValuableEnumTypeAdapter implements JsonSerializer<IValuableEnum> {
@Override @Override
public JsonElement serialize(ValuableEnum src, Type typeOfSrc, JsonSerializationContext context) { public JsonElement serialize(IValuableEnum src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.getValue()); return new JsonPrimitive(src.getValue());
} }
} }

View File

@@ -1,21 +0,0 @@
package net.miarma.api.backlib.interfaces;
import java.time.LocalDateTime;
import net.miarma.api.backlib.Constants.CoreUserGlobalStatus;
import net.miarma.api.backlib.Constants.CoreUserRole;
public interface IUser {
Integer getUser_id();
String getUser_name();
String getEmail();
String getDisplay_name();
String getPassword();
String getAvatar();
CoreUserGlobalStatus getGlobal_status();
CoreUserRole getGlobal_role();
LocalDateTime getCreated_at();
default LocalDateTime getUpdated_at() {
return null;
}
}

View File

@@ -1,7 +1,5 @@
package net.miarma.api.backlib.interfaces; package net.miarma.api.backlib.interfaces;
import net.miarma.api.backlib.ValuableEnum; public interface IUserRole extends IValuableEnum {
public interface IUserRole extends ValuableEnum {
String name(); String name();
} }

View File

@@ -1,9 +1,9 @@
package net.miarma.api.backlib; package net.miarma.api.backlib.interfaces;
/** /**
* Interfaz que define un enum con un valor entero asociado * Interfaz que define un enum con un valor entero asociado
* @author José Manuel Amador Gallardo * @author José Manuel Amador Gallardo
*/ */
public interface ValuableEnum { public interface IValuableEnum {
int getValue(); int getValue();
} }

View File

@@ -1,4 +1,4 @@
package net.miarma.api.backlib; package net.miarma.api.backlib.log;
import org.slf4j.Logger; import org.slf4j.Logger;

View File

@@ -1,4 +1,4 @@
package net.miarma.api.backlib; package net.miarma.api.backlib.log;
public record LogEntry(int order, String message) { public record LogEntry(int order, String message) {
public static LogEntry of(int order, String message) { public static LogEntry of(int order, String message) {

View File

@@ -0,0 +1,11 @@
package net.miarma.api.backlib.log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LoggerProvider {
public static Logger getLogger() {
String className = Thread.currentThread().getStackTrace()[2].getClassName();
return LoggerFactory.getLogger(className);
}
}

View File

@@ -1,5 +1,6 @@
package net.miarma.api.backlib.middlewares; package net.miarma.api.backlib.middlewares;
import java.util.UUID;
import java.util.function.Consumer; import java.util.function.Consumer;
import io.vertx.core.Handler; import io.vertx.core.Handler;
@@ -18,7 +19,7 @@ import net.miarma.api.backlib.util.JsonUtil;
public abstract class AbstractAuthGuard<U, R extends Enum<R> & IUserRole> { public abstract class AbstractAuthGuard<U, R extends Enum<R> & IUserRole> {
protected abstract R parseRole(String roleStr); protected abstract R parseRole(String roleStr);
protected abstract void getUserEntity(int userId, RoutingContext ctx, Consumer<U> callback); protected abstract void getUserEntity(UUID userId, RoutingContext ctx, Consumer<U> callback);
protected abstract boolean hasPermission(U user, R role); protected abstract boolean hasPermission(U user, R role);
public Handler<RoutingContext> check(R... allowedRoles) { public Handler<RoutingContext> check(R... allowedRoles) {
@@ -29,7 +30,7 @@ public abstract class AbstractAuthGuard<U, R extends Enum<R> & IUserRole> {
return; return;
} }
int userId = JWTManager.getInstance().extractUserId(token); UUID userId = JWTManager.getInstance().extractUserId(token);
String roleStr = JWTManager.getInstance().extractRole(token); String roleStr = JWTManager.getInstance().extractRole(token);
R role; R role;

View File

@@ -1,83 +1,91 @@
package net.miarma.api.backlib.security; package net.miarma.api.backlib.security;
import java.util.Date; import java.util.Date;
import java.util.UUID;
import com.auth0.jwt.JWT; import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier; import com.auth0.jwt.interfaces.JWTVerifier;
import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.ConfigManager;
import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.interfaces.IUserRole; import net.miarma.api.backlib.interfaces.IUserRole;
/**
* Clase de gestión de JSON Web Tokens (JWT).
* Proporciona métodos para generar, verificar y decodificar tokens JWT.
* <p>
* Esta clase sigue el patron Singleton para asegurar una sola instancia.
*
* @author José Manuel Amador Gallardo
*/
public class JWTManager { public class JWTManager {
private final ConfigManager config = ConfigManager.getInstance(); private final ConfigManager config = ConfigManager.getInstance();
private final Algorithm algorithm; private final Algorithm algorithm;
private final JWTVerifier verifier; private final JWTVerifier verifier;
private static JWTManager instance; private static JWTManager instance;
private JWTManager() { private JWTManager() {
this.algorithm = Algorithm.HMAC256(config.getStringProperty("jwt.secret")); this.algorithm = Algorithm.HMAC256(config.getStringProperty("jwt.secret"));
this.verifier = JWT.require(algorithm).build(); this.verifier = JWT.require(algorithm).build();
} }
/**
* Obtiene la instancia única de JWTManager.
*
* @return La instancia única de JWTManager.
*/
public static synchronized JWTManager getInstance() { public static synchronized JWTManager getInstance() {
if (instance == null) { if (instance == null) {
instance = new JWTManager(); instance = new JWTManager();
} }
return instance; return instance;
}
/**
* Genera un token JWT para un usuario.
*
* @param user El usuario para el cual se generará el token.
* @param keepLoggedIn Indica si el token debe tener una duración prolongada.
* @return El token JWT generado.
*/
public String generateToken(String user_name, Integer user_id, IUserRole role, boolean keepLoggedIn) {
final long EXPIRATION_TIME_MS = 1000L * (keepLoggedIn ? config.getIntProperty("jwt.expiration") : config.getIntProperty("jwt.expiration.short"));
return JWT.create()
.withSubject(user_name)
.withClaim("userId", user_id)
.withClaim("role", role.name())
.withClaim("isAdmin", role == Constants.CoreUserRole.ADMIN)
.withIssuedAt(new Date())
.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME_MS))
.sign(algorithm);
} }
/** /**
* Decodifica un token JWT sin verificar su firma. * Genera un token JWT usando UUID para el usuario.
*
* @param token El token JWT a decodificar.
* @return Un objeto DecodedJWT que contiene la información del token.
*/ */
public DecodedJWT decodeWithoutVerification(String token) { public String generateToken(
String displayName,
UUID userId,
IUserRole role,
Integer serviceId,
boolean keepLoggedIn
) {
final long expiration = 1000L * (
keepLoggedIn
? config.getIntProperty("jwt.expiration")
: config.getIntProperty("jwt.expiration.short")
);
return JWT.create()
.withSubject(displayName)
.withClaim("userId", userId.toString())
.withClaim("serviceId", serviceId)
.withClaim("role", role.name())
.withIssuedAt(new Date())
.withExpiresAt(new Date(System.currentTimeMillis() + expiration))
.sign(algorithm);
}
public DecodedJWT decode(String token) {
return JWT.decode(token); return JWT.decode(token);
} }
/** public UUID extractUserId(String token) {
* Verifica la validez de un token JWT. try {
* DecodedJWT jwt = verifier.verify(token);
* @param token El token JWT a verificar. String uuidStr = jwt.getClaim("userId").asString();
* @return true si el token es válido, false en caso contrario. return UUID.fromString(uuidStr);
*/ } catch (Exception e) {
return null;
}
}
public Integer extractServiceId(String token) {
try {
DecodedJWT jwt = verifier.verify(token);
return jwt.getClaim("serviceId").asInt();
} catch (Exception e) {
return -1;
}
}
public String extractRole(String token) {
try {
DecodedJWT jwt = verifier.verify(token);
return jwt.getClaim("role").asString();
} catch (Exception e) {
return null;
}
}
public boolean isValid(String token) { public boolean isValid(String token) {
try { try {
verifier.verify(token); verifier.verify(token);
@@ -86,77 +94,4 @@ public class JWTManager {
return false; return false;
} }
} }
/**
* Verifica si un token JWT pertenece a un usuario administrador.
*
* @param token El token JWT a verificar.
* @return true si el token pertenece a un administrador, false en caso contrario.
*/
public boolean isAdmin(String token) {
try {
DecodedJWT jwt = verifier.verify(token);
return jwt.getClaim("isAdmin").asBoolean();
} catch (Exception e) {
return false;
}
}
/**
* Obtiene el ID de usuario a partir de un token JWT.
*
* @param token El token JWT del cual se extraerá el ID de usuario.
* @return El ID de usuario si el token es válido, -1 en caso contrario.
*/
public int getUserId(String token) {
try {
DecodedJWT jwt = verifier.verify(token);
return jwt.getClaim("userId").asInt();
} catch (Exception e) {
return -1;
}
}
/**
* Obtiene el sub especificado en un token JWT, que generalmente es el nombre de usuario.
*
* @param token El token JWT del cual se extraerá el nombre de usuario.
* @return El nombre de usuario si el token es válido, null en caso contrario.
*/
public String getSubject(String token) {
try {
DecodedJWT jwt = verifier.verify(token);
return jwt.getSubject();
} catch (Exception e) {
return null;
}
}
/**
* Extrae el ID de usuario de un token JWT.
* @param token El token JWT del cual se extraerá el ID de usuario.
* @return El ID de usuario si el token es válido, -1 en caso contrario.
*/
public int extractUserId(String token) {
try {
DecodedJWT jwt = verifier.verify(token);
return jwt.getClaim("userId").asInt();
} catch (Exception e) {
return -1;
}
}
/**
* Extrae el rol de usuario de un token JWT.
* @param token El token JWT del cual se extraerá el ID de usuario.
* @return El rol de usuario si el token es válido, null en caso contrario.
*/
public String extractRole(String token) {
try {
DecodedJWT jwt = verifier.verify(token);
return jwt.getClaim("role").asString();
} catch (Exception e) {
return null;
}
}
} }

View File

@@ -1,7 +1,5 @@
package net.miarma.api.backlib.security; package net.miarma.api.backlib.security;
import net.miarma.api.backlib.ConfigManager;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
@@ -10,6 +8,8 @@ import java.security.SecureRandom;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import net.miarma.api.backlib.config.ConfigManager;
/** /**
* Clase encargada de generar los secrets necesarios para la autenticación JWT. * Clase encargada de generar los secrets necesarios para la autenticación JWT.
* Si el secret ya existe en el archivo de configuración, lo devuelve. * Si el secret ya existe en el archivo de configuración, lo devuelve.

View File

@@ -0,0 +1,16 @@
package net.miarma.api.backlib.util;
import java.util.UUID;
import io.vertx.core.buffer.Buffer;
public class BufferUtil {
public static UUID uuidFromBuffer(Buffer b) {
if (b == null || b.length() != 16) {
return null;
}
long mostSigBits = b.getLong(0);
long leastSigBits = b.getLong(8);
return new UUID(mostSigBits, leastSigBits);
}
}

View File

@@ -3,7 +3,7 @@ package net.miarma.api.backlib.util;
import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject; import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.miarma.api.backlib.Constants; import net.miarma.api.backlib.gson.GsonProvider;
import net.miarma.api.backlib.http.ApiResponse; import net.miarma.api.backlib.http.ApiResponse;
import net.miarma.api.backlib.http.ApiStatus; import net.miarma.api.backlib.http.ApiStatus;
@@ -26,7 +26,7 @@ public class JsonUtil {
.put("data", data); .put("data", data);
ctx.response().end(response.encode()); ctx.response().end(response.encode());
} else { } else {
ctx.response().end(Constants.GSON.toJson(new ApiResponse<>(status, message, data))); ctx.response().end(GsonProvider.get().toJson(new ApiResponse<>(status, message, data)));
} }
} }

View File

@@ -1,13 +1,16 @@
package net.miarma.api.backlib.util; package net.miarma.api.backlib.util;
import org.slf4j.Logger;
import io.vertx.ext.web.Router; import io.vertx.ext.web.Router;
import net.miarma.api.backlib.Constants; import net.miarma.api.backlib.log.LoggerProvider;
/** /**
* Clase de utilidad para adjuntar un logger a un router de Vert.x. * Clase de utilidad para adjuntar un logger a un router de Vert.x.
* @author José Manuel Amador Gallardo * @author José Manuel Amador Gallardo
*/ */
public class RouterUtil { public class RouterUtil {
private final static Logger LOGGER = LoggerProvider.getLogger();
public static void attachLogger(Router router) { public static void attachLogger(Router router) {
router.route().handler(ctx -> { router.route().handler(ctx -> {
@@ -46,7 +49,7 @@ public class RouterUtil {
duration duration
); );
Constants.LOGGER.info(log); LOGGER.info(log);
}); });
ctx.next(); ctx.next();

View File

@@ -5,8 +5,8 @@
<parent> <parent>
<groupId>net.miarma.api</groupId> <groupId>net.miarma.api</groupId>
<artifactId>miarma-ecosystem</artifactId> <artifactId>miarma-backend</artifactId>
<version>1.2.1</version> <version>2.0.0</version>
</parent> </parent>
<artifactId>bootstrap</artifactId> <artifactId>bootstrap</artifactId>
@@ -39,7 +39,8 @@
<artifactId>core</artifactId> <artifactId>core</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency> <!--
<dependency>
<groupId>net.miarma.api</groupId> <groupId>net.miarma.api</groupId>
<artifactId>huertos</artifactId> <artifactId>huertos</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
@@ -51,7 +52,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.miarma.api</groupId> <groupId>net.miarma.api</groupId>
<artifactId>miarmacraft</artifactId> <artifactId>minecraft</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency> <dependency>
@@ -59,6 +60,7 @@
<artifactId>mpaste</artifactId> <artifactId>mpaste</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
-->
</dependencies> </dependencies>
<build> <build>

View File

@@ -1,18 +1,19 @@
package net.miarma.api; package net.miarma.api;
import net.miarma.api.backlib.ConfigManager;
import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.security.SecretManager;
import net.miarma.api.backlib.vertx.VertxJacksonConfig;
import net.miarma.api.backlib.util.MessageUtil;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import org.slf4j.Logger;
import io.vertx.core.Launcher; import io.vertx.core.Launcher;
import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.log.LoggerProvider;
import net.miarma.api.backlib.security.SecretManager;
import net.miarma.api.backlib.util.MessageUtil;
import net.miarma.api.backlib.vertx.VertxJacksonConfig;
/** /**
* Punto de entrada para inicializar la aplicación. * Punto de entrada para inicializar la aplicación.
@@ -24,12 +25,13 @@ import io.vertx.core.Launcher;
* - Desplegar el Verticle Master * - Desplegar el Verticle Master
*/ */
public class AppInitializer { public class AppInitializer {
private final static Logger LOGGER = LoggerProvider.getLogger();
public static void main(String[] args) { public static void main(String[] args) {
AppInitializer initializer = new AppInitializer(); AppInitializer initializer = new AppInitializer();
initializer.init(); initializer.init();
initializer.deployMaster(); initializer.deployMaster();
Constants.LOGGER.info("✅ App initialized successfully!"); LOGGER.info("✅ App initialized successfully!");
} }
private final ConfigManager configManager; private final ConfigManager configManager;
@@ -49,7 +51,7 @@ public class AppInitializer {
private void initializeDirectories() { private void initializeDirectories() {
File baseDir = new File(configManager.getBaseDir()); File baseDir = new File(configManager.getBaseDir());
if (!baseDir.exists() && baseDir.mkdirs()) { if (!baseDir.exists() && baseDir.mkdirs()) {
Constants.LOGGER.info("Created base directory: " + baseDir.getAbsolutePath()); LOGGER.info("Created base directory: " + baseDir.getAbsolutePath());
} }
} }
@@ -59,12 +61,12 @@ public class AppInitializer {
try (InputStream in = getClass().getClassLoader().getResourceAsStream("default.properties")) { try (InputStream in = getClass().getClassLoader().getResourceAsStream("default.properties")) {
if (in != null) { if (in != null) {
Files.copy(in, configFile.toPath(), StandardCopyOption.REPLACE_EXISTING); Files.copy(in, configFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
Constants.LOGGER.info("Copied default.properties to: " + configFile.getAbsolutePath()); LOGGER.info("Copied default.properties to: " + configFile.getAbsolutePath());
} else { } else {
Constants.LOGGER.error(MessageUtil.notFound("Default config", "resources")); LOGGER.error(MessageUtil.notFound("Default config", "resources"));
} }
} catch (IOException e) { } catch (IOException e) {
Constants.LOGGER.error(MessageUtil.failedTo("copy", "default config", e)); LOGGER.error(MessageUtil.failedTo("copy", "default config", e));
} }
} }
} }

View File

@@ -1,32 +1,38 @@
package net.miarma.api; package net.miarma.api;
import java.util.Set; import java.util.List;
import org.slf4j.Logger;
import io.vertx.core.AbstractVerticle; import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future; import io.vertx.core.Future;
import io.vertx.core.Promise; import io.vertx.core.Promise;
import net.miarma.api.backlib.Constants; import net.miarma.api.backlib.log.LogAccumulator;
import net.miarma.api.backlib.LogAccumulator; import net.miarma.api.backlib.log.LoggerProvider;
import net.miarma.api.backlib.util.DeploymentUtil; import net.miarma.api.backlib.util.DeploymentUtil;
import net.miarma.api.microservices.core.verticles.CoreMainVerticle; import net.miarma.api.microservices.core.verticles.CoreMainVerticle;
import net.miarma.api.microservices.huertos.verticles.HuertosMainVerticle; //import net.miarma.api.microservices.huertos.verticles.HuertosMainVerticle;
import net.miarma.api.microservices.huertosdecine.verticles.CineMainVerticle; //import net.miarma.api.microservices.huertosdecine.verticles.CineMainVerticle;
import net.miarma.api.microservices.miarmacraft.verticles.MMCMainVerticle; //import net.miarma.api.microservices.minecraft.verticles.MMCMainVerticle;
import net.miarma.api.microservices.mpaste.verticles.MPasteMainVerticle; //import net.miarma.api.microservices.mpaste.verticles.MPasteMainVerticle;
@SuppressWarnings("unused")
public class MasterVerticle extends AbstractVerticle { public class MasterVerticle extends AbstractVerticle {
private final Logger LOGGER = LoggerProvider.getLogger();
@Override @Override
public void start(Promise<Void> startPromise) { public void start(Promise<Void> startPromise) {
deploy() deploy()
.onSuccess(v -> { .onSuccess(v -> {
vertx.setTimer(300, id -> { vertx.setTimer(300, id -> {
LogAccumulator.flushToLogger(Constants.LOGGER); LogAccumulator.flushToLogger(LOGGER);
startPromise.complete(); startPromise.complete();
}); });
}) })
.onFailure(startPromise::fail); .onFailure(startPromise::fail);
} }
@SuppressWarnings("unused")
private Future<Void> deploy() { private Future<Void> deploy() {
Promise<Void> promise = Promise.promise(); Promise<Void> promise = Promise.promise();
@@ -34,7 +40,7 @@ public class MasterVerticle extends AbstractVerticle {
.onSuccess(id -> LogAccumulator.add(DeploymentUtil.successMessage(CoreMainVerticle.class))) .onSuccess(id -> LogAccumulator.add(DeploymentUtil.successMessage(CoreMainVerticle.class)))
.onFailure(err -> LogAccumulator.add(DeploymentUtil.failMessage(CoreMainVerticle.class, err))); .onFailure(err -> LogAccumulator.add(DeploymentUtil.failMessage(CoreMainVerticle.class, err)));
Future<String> huertos = vertx.deployVerticle(new HuertosMainVerticle()) /*Future<String> huertos = vertx.deployVerticle(new HuertosMainVerticle())
.onSuccess(id -> LogAccumulator.add(DeploymentUtil.successMessage(HuertosMainVerticle.class))) .onSuccess(id -> LogAccumulator.add(DeploymentUtil.successMessage(HuertosMainVerticle.class)))
.onFailure(err -> LogAccumulator.add(DeploymentUtil.failMessage(HuertosMainVerticle.class, err))); .onFailure(err -> LogAccumulator.add(DeploymentUtil.failMessage(HuertosMainVerticle.class, err)));
@@ -48,9 +54,10 @@ public class MasterVerticle extends AbstractVerticle {
Future<String> mpaste = vertx.deployVerticle(new MPasteMainVerticle()) Future<String> mpaste = vertx.deployVerticle(new MPasteMainVerticle())
.onSuccess(id -> LogAccumulator.add(DeploymentUtil.successMessage(MPasteMainVerticle.class))) .onSuccess(id -> LogAccumulator.add(DeploymentUtil.successMessage(MPasteMainVerticle.class)))
.onFailure(err -> LogAccumulator.add(DeploymentUtil.failMessage(MPasteMainVerticle.class, err))); .onFailure(err -> LogAccumulator.add(DeploymentUtil.failMessage(MPasteMainVerticle.class, err)));*/
Future.all(core, huertos, mmc, cine, mpaste) //Future.all(core, huertos, mmc, cine, mpaste)
Future.all(List.of(core))
.onSuccess(_ -> promise.complete()) .onSuccess(_ -> promise.complete())
.onFailure(promise::fail); .onFailure(promise::fail);

View File

@@ -1,3 +1,25 @@
# App Configuration
app.name=miarma-backend
app.version=v2
# API Configuration
api.base=/api
api.core.prefix=/core/${app.version}
api.auth.prefix=/auth/${app.version}
api.huertos.prefix=/huertos/${app.version}
api.minecraft.prefix=/minecraft/${app.version}
api.cine.prefix=/cine/${app.version}
api.mpaste.prefix=/mpaste/${app.version}
# EventBus Configuration
eventbus.auth.address=auth.eventbus
eventbus.core.address=core.eventbus
eventbus.huertos.address=huertos.eventbus
eventbus.minecraft.address=minecraft.eventbus
eventbus.cine.address=cine.eventbus
eventbus.mpaste.address=mpaste.eventbus
# DB Configuration # DB Configuration
db.protocol=jdbc:mariadb db.protocol=jdbc:mariadb
db.host=localhost db.host=localhost
@@ -9,10 +31,10 @@ dp.poolSize=5
# HTTP Server Configuration # HTTP Server Configuration
inet.host=localhost inet.host=localhost
sso.logic.port=8080 core.logic.port=8080
sso.data.port=8081 core.data.port=8081
mmc.logic.port=8100 minecraft.logic.port=8100
mmc.data.port=8101 minecraft.data.port=8101
huertos.logic.port=8120 huertos.logic.port=8120
huertos.data.port=8121 huertos.data.port=8121
cine.data.port = 8140 cine.data.port = 8140

View File

@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>net.miarma.api</groupId> <groupId>net.miarma.api</groupId>
<artifactId>core</artifactId> <artifactId>core</artifactId>
<version>1.2.1</version> <version>2.0.0</version>
<properties> <properties>
<maven.compiler.source>23</maven.compiler.source> <maven.compiler.source>23</maven.compiler.source>
@@ -20,7 +20,7 @@
<dependency> <dependency>
<groupId>net.miarma.api</groupId> <groupId>net.miarma.api</groupId>
<artifactId>backlib</artifactId> <artifactId>backlib</artifactId>
<version>1.2.1</version> <version>2.0.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -0,0 +1,172 @@
package net.miarma.api.microservices.core.dao;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.sqlclient.Pool;
import net.miarma.api.microservices.core.entities.CredentialEntity;
import net.miarma.api.backlib.db.DataAccessObject;
import net.miarma.api.backlib.db.DatabaseManager;
import net.miarma.api.backlib.db.QueryBuilder;
import net.miarma.api.backlib.http.QueryFilters;
import net.miarma.api.backlib.http.QueryParams;
public class CredentialDAO implements DataAccessObject<CredentialEntity, UUID> {
private final DatabaseManager db;
public CredentialDAO(Pool pool) {
this.db = DatabaseManager.getInstance(pool);
}
@Override
public Future<List<CredentialEntity>> getAll() {
return getAll(new QueryParams(Map.of(), new QueryFilters()));
}
@Override
public Future<CredentialEntity> getById(UUID id) {
Promise<CredentialEntity> promise = Promise.promise();
String query = QueryBuilder
.select(CredentialEntity.class)
.where(Map.of("credential_id", id.toString()))
.build();
db.executeOne(query, CredentialEntity.class,
promise::complete,
promise::fail
);
return promise.future();
}
public Future<List<CredentialEntity>> getAll(QueryParams params) {
Promise<List<CredentialEntity>> promise = Promise.promise();
String query = QueryBuilder
.select(CredentialEntity.class)
.where(params.getFilters())
.orderBy(params.getQueryFilters().getSort(), params.getQueryFilters().getOrder())
.limit(params.getQueryFilters().getLimit())
.offset(params.getQueryFilters().getOffset())
.build();
db.execute(query, CredentialEntity.class,
list -> promise.complete(list.isEmpty() ? List.of() : list),
promise::fail
);
return promise.future();
}
/**
* Útil para el login: busca la credencial por servicio y usuario
*/
public Future<CredentialEntity> getByServiceAndUsername(int serviceId, String username) {
Promise<CredentialEntity> promise = Promise.promise();
String query = QueryBuilder
.select(CredentialEntity.class)
.where(Map.of("service_id", String.valueOf(serviceId), "username", username))
.build();
db.executeOne(query, CredentialEntity.class,
promise::complete,
promise::fail
);
return promise.future();
}
public Future<List<CredentialEntity>> getByUserId(UUID userId) {
Promise<List<CredentialEntity>> promise = Promise.promise();
String query = QueryBuilder
.select(CredentialEntity.class)
.where(Map.of("user_id", userId.toString()))
.build();
db.execute(query, CredentialEntity.class,
promise::complete,
promise::fail
);
return promise.future();
}
@Override
public Future<CredentialEntity> insert(CredentialEntity credential) {
Promise<CredentialEntity> promise = Promise.promise();
// Si no tiene ID, se lo generamos antes de insertar
if (credential.getCredentialId() == null) {
credential.setCredentialId(UUID.randomUUID());
}
String query = QueryBuilder.insert(credential).build();
db.executeOne(query, CredentialEntity.class,
promise::complete,
promise::fail
);
return promise.future();
}
@Override
public Future<CredentialEntity> upsert(CredentialEntity credential, String... conflictKeys) {
Promise<CredentialEntity> promise = Promise.promise();
String query = QueryBuilder.upsert(credential, conflictKeys).build();
db.executeOne(query, CredentialEntity.class,
promise::complete,
promise::fail
);
return promise.future();
}
@Override
public Future<CredentialEntity> update(CredentialEntity credential) {
Promise<CredentialEntity> promise = Promise.promise();
String query = QueryBuilder.update(credential).build();
db.executeOne(query, CredentialEntity.class,
_ -> promise.complete(credential),
promise::fail
);
return promise.future();
}
@Override
public Future<Boolean> delete(UUID id) {
Promise<Boolean> promise = Promise.promise();
CredentialEntity cred = new CredentialEntity();
cred.setCredentialId(id);
String query = QueryBuilder.delete(cred).build();
db.executeOne(query, CredentialEntity.class,
result -> promise.complete(result != null),
promise::fail
);
return promise.future();
}
@Override
public Future<Boolean> exists(UUID id) {
Promise<Boolean> promise = Promise.promise();
String query = QueryBuilder
.select(CredentialEntity.class)
.where(Map.of("credential_id", id.toString()))
.build();
db.executeOne(query, CredentialEntity.class,
result -> promise.complete(result != null),
promise::fail
);
return promise.future();
}
}

View File

@@ -1,4 +1,8 @@
package net.miarma.api.backlib.core.dao; package net.miarma.api.microservices.core.dao;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import io.vertx.core.Future; import io.vertx.core.Future;
import io.vertx.core.Promise; import io.vertx.core.Promise;
@@ -8,12 +12,9 @@ import net.miarma.api.backlib.db.DatabaseManager;
import net.miarma.api.backlib.db.QueryBuilder; import net.miarma.api.backlib.db.QueryBuilder;
import net.miarma.api.backlib.http.QueryFilters; import net.miarma.api.backlib.http.QueryFilters;
import net.miarma.api.backlib.http.QueryParams; import net.miarma.api.backlib.http.QueryParams;
import net.miarma.api.backlib.core.entities.FileEntity; import net.miarma.api.microservices.core.entities.FileEntity;
import java.util.List; public class FileDAO implements DataAccessObject<FileEntity, UUID> {
import java.util.Map;
public class FileDAO implements DataAccessObject<FileEntity, Integer> {
private final DatabaseManager db; private final DatabaseManager db;
@@ -27,7 +28,7 @@ public class FileDAO implements DataAccessObject<FileEntity, Integer> {
} }
@Override @Override
public Future<FileEntity> getById(Integer id) { public Future<FileEntity> getById(UUID id) {
Promise<FileEntity> promise = Promise.promise(); Promise<FileEntity> promise = Promise.promise();
String query = QueryBuilder String query = QueryBuilder
.select(FileEntity.class) .select(FileEntity.class)
@@ -60,7 +61,7 @@ public class FileDAO implements DataAccessObject<FileEntity, Integer> {
return promise.future(); return promise.future();
} }
public Future<List<FileEntity>> getUserFiles(Integer userId) { public Future<List<FileEntity>> getUserFiles(UUID userId) {
Promise<List<FileEntity>> promise = Promise.promise(); Promise<List<FileEntity>> promise = Promise.promise();
String query = QueryBuilder String query = QueryBuilder
.select(FileEntity.class) .select(FileEntity.class)
@@ -115,7 +116,7 @@ public class FileDAO implements DataAccessObject<FileEntity, Integer> {
} }
@Override @Override
public Future<Boolean> exists(Integer id) { public Future<Boolean> exists(UUID id) {
Promise<Boolean> promise = Promise.promise(); Promise<Boolean> promise = Promise.promise();
String query = QueryBuilder String query = QueryBuilder
.select(FileEntity.class) .select(FileEntity.class)
@@ -131,10 +132,10 @@ public class FileDAO implements DataAccessObject<FileEntity, Integer> {
} }
@Override @Override
public Future<Boolean> delete(Integer id) { public Future<Boolean> delete(UUID id) {
Promise<Boolean> promise = Promise.promise(); Promise<Boolean> promise = Promise.promise();
FileEntity file = new FileEntity(); FileEntity file = new FileEntity();
file.setFile_id(id); file.setFileId(id);
String query = QueryBuilder.delete(file).build(); String query = QueryBuilder.delete(file).build();

View File

@@ -1,19 +1,20 @@
package net.miarma.api.backlib.core.dao; package net.miarma.api.microservices.core.dao;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import io.vertx.core.Future; import io.vertx.core.Future;
import io.vertx.core.Promise; import io.vertx.core.Promise;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.core.entities.UserEntity; import net.miarma.api.microservices.core.entities.UserEntity;
import net.miarma.api.backlib.db.DataAccessObject; import net.miarma.api.backlib.db.DataAccessObject;
import net.miarma.api.backlib.db.DatabaseManager; import net.miarma.api.backlib.db.DatabaseManager;
import net.miarma.api.backlib.db.QueryBuilder; import net.miarma.api.backlib.db.QueryBuilder;
import net.miarma.api.backlib.http.QueryFilters; import net.miarma.api.backlib.http.QueryFilters;
import net.miarma.api.backlib.http.QueryParams; import net.miarma.api.backlib.http.QueryParams;
public class UserDAO implements DataAccessObject<UserEntity, Integer> { public class UserDAO implements DataAccessObject<UserEntity, UUID> {
private final DatabaseManager db; private final DatabaseManager db;
@@ -27,13 +28,15 @@ public class UserDAO implements DataAccessObject<UserEntity, Integer> {
} }
@Override @Override
public Future<UserEntity> getById(Integer id) { public Future<UserEntity> getById(UUID id) {
Promise<UserEntity> promise = Promise.promise(); Promise<UserEntity> promise = Promise.promise();
String query = QueryBuilder String query = QueryBuilder
.select(UserEntity.class) .select(UserEntity.class)
.where(Map.of("user_id", id.toString())) .where(Map.of("user_id", id))
.build(); .build();
System.out.println(query);
db.executeOne(query, UserEntity.class, db.executeOne(query, UserEntity.class,
promise::complete, promise::complete,
promise::fail promise::fail
@@ -60,36 +63,6 @@ public class UserDAO implements DataAccessObject<UserEntity, Integer> {
return promise.future(); return promise.future();
} }
public Future<UserEntity> getByEmail(String email) {
Promise<UserEntity> promise = Promise.promise();
String query = QueryBuilder
.select(UserEntity.class)
.where(Map.of("email", email))
.build();
db.executeOne(query, UserEntity.class,
promise::complete,
promise::fail
);
return promise.future();
}
public Future<UserEntity> getByUserName(String userName) {
Promise<UserEntity> promise = Promise.promise();
String query = QueryBuilder
.select(UserEntity.class)
.where(Map.of("user_name", userName))
.build();
db.executeOne(query, UserEntity.class,
promise::complete,
promise::fail
);
return promise.future();
}
@Override @Override
public Future<UserEntity> insert(UserEntity user) { public Future<UserEntity> insert(UserEntity user) {
Promise<UserEntity> promise = Promise.promise(); Promise<UserEntity> promise = Promise.promise();
@@ -130,10 +103,10 @@ public class UserDAO implements DataAccessObject<UserEntity, Integer> {
} }
@Override @Override
public Future<Boolean> delete(Integer id) { public Future<Boolean> delete(UUID id) {
Promise<Boolean> promise = Promise.promise(); Promise<Boolean> promise = Promise.promise();
UserEntity user = new UserEntity(); UserEntity user = new UserEntity();
user.setUser_id(id); user.setUserId(id);
String query = QueryBuilder.delete(user).build(); String query = QueryBuilder.delete(user).build();
@@ -146,7 +119,7 @@ public class UserDAO implements DataAccessObject<UserEntity, Integer> {
} }
@Override @Override
public Future<Boolean> exists(Integer id) { public Future<Boolean> exists(UUID id) {
Promise<Boolean> promise = Promise.promise(); Promise<Boolean> promise = Promise.promise();
String query = QueryBuilder String query = QueryBuilder
.select(UserEntity.class) .select(UserEntity.class)

View File

@@ -0,0 +1,114 @@
package net.miarma.api.microservices.core.entities;
import java.time.LocalDateTime;
import java.util.UUID;
import com.google.gson.annotations.SerializedName;
import io.vertx.sqlclient.Row;
import net.miarma.api.backlib.annotations.APIDontReturn;
import net.miarma.api.backlib.annotations.Table;
import net.miarma.api.backlib.db.AbstractEntity;
@Table("credentials")
public class CredentialEntity extends AbstractEntity {
@SerializedName("credential_id")
private UUID credentialId;
@SerializedName("user_id")
private UUID userId;
@SerializedName("service_id")
private int serviceId;
private String username;
private String email;
@APIDontReturn
private String password;
private int status;
@SerializedName("created_at")
private LocalDateTime createdAt;
@SerializedName("updated_at")
private LocalDateTime updatedAt;
public CredentialEntity() { }
public CredentialEntity(Row row) { super(row); }
public UUID getCredentialId() {
return credentialId;
}
public void setCredentialId(UUID credentialId) {
this.credentialId = credentialId;
}
public UUID getUserId() {
return userId;
}
public void setUserId(UUID userId) {
this.userId = userId;
}
public int getServiceId() {
return serviceId;
}
public void setServiceId(int serviceId) {
this.serviceId = serviceId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
}

View File

@@ -0,0 +1,99 @@
package net.miarma.api.microservices.core.entities;
import java.time.LocalDateTime;
import java.util.UUID;
import com.google.gson.annotations.SerializedName;
import io.vertx.sqlclient.Row;
import net.miarma.api.backlib.annotations.Table;
import net.miarma.api.backlib.db.AbstractEntity;
import net.miarma.api.microservices.core.enums.CoreFileContext;
@Table("files")
public class FileEntity extends AbstractEntity {
@SerializedName("file_id")
private UUID fileId;
@SerializedName("file_name")
private String fileName;
@SerializedName("file_path")
private String filePath;
@SerializedName("mime_type")
private String mimeType;
@SerializedName("uploaded_by")
private UUID uploadedBy;
private CoreFileContext context;
@SerializedName("uploaded_at")
private LocalDateTime uploadedAt;
public FileEntity() {
super();
}
public FileEntity(Row row) {
super(row);
}
public UUID getFileId() {
return fileId;
}
public void setFileId(UUID fileId) {
this.fileId = fileId;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public String getMimeType() {
return mimeType;
}
public void setMimeType(String mimeType) {
this.mimeType = mimeType;
}
public UUID getUploadedBy() {
return uploadedBy;
}
public void setUploadedBy(UUID uploadedBy) {
this.uploadedBy = uploadedBy;
}
public CoreFileContext getContext() {
return context;
}
public void setContext(CoreFileContext context) {
this.context = context;
}
public LocalDateTime getUploadedAt() {
return uploadedAt;
}
public void setUploadedAt(LocalDateTime uploadedAt) {
this.uploadedAt = uploadedAt;
}
}

View File

@@ -0,0 +1,59 @@
package net.miarma.api.microservices.core.entities;
import java.time.LocalDateTime;
import java.util.UUID;
import com.google.gson.annotations.SerializedName;
import io.vertx.sqlclient.Row;
import net.miarma.api.backlib.annotations.Table;
import net.miarma.api.backlib.db.AbstractEntity;
import net.miarma.api.microservices.core.enums.CoreUserGlobalRole;
import net.miarma.api.microservices.core.enums.CoreUserGlobalStatus;
@Table("users")
public class UserEntity extends AbstractEntity {
@SerializedName("user_id")
private UUID userId;
@SerializedName("display_name")
private String displayName;
private String avatar;
@SerializedName("global_status")
private CoreUserGlobalStatus globalStatus;
@SerializedName("global_role")
private CoreUserGlobalRole globalRole;
@SerializedName("created_at")
private LocalDateTime createdAt;
@SerializedName("updated_at")
private LocalDateTime updatedAt;
public UserEntity() { }
public UserEntity(Row row) { super(row); }
public UUID getUserId() { return userId; }
public void setUserId(UUID userId) { this.userId = userId; }
public String getDisplayName() { return displayName; }
public void setDisplayName(String displayName) { this.displayName = displayName; }
public String getAvatar() { return avatar; }
public void setAvatar(String avatar) { this.avatar = avatar; }
public CoreUserGlobalStatus getGlobalStatus() { return globalStatus; }
public void setGlobalStatus(CoreUserGlobalStatus globalStatus) { this.globalStatus = globalStatus; }
public CoreUserGlobalRole getGlobalRole() { return globalRole; }
public void setGlobalRole(CoreUserGlobalRole role) { this.globalRole = role; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
}

View File

@@ -0,0 +1,37 @@
package net.miarma.api.microservices.core.enums;
import net.miarma.api.backlib.interfaces.IValuableEnum;
public enum CoreFileContext implements IValuableEnum {
CORE(0),
HUERTOS(1),
MMC(2),
CINE(3);
private final int value;
CoreFileContext(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public String toCtxString() {
return switch(this) {
case CORE -> "core";
case HUERTOS -> "huertos";
case MMC -> "miarmacraft";
case CINE -> "cine";
};
}
public static CoreFileContext fromInt(int i) {
for (CoreFileContext context : values()) {
if (context.value == i) return context;
}
throw new IllegalArgumentException("Invalid CoreFileContext value: " + i);
}
}

View File

@@ -0,0 +1,26 @@
package net.miarma.api.microservices.core.enums;
import net.miarma.api.backlib.interfaces.IUserRole;
public enum CoreUserGlobalRole implements IUserRole {
USER(0),
ADMIN(1);
private final int value;
CoreUserGlobalRole(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static CoreUserGlobalRole fromInt(int i) {
for (CoreUserGlobalRole role : values()) {
if (role.value == i) return role;
}
throw new IllegalArgumentException("Invalid CoreUserGlobalRole value: " + i);
}
}

View File

@@ -0,0 +1,26 @@
package net.miarma.api.microservices.core.enums;
import net.miarma.api.backlib.interfaces.IValuableEnum;
public enum CoreUserGlobalStatus implements IValuableEnum {
INACTIVE(0),
ACTIVE(1);
private final int value;
CoreUserGlobalStatus(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static CoreUserGlobalStatus fromInt(int i) {
for (CoreUserGlobalStatus status : values()) {
if (status.value == i) return status;
}
throw new IllegalArgumentException("Invalid CoreUserGlobalStatus value: " + i);
}
}

View File

@@ -0,0 +1,146 @@
package net.miarma.api.microservices.core.handlers;
import java.util.UUID;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.google.gson.Gson;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.gson.GsonProvider;
import net.miarma.api.backlib.http.ApiStatus;
import net.miarma.api.backlib.security.JWTManager;
import net.miarma.api.backlib.util.EventBusUtil;
import net.miarma.api.backlib.util.JsonUtil;
import net.miarma.api.microservices.core.entities.UserEntity;
@SuppressWarnings("unused")
public class AuthHandler {
private final Gson GSON = GsonProvider.get();
private final Vertx vertx;
private final String AUTH_EVENT_BUS = ConfigManager.getInstance()
.getStringProperty("eventbus.auth.address");
public AuthHandler(Vertx vertx) {
this.vertx = vertx;
}
public void login(RoutingContext ctx) {
vertx.eventBus().request(AUTH_EVENT_BUS, ctx.body().asJsonObject().put("action", "login"))
.onSuccess(reply -> JsonUtil.sendJson(ctx, ApiStatus.OK, reply.body()))
.onFailure(err -> EventBusUtil.handleReplyError(ctx, err));
}
public void loginValidate(RoutingContext ctx) {
JsonObject body = ctx.body().asJsonObject();
JsonObject request = new JsonObject()
.put("action", "loginValidate")
.put("userId", body.getInteger("userId"))
.put("password", body.getString("password"));
vertx.eventBus().request(AUTH_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void validateToken(RoutingContext ctx) {
String authHeader = ctx.request().getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
JsonObject request = new JsonObject()
.put("action", "validateToken")
.put("token", token);
vertx.eventBus().request(AUTH_EVENT_BUS, request, ar -> {
if (ar.succeeded() && Boolean.TRUE.equals(ar.result().body())) {
JsonUtil.sendJson(ctx, ApiStatus.OK, true, "Valid token");
} else {
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, false, "Invalid token");
}
});
} else {
JsonUtil.sendJson(ctx, ApiStatus.BAD_REQUEST, null, "Missing or invalid Authorization header");
}
}
public void refreshToken(RoutingContext ctx) {
String tokenHeader = ctx.request().getHeader("Authorization");
if (tokenHeader == null || !tokenHeader.startsWith("Bearer ")) {
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, null, "Missing or invalid Authorization header");
return;
}
String token = tokenHeader.substring("Bearer ".length());
JWTManager jwt = JWTManager.getInstance();
try {
DecodedJWT decoded = jwt.decode(token);
String userIdStr = decoded.getClaim("userId").asString();
if (userIdStr == null) {
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, null, "Invalid token claims");
return;
}
UUID userId = UUID.fromString(userIdStr);
vertx.eventBus().request(AUTH_EVENT_BUS, new JsonObject()
.put("action", "getUserById")
.put("userId", userId.toString()), ar -> {
if (ar.succeeded()) {
JsonObject userJson = (JsonObject) ar.result().body();
UserEntity user = GSON.fromJson(userJson.encode(), UserEntity.class);
String newToken = jwt.generateToken(
user.getDisplayName(),
user.getUserId(),
user.getGlobalRole(),
0,
false
);
JsonUtil.sendJson(ctx, ApiStatus.OK, new JsonObject().put("token", newToken));
} else {
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, null, "User not found or service unavailable");
}
});
} catch (Exception e) {
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, null, "Invalid token format");
}
}
public void changePassword(RoutingContext ctx) {
JsonObject body = ctx.body().asJsonObject();
JsonObject request = new JsonObject()
.put("action", "changePassword")
.put("userId", body.getInteger("userId"))
.put("newPassword", body.getString("newPassword"));
vertx.eventBus().request(AUTH_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, true, "Updated");
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void register(RoutingContext ctx) {
vertx.eventBus().request(AUTH_EVENT_BUS, ctx.body().asJsonObject().put("action", "register"))
.onSuccess(reply -> JsonUtil.sendJson(ctx, ApiStatus.CREATED, null))
.onFailure(err -> EventBusUtil.handleReplyError(ctx, err));
}
}

View File

@@ -1,25 +1,33 @@
package net.miarma.api.backlib.core.handlers; package net.miarma.api.microservices.core.handlers;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.UUID;
import org.slf4j.Logger;
import com.google.gson.Gson;
import io.vertx.core.buffer.Buffer; import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject; import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.FileUpload; import io.vertx.ext.web.FileUpload;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.Constants; import net.miarma.api.backlib.gson.GsonProvider;
import net.miarma.api.backlib.Constants.CoreFileContext;
import net.miarma.api.backlib.http.ApiStatus; import net.miarma.api.backlib.http.ApiStatus;
import net.miarma.api.backlib.http.QueryParams; import net.miarma.api.backlib.http.QueryParams;
import net.miarma.api.backlib.core.entities.FileEntity; import net.miarma.api.backlib.log.LoggerProvider;
import net.miarma.api.backlib.core.services.FileService;
import net.miarma.api.backlib.util.JsonUtil; import net.miarma.api.backlib.util.JsonUtil;
import net.miarma.api.microservices.core.entities.FileEntity;
import java.io.IOException; import net.miarma.api.microservices.core.enums.CoreFileContext;
import java.nio.file.Files; import net.miarma.api.microservices.core.services.FileService;
import java.nio.file.Paths;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class FileDataHandler { public class FileDataHandler {
private final Logger LOGGER = LoggerProvider.getLogger();
private final Gson GSON = GsonProvider.get();
private final FileService fileService; private final FileService fileService;
public FileDataHandler(Pool pool) { public FileDataHandler(Pool pool) {
@@ -35,7 +43,7 @@ public class FileDataHandler {
} }
public void getById(RoutingContext ctx) { public void getById(RoutingContext ctx) {
Integer fileId = Integer.parseInt(ctx.pathParam("file_id")); UUID fileId = UUID.fromString(ctx.pathParam("file_id"));
fileService.getById(fileId) fileService.getById(fileId)
.onSuccess(file -> JsonUtil.sendJson(ctx, ApiStatus.OK, file)) .onSuccess(file -> JsonUtil.sendJson(ctx, ApiStatus.OK, file))
@@ -46,7 +54,7 @@ public class FileDataHandler {
try { try {
String fileName = ctx.request().getFormAttribute("file_name"); String fileName = ctx.request().getFormAttribute("file_name");
String mimeType = ctx.request().getFormAttribute("mime_type"); String mimeType = ctx.request().getFormAttribute("mime_type");
int uploadedBy = Integer.parseInt(ctx.request().getFormAttribute("uploaded_by")); UUID uploadedBy = UUID.fromString(ctx.request().getFormAttribute("uploaded_by"));
int contextValue = Integer.parseInt(ctx.request().getFormAttribute("context")); int contextValue = Integer.parseInt(ctx.request().getFormAttribute("context"));
FileUpload upload = ctx.fileUploads().stream() FileUpload upload = ctx.fileUploads().stream()
@@ -58,23 +66,23 @@ public class FileDataHandler {
byte[] fileBinary = buffer.getBytes(); byte[] fileBinary = buffer.getBytes();
FileEntity file = new FileEntity(); FileEntity file = new FileEntity();
file.setFile_name(fileName); file.setFileName(fileName);
file.setMime_type(mimeType); file.setMimeType(mimeType);
file.setUploaded_by(uploadedBy); file.setUploadedBy(uploadedBy);
file.setContext(CoreFileContext.fromInt(contextValue)); file.setContext(CoreFileContext.fromInt(contextValue));
fileService.create(file, fileBinary) fileService.create(file, fileBinary)
.onSuccess(result -> JsonUtil.sendJson(ctx, ApiStatus.CREATED, result)) .onSuccess(result -> JsonUtil.sendJson(ctx, ApiStatus.CREATED, result))
.onFailure(err -> JsonUtil.sendJson(ctx, ApiStatus.fromException(err), null, err.getMessage())); .onFailure(err -> JsonUtil.sendJson(ctx, ApiStatus.fromException(err), null, err.getMessage()));
} catch (Exception e) { } catch (Exception e) {
Constants.LOGGER.error(e.getMessage(), e); LOGGER.error(e.getMessage(), e);
JsonUtil.sendJson(ctx, ApiStatus.INTERNAL_SERVER_ERROR, null, e.getMessage()); JsonUtil.sendJson(ctx, ApiStatus.INTERNAL_SERVER_ERROR, null, e.getMessage());
} }
} }
public void update(RoutingContext ctx) { public void update(RoutingContext ctx) {
FileEntity file = Constants.GSON.fromJson(ctx.body().asString(), FileEntity.class); FileEntity file = GSON.fromJson(ctx.body().asString(), FileEntity.class);
fileService.update(file) fileService.update(file)
.onSuccess(result -> JsonUtil.sendJson(ctx, ApiStatus.OK, result)) .onSuccess(result -> JsonUtil.sendJson(ctx, ApiStatus.OK, result))
@@ -82,14 +90,14 @@ public class FileDataHandler {
} }
public void delete(RoutingContext ctx) { public void delete(RoutingContext ctx) {
Integer fileId = Integer.parseInt(ctx.pathParam("file_id")); UUID fileId = UUID.fromString(ctx.pathParam("file_id"));
JsonObject body = ctx.body().asJsonObject(); JsonObject body = ctx.body().asJsonObject();
String filePath = body.getString("file_path"); String filePath = body.getString("file_path");
try { try {
Files.deleteIfExists(Paths.get(filePath)); Files.deleteIfExists(Paths.get(filePath));
} catch (IOException e) { } catch (IOException e) {
Constants.LOGGER.error(e.getMessage(), e); LOGGER.error(e.getMessage(), e);
JsonUtil.sendJson(ctx, ApiStatus.INTERNAL_SERVER_ERROR, null, e.getMessage()); JsonUtil.sendJson(ctx, ApiStatus.INTERNAL_SERVER_ERROR, null, e.getMessage());
return; return;
} }

View File

@@ -1,9 +1,11 @@
package net.miarma.api.backlib.core.handlers; package net.miarma.api.microservices.core.handlers;
import java.util.UUID;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject; import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.miarma.api.backlib.Constants; import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.http.ApiStatus; import net.miarma.api.backlib.http.ApiStatus;
import net.miarma.api.backlib.security.JWTManager; import net.miarma.api.backlib.security.JWTManager;
import net.miarma.api.backlib.util.JsonUtil; import net.miarma.api.backlib.util.JsonUtil;
@@ -11,6 +13,8 @@ import net.miarma.api.backlib.util.JsonUtil;
public class FileLogicHandler { public class FileLogicHandler {
private final Vertx vertx; private final Vertx vertx;
private final String CORE_EVENT_BUS = ConfigManager.getInstance()
.getStringProperty("eventbus.core.address");
public FileLogicHandler(Vertx vertx) { public FileLogicHandler(Vertx vertx) {
this.vertx = vertx; this.vertx = vertx;
@@ -25,14 +29,14 @@ public class FileLogicHandler {
} }
String token = authHeader.substring(7); String token = authHeader.substring(7);
int userId = JWTManager.getInstance().getUserId(token); UUID userId = JWTManager.getInstance().extractUserId(token);
if (userId <= 0) { if (userId == null) {
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, null, "Invalid token"); JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, null, "Invalid or expired token");
return false; return false;
} }
request.put("userId", userId); request.put("userId", userId.toString());
return true; return true;
} }
@@ -40,7 +44,7 @@ public class FileLogicHandler {
JsonObject request = new JsonObject().put("action", "getUserFiles"); JsonObject request = new JsonObject().put("action", "getUserFiles");
if (!validateAuth(ctx, request)) return; if (!validateAuth(ctx, request)) return;
vertx.eventBus().request(Constants.CORE_EVENT_BUS, request, ar -> { vertx.eventBus().request(CORE_EVENT_BUS, request, ar -> {
if (ar.succeeded()) { if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body()); JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else { } else {
@@ -50,13 +54,15 @@ public class FileLogicHandler {
} }
public void downloadFile(RoutingContext ctx) { public void downloadFile(RoutingContext ctx) {
UUID fileId = UUID.fromString(ctx.pathParam("file_id"));
JsonObject request = new JsonObject() JsonObject request = new JsonObject()
.put("action", "downloadFile") .put("action", "downloadFile")
.put("fileId", Integer.parseInt(ctx.pathParam("file_id"))); .put("fileId", fileId.toString());
if (!validateAuth(ctx, request)) return; if (!validateAuth(ctx, request)) return;
vertx.eventBus().request(Constants.CORE_EVENT_BUS, request, ar -> { vertx.eventBus().request(CORE_EVENT_BUS, request, ar -> {
if (ar.succeeded()) { if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body()); JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else { } else {

View File

@@ -1,4 +1,4 @@
package net.miarma.api.backlib.core.handlers; package net.miarma.api.microservices.core.handlers;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;

View File

@@ -0,0 +1,54 @@
package net.miarma.api.microservices.core.handlers;
import java.util.UUID;
import com.google.gson.Gson;
import io.vertx.core.Future;
import io.vertx.ext.web.RoutingContext;
import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.gson.GsonProvider;
import net.miarma.api.backlib.http.ApiStatus;
import net.miarma.api.backlib.http.QueryParams;
import net.miarma.api.backlib.util.JsonUtil;
import net.miarma.api.microservices.core.entities.UserEntity;
import net.miarma.api.microservices.core.services.UserService;
@SuppressWarnings("unused")
public class UserDataHandler {
private final Gson GSON = GsonProvider.get();
private final UserService userService;
public UserDataHandler(Pool pool) {
this.userService = new UserService(pool);
}
public void getAll(RoutingContext ctx) {
userService.getAll(QueryParams.from(ctx))
.onSuccess(users -> JsonUtil.sendJson(ctx, ApiStatus.OK, users))
.onFailure(err -> JsonUtil.sendJson(ctx, ApiStatus.fromException(err), null, err.getMessage()));
}
public void getById(RoutingContext ctx) {
Future.succeededFuture(ctx.pathParam("user_id"))
.map(UUID::fromString)
.compose(userService::getById)
.onSuccess(user -> JsonUtil.sendJson(ctx, ApiStatus.OK, user))
.onFailure(err -> JsonUtil.sendJson(ctx, ApiStatus.BAD_REQUEST, null, "Invalid UUID format"));
}
public void update(RoutingContext ctx) {
Future.succeededFuture(ctx.body().asString())
.map(body -> GSON.fromJson(body, UserEntity.class))
.compose(userService::update)
.onSuccess(result -> JsonUtil.sendJson(ctx, ApiStatus.OK, result))
.onFailure(err -> JsonUtil.sendJson(ctx, ApiStatus.BAD_REQUEST, null, "Invalid user data"));
}
public void delete(RoutingContext ctx) {
Future.succeededFuture(ctx.pathParam("user_id"))
.map(UUID::fromString)
.compose(userService::delete)
.onSuccess(v -> JsonUtil.sendJson(ctx, ApiStatus.NO_CONTENT, null))
.onFailure(err -> JsonUtil.sendJson(ctx, ApiStatus.BAD_REQUEST, null, "Invalid UUID format"));
}
}

View File

@@ -0,0 +1,150 @@
package net.miarma.api.microservices.core.handlers;
import java.util.UUID;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.http.ApiStatus;
import net.miarma.api.backlib.security.JWTManager;
import net.miarma.api.backlib.util.EventBusUtil;
import net.miarma.api.backlib.util.JsonUtil;
public class UserLogicHandler {
private final String CORE_EVENT_BUS = ConfigManager.getInstance()
.getStringProperty("eventbus.core.address");
private final Vertx vertx;
public UserLogicHandler(Vertx vertx) {
this.vertx = vertx;
}
public void getInfo(RoutingContext ctx) {
String authHeader = ctx.request().getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, null, "Unauthorized");
return;
}
String token = authHeader.substring(7);
UUID userId = JWTManager.getInstance().extractUserId(token);
if (userId == null) {
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, null, "Invalid token");
return;
}
JsonObject request = new JsonObject()
.put("action", "getInfo")
.put("userId", userId);
vertx.eventBus().request(CORE_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void exists(RoutingContext ctx) {
int userId = Integer.parseInt(ctx.pathParam("user_id"));
JsonObject request = new JsonObject()
.put("action", "userExists")
.put("userId", userId);
vertx.eventBus().request(CORE_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void getStatus(RoutingContext ctx) {
int userId = Integer.parseInt(ctx.pathParam("user_id"));
JsonObject request = new JsonObject()
.put("action", "getStatus")
.put("userId", userId);
vertx.eventBus().request(CORE_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void getRole(RoutingContext ctx) {
int userId = Integer.parseInt(ctx.pathParam("user_id"));
JsonObject request = new JsonObject()
.put("action", "getRole")
.put("userId", userId);
vertx.eventBus().request(CORE_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void getAvatar(RoutingContext ctx) {
int userId = Integer.parseInt(ctx.pathParam("user_id"));
JsonObject request = new JsonObject()
.put("action", "getAvatar")
.put("userId", userId);
vertx.eventBus().request(CORE_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void updateStatus(RoutingContext ctx) {
JsonObject body = ctx.body().asJsonObject();
JsonObject request = new JsonObject()
.put("action", "updateStatus")
.put("userId", body.getInteger("userId"))
.put("status", body.getInteger("status"));
vertx.eventBus().request(CORE_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.NO_CONTENT, null);
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
public void updateRole(RoutingContext ctx) {
JsonObject body = ctx.body().asJsonObject();
JsonObject request = new JsonObject()
.put("action", "updateRole")
.put("userId", body.getInteger("userId"))
.put("role", body.getInteger("role"));
vertx.eventBus().request(CORE_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.NO_CONTENT, null);
} else {
EventBusUtil.handleReplyError(ctx, ar.cause());
}
});
}
}

View File

@@ -4,32 +4,30 @@ import io.vertx.core.Vertx;
import io.vertx.ext.web.Router; import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.Constants.CoreUserRole; import net.miarma.api.microservices.core.enums.CoreUserGlobalRole;
import net.miarma.api.backlib.core.handlers.FileDataHandler; import net.miarma.api.microservices.core.handlers.FileDataHandler;
import net.miarma.api.backlib.core.handlers.UserDataHandler; import net.miarma.api.microservices.core.handlers.UserDataHandler;
import net.miarma.api.backlib.core.services.UserService;
import net.miarma.api.microservices.core.routing.middlewares.CoreAuthGuard; import net.miarma.api.microservices.core.routing.middlewares.CoreAuthGuard;
import net.miarma.api.microservices.core.services.UserService;
public class CoreDataRouter { public class CoreDataRouter {
public static void mount(Router router, Vertx vertx, Pool pool) { public static void mount(Router router, Vertx vertx, Pool pool) {
UserDataHandler hUserData = new UserDataHandler(pool); UserDataHandler hUserData = new UserDataHandler(pool);
FileDataHandler hFileData = new FileDataHandler(pool); FileDataHandler hFileData = new FileDataHandler(pool);
UserService userService = new UserService(pool); UserService userService = new UserService(pool);
CoreAuthGuard authGuard = new CoreAuthGuard(userService); CoreAuthGuard authGuard = new CoreAuthGuard(userService);
router.route().handler(BodyHandler.create()); router.route().handler(BodyHandler.create());
router.get(CoreEndpoints.USERS).handler(authGuard.check(CoreUserRole.ADMIN)).handler(hUserData::getAll); router.get(CoreEndpoints.USERS).handler(authGuard.check(CoreUserGlobalRole.ADMIN)).handler(hUserData::getAll);
router.get(CoreEndpoints.USER).handler(authGuard.check(CoreUserRole.ADMIN)).handler(hUserData::getById); router.get(CoreEndpoints.USER).handler(authGuard.check(CoreUserGlobalRole.ADMIN)).handler(hUserData::getById);
router.post(CoreEndpoints.USERS).handler(hUserData::create); router.put(CoreEndpoints.USER).handler(authGuard.check(CoreUserGlobalRole.ADMIN)).handler(hUserData::update);
router.put(CoreEndpoints.USER).handler(authGuard.check(CoreUserRole.ADMIN)).handler(hUserData::update); router.delete(CoreEndpoints.USER).handler(authGuard.check(CoreUserGlobalRole.ADMIN)).handler(hUserData::delete);
router.delete(CoreEndpoints.USER).handler(authGuard.check(CoreUserRole.ADMIN)).handler(hUserData::delete);
router.get(CoreEndpoints.FILES).handler(authGuard.check()).handler(hFileData::getAll); router.get(CoreEndpoints.FILES).handler(authGuard.check(CoreUserGlobalRole.ADMIN)).handler(hFileData::getAll);
router.get(CoreEndpoints.FILE).handler(authGuard.check()).handler(hFileData::getById); router.get(CoreEndpoints.FILE).handler(authGuard.check()).handler(hFileData::getById);
router.post(CoreEndpoints.FILE_UPLOAD).handler(authGuard.check()).handler(hFileData::create); router.post(CoreEndpoints.FILE_UPLOAD).handler(authGuard.check()).handler(hFileData::create);
router.put(CoreEndpoints.FILE).handler(authGuard.check()).handler(hFileData::update); router.put(CoreEndpoints.FILE).handler(authGuard.check()).handler(hFileData::update);
router.delete(CoreEndpoints.FILE).handler(authGuard.check()).handler(hFileData::delete); router.delete(CoreEndpoints.FILE).handler(authGuard.check()).handler(hFileData::delete);
}
}
} }

View File

@@ -1,39 +1,36 @@
package net.miarma.api.microservices.core.routing; package net.miarma.api.microservices.core.routing;
import net.miarma.api.backlib.Constants; import net.miarma.api.backlib.config.ConfigManager;
public class CoreEndpoints { public class CoreEndpoints {
/* private static final ConfigManager config = ConfigManager.getInstance();
* RUTAS DE LA API DE DATOS private static final String CORE = config.getApiPrefix("core");
* DE NEGOCIO DEL SSO private static final String AUTH = config.getApiPrefix("auth");
*/
// Usuarios /* API DE DATOS (UserDataHandler / FileDataHandler) */
public static final String USERS = Constants.CORE_PREFIX + "/users"; // GET, POST, PUT, DELETE public static final String USERS = CORE + "/users";
public static final String USER = Constants.CORE_PREFIX + "/users/:user_id"; // GET, PUT, DELETE public static final String USER = CORE + "/users/:user_id";
public static final String USER_STATUS = Constants.CORE_PREFIX + "/users/:user_id/status"; // GET, PUT public static final String FILES = CORE + "/files";
public static final String USER_ROLE = Constants.CORE_PREFIX + "/users/:user_id/role"; // GET, PUT public static final String FILE = CORE + "/files/:file_id";
public static final String USER_EXISTS = Constants.CORE_PREFIX + "/users/:user_id/exists"; // GET public static final String FILE_UPLOAD = CORE + "/files/upload";
public static final String USER_AVATAR = Constants.CORE_PREFIX + "/users/:user_id/avatar"; // GET, PUT
public static final String USER_INFO = Constants.CORE_PREFIX + "/users/me"; // GET
// Archivos /* API DE LÓGICA (AuthHandler / UserLogicHandler / FileLogicHandler) */
public static final String FILES = Constants.CORE_PREFIX + "/files"; // GET, POST public static final String LOGIN = AUTH + "/login";
public static final String FILE = Constants.CORE_PREFIX + "/files/:file_id"; // GET, PUT, DELETE public static final String LOGIN_VALID = AUTH + "/login/validate";
public static final String FILE_UPLOAD = Constants.CORE_PREFIX + "/files/upload"; // POST public static final String REGISTER = AUTH + "/register";
public static final String FILE_DOWNLOAD = Constants.CORE_PREFIX + "/files/:file_id/download"; // GET public static final String CHANGE_PASSWORD = AUTH + "/change-password";
public static final String USER_FILES = Constants.CORE_PREFIX + "/files/myfiles"; // GET public static final String VALIDATE_TOKEN = AUTH + "/validate-token";
public static final String REFRESH_TOKEN = AUTH + "/refresh-token";
/* public static final String USER_INFO = CORE + "/users/me";
* RUTAS DE LA API DE LOGICA public static final String USER_STATUS = CORE + "/users/:user_id/status";
* DE NEGOCIO DEL SSO public static final String USER_ROLE = CORE + "/users/:user_id/role";
*/ public static final String USER_EXISTS = CORE + "/users/:user_id/exists";
public static final String LOGIN = Constants.AUTH_PREFIX + "/login"; // POST public static final String USER_AVATAR = CORE + "/users/:user_id/avatar";
public static final String LOGIN_VALID = Constants.AUTH_PREFIX + "/login/validate"; // POST
public static final String REGISTER = Constants.AUTH_PREFIX + "/register"; // POST public static final String FILE_DOWNLOAD = CORE + "/files/:file_id/download";
public static final String CHANGE_PASSWORD = Constants.AUTH_PREFIX + "/change-password"; // POST public static final String USER_FILES = CORE + "/files/myfiles";
public static final String VALIDATE_TOKEN = Constants.AUTH_PREFIX + "/validate-token"; // POST
public static final String REFRESH_TOKEN = Constants.AUTH_PREFIX + "/refresh-token"; // POST public static final String SCREENSHOT = CORE + "/screenshot";
public static final String SCREENSHOT = Constants.CORE_PREFIX + "/screenshot"; // GET
} }

View File

@@ -4,41 +4,45 @@ import io.vertx.core.Vertx;
import io.vertx.ext.web.Router; import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.Constants.CoreUserRole; import net.miarma.api.microservices.core.enums.CoreUserGlobalRole;
import net.miarma.api.backlib.core.handlers.FileLogicHandler; import net.miarma.api.microservices.core.handlers.AuthHandler;
import net.miarma.api.backlib.core.handlers.ScreenshotHandler; import net.miarma.api.microservices.core.handlers.FileLogicHandler;
import net.miarma.api.backlib.core.handlers.UserLogicHandler; import net.miarma.api.microservices.core.handlers.ScreenshotHandler;
import net.miarma.api.backlib.core.services.UserService; import net.miarma.api.microservices.core.handlers.UserLogicHandler;
import net.miarma.api.microservices.core.routing.middlewares.CoreAuthGuard; import net.miarma.api.microservices.core.routing.middlewares.CoreAuthGuard;
import net.miarma.api.microservices.core.services.UserService;
public class CoreLogicRouter { public class CoreLogicRouter {
public static void mount(Router router, Vertx vertx, Pool pool) { public static void mount(Router router, Vertx vertx, Pool pool) {
UserLogicHandler hUserLogic = new UserLogicHandler(vertx); UserLogicHandler hUserLogic = new UserLogicHandler(vertx);
FileLogicHandler hFileLogic = new FileLogicHandler(vertx); FileLogicHandler hFileLogic = new FileLogicHandler(vertx);
ScreenshotHandler hScreenshot = new ScreenshotHandler(vertx); AuthHandler hAuth = new AuthHandler(vertx);
ScreenshotHandler hScreenshot = new ScreenshotHandler(vertx);
UserService userService = new UserService(pool); UserService userService = new UserService(pool);
CoreAuthGuard authGuard = new CoreAuthGuard(userService); CoreAuthGuard authGuard = new CoreAuthGuard(userService);
router.route().handler(BodyHandler.create()); router.route().handler(BodyHandler.create());
router.post(CoreEndpoints.LOGIN).handler(hUserLogic::login); router.post(CoreEndpoints.LOGIN).handler(hAuth::login);
router.get(CoreEndpoints.USER_INFO).handler(authGuard.check()).handler(hUserLogic::getInfo); router.post(CoreEndpoints.REGISTER).handler(hAuth::register);
router.post(CoreEndpoints.REGISTER).handler(hUserLogic::register); router.post(CoreEndpoints.LOGIN_VALID).handler(hAuth::loginValidate);
router.post(CoreEndpoints.CHANGE_PASSWORD).handler(authGuard.check()).handler(hUserLogic::changePassword);
router.post(CoreEndpoints.LOGIN_VALID).handler(hUserLogic::loginValidate);
router.get(CoreEndpoints.VALIDATE_TOKEN).handler(hUserLogic::validateToken);
router.get(CoreEndpoints.REFRESH_TOKEN).handler(hUserLogic::refreshToken);
router.get(CoreEndpoints.VALIDATE_TOKEN).handler(authGuard.check()).handler(hAuth::validateToken);
router.get(CoreEndpoints.REFRESH_TOKEN).handler(hAuth::refreshToken);
router.post(CoreEndpoints.CHANGE_PASSWORD).handler(authGuard.check()).handler(hAuth::changePassword);
router.get(CoreEndpoints.USER_INFO).handler(authGuard.check()).handler(hUserLogic::getInfo);
router.get(CoreEndpoints.USER_EXISTS).handler(authGuard.check()).handler(hUserLogic::exists); router.get(CoreEndpoints.USER_EXISTS).handler(authGuard.check()).handler(hUserLogic::exists);
router.get(CoreEndpoints.USER_STATUS).handler(authGuard.check()).handler(hUserLogic::getStatus); router.get(CoreEndpoints.USER_STATUS).handler(authGuard.check()).handler(hUserLogic::getStatus);
router.put(CoreEndpoints.USER_STATUS).handler(authGuard.check(CoreUserRole.ADMIN)).handler(hUserLogic::updateStatus); router.put(CoreEndpoints.USER_STATUS).handler(authGuard.check(CoreUserGlobalRole.ADMIN)).handler(hUserLogic::updateStatus);
router.get(CoreEndpoints.USER_ROLE).handler(authGuard.check()).handler(hUserLogic::getRole); router.get(CoreEndpoints.USER_ROLE).handler(authGuard.check()).handler(hUserLogic::getRole);
router.put(CoreEndpoints.USER_ROLE).handler(authGuard.check(CoreUserRole.ADMIN)).handler(hUserLogic::updateRole); router.put(CoreEndpoints.USER_ROLE).handler(authGuard.check(CoreUserGlobalRole.ADMIN)).handler(hUserLogic::updateRole);
router.get(CoreEndpoints.USER_AVATAR).handler(authGuard.check()).handler(hUserLogic::getAvatar); router.get(CoreEndpoints.USER_AVATAR).handler(authGuard.check()).handler(hUserLogic::getAvatar);
router.get(CoreEndpoints.FILE_DOWNLOAD).handler(authGuard.check()).handler(hFileLogic::downloadFile); router.get(CoreEndpoints.FILE_DOWNLOAD).handler(authGuard.check()).handler(hFileLogic::downloadFile);
router.get(CoreEndpoints.USER_FILES).handler(authGuard.check()).handler(hFileLogic::getUserFiles); router.get(CoreEndpoints.USER_FILES).handler(authGuard.check()).handler(hFileLogic::getUserFiles);
router.get(CoreEndpoints.SCREENSHOT).handler(hScreenshot::getScreenshot); router.get(CoreEndpoints.SCREENSHOT).handler(hScreenshot::getScreenshot);
} }
} }

View File

@@ -1,14 +1,15 @@
package net.miarma.api.microservices.core.routing.middlewares; package net.miarma.api.microservices.core.routing.middlewares;
import java.util.UUID;
import java.util.function.Consumer; import java.util.function.Consumer;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.miarma.api.backlib.Constants.CoreUserRole;
import net.miarma.api.backlib.middlewares.AbstractAuthGuard; import net.miarma.api.backlib.middlewares.AbstractAuthGuard;
import net.miarma.api.backlib.core.entities.UserEntity; import net.miarma.api.microservices.core.entities.UserEntity;
import net.miarma.api.backlib.core.services.UserService; import net.miarma.api.microservices.core.enums.CoreUserGlobalRole;
import net.miarma.api.microservices.core.services.UserService;
public class CoreAuthGuard extends AbstractAuthGuard<UserEntity, CoreUserRole> { public class CoreAuthGuard extends AbstractAuthGuard<UserEntity, CoreUserGlobalRole> {
private final UserService userService; private final UserService userService;
public CoreAuthGuard(UserService userService) { public CoreAuthGuard(UserService userService) {
@@ -16,12 +17,12 @@ public class CoreAuthGuard extends AbstractAuthGuard<UserEntity, CoreUserRole> {
} }
@Override @Override
protected CoreUserRole parseRole(String roleStr) { protected CoreUserGlobalRole parseRole(String roleStr) {
return CoreUserRole.valueOf(roleStr.toUpperCase()); return CoreUserGlobalRole.valueOf(roleStr.toUpperCase());
} }
@Override @Override
protected void getUserEntity(int userId, RoutingContext ctx, Consumer<UserEntity> callback) { protected void getUserEntity(UUID userId, RoutingContext ctx, Consumer<UserEntity> callback) {
userService.getById(userId).onComplete(ar -> { userService.getById(userId).onComplete(ar -> {
if (ar.succeeded()) callback.accept(ar.result()); if (ar.succeeded()) callback.accept(ar.result());
else callback.accept(null); else callback.accept(null);
@@ -29,8 +30,8 @@ public class CoreAuthGuard extends AbstractAuthGuard<UserEntity, CoreUserRole> {
} }
@Override @Override
protected boolean hasPermission(UserEntity user, CoreUserRole userRole) { protected boolean hasPermission(UserEntity user, CoreUserGlobalRole userRole) {
return user.getGlobal_role() == CoreUserRole.ADMIN; return user.getGlobalRole() == CoreUserGlobalRole.ADMIN;
} }
} }

View File

@@ -0,0 +1,91 @@
package net.miarma.api.microservices.core.services;
import java.util.UUID;
import io.vertx.core.Future;
import io.vertx.core.json.JsonObject;
import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.exceptions.BadRequestException;
import net.miarma.api.backlib.exceptions.ForbiddenException;
import net.miarma.api.backlib.exceptions.NotFoundException;
import net.miarma.api.backlib.security.JWTManager;
import net.miarma.api.backlib.security.PasswordHasher;
import net.miarma.api.microservices.core.dao.CredentialDAO;
import net.miarma.api.microservices.core.dao.UserDAO;
import net.miarma.api.microservices.core.entities.CredentialEntity;
import net.miarma.api.microservices.core.entities.UserEntity;
import net.miarma.api.microservices.core.enums.CoreUserGlobalRole;
import net.miarma.api.microservices.core.enums.CoreUserGlobalStatus;
public class AuthService {
private final UserDAO userDAO;
private final CredentialDAO credentialDAO;
private final int serviceId;
public AuthService(Pool pool, int serviceId) {
this.userDAO = new UserDAO(pool);
this.credentialDAO = new CredentialDAO(pool);
this.serviceId = serviceId;
}
/**
* Login: Comprueba credenciales y devuelve el token + datos del usuario
*/
public Future<JsonObject> login(String login, String plainPassword, boolean keepLoggedIn) {
return credentialDAO.getByServiceAndUsername(this.serviceId, login).compose(cred -> {
if (cred == null) {
return Future.failedFuture(new NotFoundException("User not found in this domain"));
}
if (!PasswordHasher.verify(plainPassword, cred.getPassword())) {
return Future.failedFuture(new BadRequestException("Invalid credentials"));
}
return userDAO.getById(cred.getUserId()).compose(user -> {
if (user == null) {
return Future.failedFuture(new NotFoundException("User not found"));
}
if (user.getGlobalStatus() != CoreUserGlobalStatus.ACTIVE) {
return Future.failedFuture(new ForbiddenException("User is inactive"));
}
String token = JWTManager.getInstance().generateToken(
cred.getUsername(),
user.getUserId(),
user.getGlobalRole(),
this.serviceId,
keepLoggedIn
);
JsonObject response = new JsonObject()
.put("token", token)
.put("user", JsonObject.mapFrom(user));
return Future.succeededFuture(response);
});
});
}
/**
* Register: Crea el perfil global y la credencial de servicio
*/
public Future<UserEntity> register(UserEntity profile, String username, String email, String password) {
profile.setUserId(UUID.randomUUID());
profile.setGlobalRole(CoreUserGlobalRole.USER);
profile.setGlobalStatus(CoreUserGlobalStatus.ACTIVE);
return userDAO.insert(profile).compose(savedUser -> {
CredentialEntity cred = new CredentialEntity();
cred.setCredentialId(UUID.randomUUID());
cred.setUserId(savedUser.getUserId());
cred.setServiceId(this.serviceId);
cred.setUsername(username);
cred.setEmail(email);
cred.setPassword(PasswordHasher.hash(password));
cred.setStatus(1);
return credentialDAO.insert(cred).map(savedUser);
});
}
}

View File

@@ -1,27 +1,35 @@
package net.miarma.api.backlib.core.services; package net.miarma.api.microservices.core.services;
import io.vertx.core.Future;
import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.ConfigManager;
import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.OSType;
import net.miarma.api.backlib.exceptions.NotFoundException;
import net.miarma.api.backlib.exceptions.ValidationException;
import net.miarma.api.backlib.http.QueryParams;
import net.miarma.api.backlib.core.dao.FileDAO;
import net.miarma.api.backlib.core.entities.FileEntity;
import net.miarma.api.backlib.core.validators.FileValidator;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.List; import java.util.List;
import java.util.UUID;
import org.slf4j.Logger;
import com.google.gson.Gson;
import io.vertx.core.Future;
import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.config.OSType;
import net.miarma.api.backlib.exceptions.NotFoundException;
import net.miarma.api.backlib.exceptions.ValidationException;
import net.miarma.api.backlib.gson.GsonProvider;
import net.miarma.api.backlib.http.QueryParams;
import net.miarma.api.backlib.log.LoggerProvider;
import net.miarma.api.microservices.core.dao.FileDAO;
import net.miarma.api.microservices.core.entities.FileEntity;
import net.miarma.api.microservices.core.validators.FileValidator;
public class FileService { public class FileService {
private final FileDAO fileDAO; private final FileDAO fileDAO;
private final FileValidator fileValidator; private final FileValidator fileValidator;
private final Gson GSON = GsonProvider.get();
private final Logger LOGGER = LoggerProvider.getLogger();
public FileService(Pool pool) { public FileService(Pool pool) {
this.fileDAO = new FileDAO(pool); this.fileDAO = new FileDAO(pool);
@@ -32,7 +40,7 @@ public class FileService {
return fileDAO.getAll(params); return fileDAO.getAll(params);
} }
public Future<FileEntity> getById(Integer id) { public Future<FileEntity> getById(UUID id) {
return fileDAO.getById(id).compose(file -> { return fileDAO.getById(id).compose(file -> {
if (file == null) { if (file == null) {
return Future.failedFuture(new NotFoundException("File not found with id: " + id)); return Future.failedFuture(new NotFoundException("File not found with id: " + id));
@@ -41,28 +49,28 @@ public class FileService {
}); });
} }
public Future<List<FileEntity>> getUserFiles(Integer userId) { public Future<List<FileEntity>> getUserFiles(UUID userId) {
return fileDAO.getUserFiles(userId); return fileDAO.getUserFiles(userId);
} }
public Future<FileEntity> create(FileEntity file, byte[] fileBinary) { public Future<FileEntity> create(FileEntity file, byte[] fileBinary) {
return fileValidator.validate(file, fileBinary.length).compose(validation -> { return fileValidator.validateAsync(file, fileBinary.length).compose(validation -> {
if (!validation.isValid()) { if (!validation.isValid()) {
return Future.failedFuture(new ValidationException(Constants.GSON.toJson(validation.getErrors()))); return Future.failedFuture(new ValidationException(GSON.toJson(validation.getErrors())));
} }
String dir = ConfigManager.getInstance() String dir = ConfigManager.getInstance()
.getFilesDir(file.getContext().toCtxString()); .getFilesDir(file.getContext().toCtxString());
String pathString = dir + file.getFile_name(); String pathString = dir + file.getFileName();
Path filePath = Paths.get(dir + file.getFile_name()); Path filePath = Paths.get(dir + file.getFileName());
file.setFile_path(ConfigManager.getOS() == OSType.WINDOWS ? file.setFilePath(ConfigManager.getOS() == OSType.WINDOWS ?
pathString.replace("\\", "\\\\") : pathString); pathString.replace("\\", "\\\\") : pathString);
try { try {
Files.write(filePath, fileBinary); Files.write(filePath, fileBinary);
} catch (IOException e) { } catch (IOException e) {
Constants.LOGGER.error("Error writing file to disk: ", e); LOGGER.error("Error writing file to disk: ", e);
return Future.failedFuture(e); return Future.failedFuture(e);
} }
@@ -70,14 +78,14 @@ public class FileService {
}); });
} }
public Future<FileEntity> downloadFile(Integer fileId) { public Future<FileEntity> downloadFile(UUID fileId) {
return getById(fileId); return getById(fileId);
} }
public Future<FileEntity> update(FileEntity file) { public Future<FileEntity> update(FileEntity file) {
return fileValidator.validate(file).compose(validation -> { return fileValidator.validateAsync(file).compose(validation -> {
if (!validation.isValid()) { if (!validation.isValid()) {
return Future.failedFuture(new ValidationException(Constants.GSON.toJson(validation.getErrors()))); return Future.failedFuture(new ValidationException(GSON.toJson(validation.getErrors())));
} }
return fileDAO.update(file); return fileDAO.update(file);
@@ -85,27 +93,27 @@ public class FileService {
} }
public Future<FileEntity> upsert(FileEntity file) { public Future<FileEntity> upsert(FileEntity file) {
return fileValidator.validate(file).compose(validation -> { return fileValidator.validateAsync(file).compose(validation -> {
if (!validation.isValid()) { if (!validation.isValid()) {
return Future.failedFuture(new ValidationException(Constants.GSON.toJson(validation.getErrors()))); return Future.failedFuture(new ValidationException(GSON.toJson(validation.getErrors())));
} }
return fileDAO.upsert(file, "file_id"); return fileDAO.upsert(file, "file_id");
}); });
} }
public Future<Boolean> delete(Integer fileId) { public Future<Boolean> delete(UUID fileId) {
return getById(fileId).compose(file -> { return getById(fileId).compose(file -> {
String dir = ConfigManager.getInstance() String dir = ConfigManager.getInstance()
.getFilesDir(file.getContext().toCtxString()); .getFilesDir(file.getContext().toCtxString());
String filePath = dir + file.getFile_name(); String filePath = dir + file.getFileName();
Path path = Paths.get(filePath); Path path = Paths.get(filePath);
try { try {
Files.deleteIfExists(path); Files.deleteIfExists(path);
} catch (IOException e) { } catch (IOException e) {
Constants.LOGGER.error("Error deleting file from disk: ", e); LOGGER.error("Error deleting file from disk: ", e);
return Future.failedFuture(e); return Future.failedFuture(e);
} }
@@ -118,7 +126,7 @@ public class FileService {
}); });
} }
public Future<Boolean> exists(Integer fileId) { public Future<Boolean> exists(UUID fileId) {
return fileDAO.exists(fileId); return fileDAO.exists(fileId);
} }
} }

View File

@@ -0,0 +1,55 @@
package net.miarma.api.microservices.core.services;
import java.util.List;
import java.util.UUID;
import io.vertx.core.Future;
import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.exceptions.NotFoundException;
import net.miarma.api.backlib.http.QueryParams;
import net.miarma.api.microservices.core.dao.UserDAO;
import net.miarma.api.microservices.core.entities.UserEntity;
import net.miarma.api.microservices.core.enums.CoreUserGlobalRole;
import net.miarma.api.microservices.core.enums.CoreUserGlobalStatus;
public class UserService {
private final UserDAO userDAO;
public UserService(Pool pool) {
this.userDAO = new UserDAO(pool);
}
public Future<List<UserEntity>> getAll(QueryParams params) {
return userDAO.getAll(params);
}
public Future<UserEntity> getById(UUID id) {
return userDAO.getById(id).compose(user -> {
if (user == null) return Future.failedFuture(new NotFoundException("User not found"));
return Future.succeededFuture(user);
});
}
public Future<UserEntity> update(UserEntity user) {
return userDAO.update(user);
}
public Future<UserEntity> changeRole(UUID userId, CoreUserGlobalRole newRole) {
return getById(userId).compose(user -> {
user.setGlobalRole(newRole);
return userDAO.update(user);
});
}
public Future<UserEntity> changeStatus(UUID userId, CoreUserGlobalStatus newStatus) {
return getById(userId).compose(user -> {
user.setGlobalStatus(newStatus);
return userDAO.update(user);
});
}
public Future<Boolean> delete(UUID id) {
return userDAO.delete(id);
}
}

View File

@@ -0,0 +1,63 @@
package net.miarma.api.microservices.core.validators;
import io.vertx.core.Future;
import net.miarma.api.backlib.validation.ValidationResult;
import net.miarma.api.microservices.core.entities.FileEntity;
public class FileValidator {
private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
public Future<ValidationResult> validateAsync(FileEntity file, long size) {
ValidationResult result = validate(file);
if (size <= 0) {
result.addError("size", "File size must be greater than 0 bytes");
}
if (size > MAX_FILE_SIZE) {
result.addError("size", "File size exceeds the 10 MB limit");
}
return Future.succeededFuture(result);
}
public Future<ValidationResult> validateAsync(FileEntity file) {
ValidationResult result = validate(file);
return Future.succeededFuture(result);
}
private ValidationResult validate(FileEntity file) {
ValidationResult result = new ValidationResult();
if (file == null) {
return result.addError("file", "File object cannot be null");
}
if (file.getFileName() == null || file.getFileName().isBlank()) {
result.addError("file_name", "File name is required");
} else if (file.getFileName().length() > 255) {
result.addError("file_name", "File name is too long (max 255 characters)");
}
if (file.getFilePath() == null || file.getFilePath().isBlank()) {
result.addError("file_path", "File path is required");
} else if (file.getFilePath().length() > 255) {
result.addError("file_path", "File path is too long (max 255 characters)");
}
if (file.getMimeType() == null || file.getMimeType().isBlank()) {
result.addError("mime_type", "MIME type is required");
}
if (file.getContext() == null) {
result.addError("context", "File context is required");
}
if (file.getUploadedBy() == null) {
result.addError("uploaded_by", "Uploader User ID is required");
}
return result;
}
}

View File

@@ -0,0 +1,45 @@
package net.miarma.api.microservices.core.validators;
import io.vertx.core.Future;
import net.miarma.api.backlib.validation.ValidationResult;
import net.miarma.api.microservices.core.entities.CredentialEntity;
import net.miarma.api.microservices.core.entities.UserEntity;
public class UserValidator {
public Future<ValidationResult> validateAsync(UserEntity user, CredentialEntity creds) {
ValidationResult result = new ValidationResult();
if (user == null) {
return Future.succeededFuture(result.addError("user", "User profile cannot be null"));
}
if (user.getDisplayName() == null || user.getDisplayName().isBlank()) {
result.addError("display_name", "Display name is required");
}
if (creds == null) {
return Future.succeededFuture(result.addError("credentials", "Credentials are required"));
}
if (creds.getUsername() == null || creds.getUsername().isBlank()) {
result.addError("username", "Username is required");
}
if (creds.getEmail() != null && !creds.getEmail().isBlank()) {
if (!creds.getEmail().matches("^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$")) {
result.addError("email", "Invalid email format");
}
} else {
result.addError("email", "Email is required");
}
if (creds.getPassword() == null || creds.getPassword().isBlank()) {
result.addError("password", "Password is required");
} else if (creds.getPassword().length() < 6) {
result.addError("password", "Password must be at least 6 characters long");
}
return Future.succeededFuture(result);
}
}

View File

@@ -1,196 +1,123 @@
package net.miarma.api.microservices.core.verticles; package net.miarma.api.microservices.core.verticles;
import java.util.HashMap; import java.util.UUID;
import java.util.Map;
import io.vertx.core.AbstractVerticle; import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise; import io.vertx.core.Promise;
import io.vertx.core.json.JsonObject; import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router; import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.ConfigManager; import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.Constants.CoreUserGlobalStatus;
import net.miarma.api.backlib.Constants.CoreUserRole;
import net.miarma.api.backlib.core.entities.UserEntity;
import net.miarma.api.backlib.core.services.FileService;
import net.miarma.api.backlib.core.services.UserService;
import net.miarma.api.backlib.db.DatabaseProvider; import net.miarma.api.backlib.db.DatabaseProvider;
import net.miarma.api.backlib.util.EventBusUtil; import net.miarma.api.backlib.util.EventBusUtil;
import net.miarma.api.backlib.util.RouterUtil; import net.miarma.api.backlib.util.RouterUtil;
import net.miarma.api.microservices.core.entities.UserEntity;
import net.miarma.api.microservices.core.enums.CoreUserGlobalRole;
import net.miarma.api.microservices.core.enums.CoreUserGlobalStatus;
import net.miarma.api.microservices.core.routing.CoreDataRouter; import net.miarma.api.microservices.core.routing.CoreDataRouter;
import net.miarma.api.microservices.core.routing.CoreEndpoints;
import net.miarma.api.microservices.core.services.AuthService;
import net.miarma.api.microservices.core.services.FileService;
import net.miarma.api.microservices.core.services.UserService;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class CoreDataVerticle extends AbstractVerticle { public class CoreDataVerticle extends AbstractVerticle {
private ConfigManager configManager; private ConfigManager configManager;
private UserService userService; private UserService userService;
private FileService fileService; private FileService fileService;
private AuthService authService;
@Override @Override
public void start(Promise<Void> startPromise) { public void start(Promise<Void> startPromise) {
configManager = ConfigManager.getInstance(); configManager = ConfigManager.getInstance();
Pool pool = DatabaseProvider.createPool(vertx, configManager); Pool pool = DatabaseProvider.createPool(vertx, configManager);
userService = new UserService(pool); userService = new UserService(pool);
fileService = new FileService(pool); fileService = new FileService(pool);
authService = new AuthService(pool, 0);
Router router = Router.router(vertx); Router router = Router.router(vertx);
RouterUtil.attachLogger(router); RouterUtil.attachLogger(router);
CoreDataRouter.mount(router, vertx, pool); CoreDataRouter.mount(router, vertx, pool);
registerLogicVerticleConsumer(); registerLogicVerticleConsumer();
vertx.createHttpServer() vertx.createHttpServer()
.requestHandler(router) .requestHandler(router)
.listen(configManager.getIntProperty("sso.data.port"), res -> { .listen(configManager.getIntProperty("core.data.port"), res -> {
if (res.succeeded()) startPromise.complete(); if (res.succeeded()) startPromise.complete();
else startPromise.fail(res.cause()); else startPromise.fail(res.cause());
}); });
} }
private void registerLogicVerticleConsumer() { private void registerLogicVerticleConsumer() {
vertx.eventBus().consumer(Constants.AUTH_EVENT_BUS, message -> { String authAddress = configManager.getStringProperty("eventbus.auth.address");
vertx.eventBus().consumer(authAddress, message -> {
JsonObject body = (JsonObject) message.body(); JsonObject body = (JsonObject) message.body();
String action = body.getString("action"); String action = body.getString("action");
switch (action) { try {
case "login" -> { switch (action) {
String email = body.getString("email"); case "login" -> {
String userName = body.getString("userName"); authService.login(
String password = body.getString("password"); body.getString("login"),
boolean keepLoggedIn = body.getBoolean("keepLoggedIn", false); body.getString("password"),
body.getBoolean("keepLoggedIn", false)
).onSuccess(message::reply).onFailure(EventBusUtil.fail(message));
}
userService.login(email != null ? email : userName, password, keepLoggedIn) case "register" -> {
.onSuccess(message::reply) UserEntity profile = new UserEntity();
.onFailure(EventBusUtil.fail(message)); profile.setDisplayName(body.getString("displayName"));
authService.register(
profile,
body.getString("username"),
body.getString("email"),
body.getString("password")
).onSuccess(user -> message.reply(JsonObject.mapFrom(user)))
.onFailure(EventBusUtil.fail(message));
}
case "getInfo", "getById", "getUserById" -> {
userService.getById(UUID.fromString(body.getString("userId")))
.onSuccess(user -> message.reply(JsonObject.mapFrom(user)))
.onFailure(EventBusUtil.fail(message));
}
case "updateStatus" -> {
userService.changeStatus(
UUID.fromString(body.getString("userId")),
CoreUserGlobalStatus.fromInt(body.getInteger("status"))
).onSuccess(res -> message.reply(new JsonObject().put("message", "Status updated")))
.onFailure(EventBusUtil.fail(message));
}
case "updateRole" -> {
userService.changeRole(
UUID.fromString(body.getString("userId")),
CoreUserGlobalRole.fromInt(body.getInteger("role"))
).onSuccess(res -> message.reply(new JsonObject().put("message", "Role updated")))
.onFailure(EventBusUtil.fail(message));
}
case "getUserFiles" -> {
fileService.getUserFiles(UUID.fromString(body.getString("userId")))
.onSuccess(message::reply)
.onFailure(EventBusUtil.fail(message));
}
case "downloadFile" -> {
fileService.getById(UUID.fromString(body.getString("fileId")))
.onSuccess(message::reply)
.onFailure(EventBusUtil.fail(message));
}
default -> message.fail(404, "Action not found: " + action);
} }
} catch (Exception e) {
case "register" -> { message.fail(400, "Invalid data format or UUID: " + e.getMessage());
UserEntity user = new UserEntity();
user.setUser_name(body.getString("userName"));
user.setEmail(body.getString("email"));
user.setDisplay_name(body.getString("displayName"));
user.setPassword(body.getString("password"));
userService.register(user)
.onSuccess(message::reply)
.onFailure(EventBusUtil.fail(message));
}
case "changePassword" -> {
Integer userId = body.getInteger("userId");
String newPassword = body.getString("newPassword");
userService.changePassword(userId, newPassword)
.onSuccess(user -> {
String userJson = Constants.GSON.toJson(user);
message.reply(new JsonObject(userJson));
})
.onFailure(EventBusUtil.fail(message));
}
case "validateToken" -> {
String token = body.getString("token");
userService.validateToken(token)
.onSuccess(message::reply)
.onFailure(EventBusUtil.fail(message));
}
case "getInfo", "getById" -> {
Integer userId = body.getInteger("userId");
userService.getById(userId)
.onSuccess(message::reply)
.onFailure(EventBusUtil.fail(message));
}
case "userExists" -> {
Integer userId = body.getInteger("userId");
userService.getById(userId)
.onSuccess(user -> {
Map<String, Object> result = new HashMap<>();
result.put("user_id", userId);
result.put("exists", user != null);
message.reply(result);
})
.onFailure(EventBusUtil.fail(message));
}
case "getByEmail" -> userService.getByEmail(body.getString("email"))
.onSuccess(message::reply)
.onFailure(EventBusUtil.fail(message));
case "getByUserName" -> userService.getByUserName(body.getString("userName"))
.onSuccess(message::reply)
.onFailure(EventBusUtil.fail(message));
case "getStatus" -> userService.getById(body.getInteger("userId"))
.onSuccess(user -> {
Map<String, Object> result = new HashMap<>();
result.put("user_id", user.getUser_id());
result.put("status", user.getGlobal_status());
message.reply(result);
})
.onFailure(EventBusUtil.fail(message));
case "getRole" -> userService.getById(body.getInteger("userId"))
.onSuccess(user -> {
Map<String, Object> result = new HashMap<>();
result.put("user_id", user.getUser_id());
result.put("role", user.getGlobal_role());
message.reply(result);
})
.onFailure(EventBusUtil.fail(message));
case "getAvatar" -> userService.getById(body.getInteger("userId"))
.onSuccess(user -> {
Map<String, Object> result = new HashMap<>();
result.put("user_id", user.getUser_id());
result.put("avatar", user.getAvatar());
message.reply(result);
})
.onFailure(EventBusUtil.fail(message));
case "updateStatus" -> userService.updateStatus(
body.getInteger("userId"),
CoreUserGlobalStatus.fromInt(body.getInteger("status")))
.onSuccess(res -> message.reply("Status updated successfully"))
.onFailure(EventBusUtil.fail(message));
case "updateRole" -> userService.updateRole(
body.getInteger("userId"),
CoreUserRole.fromInt(body.getInteger("role")))
.onSuccess(res -> message.reply("Role updated successfully"))
.onFailure(EventBusUtil.fail(message));
case "getUserFiles" -> fileService.getUserFiles(body.getInteger("userId"))
.onSuccess(message::reply)
.onFailure(EventBusUtil.fail(message));
case "downloadFile" -> fileService.downloadFile(body.getInteger("fileId"))
.onSuccess(message::reply)
.onFailure(EventBusUtil.fail(message));
case "getUserById" -> userService.getById(body.getInteger("userId"))
.onSuccess(user -> {
String userJson = Constants.GSON.toJson(user);
message.reply(new JsonObject(userJson));
})
.onFailure(EventBusUtil.fail(message));
case "loginValidate" -> {
Integer userId = body.getInteger("userId");
String password = body.getString("password");
userService.loginValidate(userId, password)
.onSuccess(user -> {
String userJson = Constants.GSON.toJson(user);
message.reply(new JsonObject(userJson));
})
.onFailure(EventBusUtil.fail(message));
}
default -> EventBusUtil.fail(message);
} }
}); });
} }

View File

@@ -5,7 +5,7 @@ import io.vertx.core.Promise;
import io.vertx.ext.web.Router; import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.ConfigManager; import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.db.DatabaseProvider; import net.miarma.api.backlib.db.DatabaseProvider;
import net.miarma.api.backlib.util.RouterUtil; import net.miarma.api.backlib.util.RouterUtil;
import net.miarma.api.microservices.core.routing.CoreLogicRouter; import net.miarma.api.microservices.core.routing.CoreLogicRouter;
@@ -24,7 +24,7 @@ public class CoreLogicVerticle extends AbstractVerticle {
vertx.createHttpServer() vertx.createHttpServer()
.requestHandler(router) .requestHandler(router)
.listen(configManager.getIntProperty("sso.logic.port"), res -> { .listen(configManager.getIntProperty("core.logic.port"), res -> {
if (res.succeeded()) startPromise.complete(); if (res.succeeded()) startPromise.complete();
else startPromise.fail(res.cause()); else startPromise.fail(res.cause());
}); });

View File

@@ -1,16 +1,19 @@
package net.miarma.api.microservices.core.verticles; package net.miarma.api.microservices.core.verticles;
import org.slf4j.Logger;
import io.vertx.core.AbstractVerticle; import io.vertx.core.AbstractVerticle;
import io.vertx.core.DeploymentOptions; import io.vertx.core.DeploymentOptions;
import io.vertx.core.Promise; import io.vertx.core.Promise;
import io.vertx.core.ThreadingModel; import io.vertx.core.ThreadingModel;
import net.miarma.api.backlib.ConfigManager; import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.Constants; import net.miarma.api.backlib.log.LogAccumulator;
import net.miarma.api.backlib.LogAccumulator; import net.miarma.api.backlib.log.LoggerProvider;
import net.miarma.api.backlib.util.DeploymentUtil; import net.miarma.api.backlib.util.DeploymentUtil;
public class CoreMainVerticle extends AbstractVerticle { public class CoreMainVerticle extends AbstractVerticle {
private final Logger LOGGER = LoggerProvider.getLogger();
private ConfigManager configManager; private ConfigManager configManager;
@Override @Override
@@ -20,7 +23,7 @@ public class CoreMainVerticle extends AbstractVerticle {
deployVerticles(); deployVerticles();
startPromise.complete(); startPromise.complete();
} catch (Exception e) { } catch (Exception e) {
Constants.LOGGER.error(DeploymentUtil.failMessage(CoreMainVerticle.class, e)); LOGGER.error(DeploymentUtil.failMessage(CoreMainVerticle.class, e));
startPromise.fail(e); startPromise.fail(e);
} }
} }
@@ -35,7 +38,7 @@ public class CoreMainVerticle extends AbstractVerticle {
DeploymentUtil.successMessage(CoreDataVerticle.class), DeploymentUtil.successMessage(CoreDataVerticle.class),
DeploymentUtil.apiUrlMessage( DeploymentUtil.apiUrlMessage(
configManager.getHost(), configManager.getHost(),
configManager.getIntProperty("sso.data.port") configManager.getIntProperty("core.data.port")
) )
); );
LogAccumulator.add(message); LogAccumulator.add(message);
@@ -50,7 +53,7 @@ public class CoreMainVerticle extends AbstractVerticle {
DeploymentUtil.successMessage(CoreLogicVerticle.class), DeploymentUtil.successMessage(CoreLogicVerticle.class),
DeploymentUtil.apiUrlMessage( DeploymentUtil.apiUrlMessage(
configManager.getHost(), configManager.getHost(),
configManager.getIntProperty("sso.logic.port") configManager.getIntProperty("core.logic.port")
) )
); );
LogAccumulator.add(message); LogAccumulator.add(message);

View File

@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>net.miarma.api</groupId> <groupId>net.miarma.api</groupId>
<artifactId>huertos</artifactId> <artifactId>huertos</artifactId>
<version>1.2.1</version> <version>2.0.0</version>
<properties> <properties>
<maven.compiler.source>23</maven.compiler.source> <maven.compiler.source>23</maven.compiler.source>
@@ -20,7 +20,7 @@
<dependency> <dependency>
<groupId>net.miarma.api</groupId> <groupId>net.miarma.api</groupId>
<artifactId>backlib</artifactId> <artifactId>backlib</artifactId>
<version>1.2.1</version> <version>2.0.0</version>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@@ -0,0 +1,27 @@
package net.miarma.api.microservices.huertos.enums;
import net.miarma.api.backlib.interfaces.IValuableEnum;
public enum HuertosAnnouncementPriority implements IValuableEnum {
LOW(0),
MEDIUM(1),
HIGH(2);
private final int value;
HuertosAnnouncementPriority(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static HuertosAnnouncementPriority fromInt(int i) {
for (HuertosAnnouncementPriority priority : values()) {
if (priority.value == i) return priority;
}
throw new IllegalArgumentException("Invalid HuertosAnnouncementPriority value: " + i);
}
}

View File

@@ -0,0 +1,26 @@
package net.miarma.api.microservices.huertos.enums;
import net.miarma.api.backlib.interfaces.IValuableEnum;
public enum HuertosPaymentFrequency implements IValuableEnum {
BIYEARLY(0),
YEARLY(1);
private final int value;
HuertosPaymentFrequency(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static HuertosPaymentFrequency fromInt(int i) {
for (HuertosPaymentFrequency frequency : values()) {
if (frequency.value == i) return frequency;
}
throw new IllegalArgumentException("Invalid HuertoPaymentFrequency value: " + i);
}
}

View File

@@ -0,0 +1,26 @@
package net.miarma.api.microservices.huertos.enums;
import net.miarma.api.backlib.interfaces.IValuableEnum;
public enum HuertosPaymentType implements IValuableEnum {
BANK(0),
CASH(1);
private final int value;
HuertosPaymentType(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static HuertosPaymentType fromInt(int i) {
for (HuertosPaymentType type : values()) {
if (type.value == i) return type;
}
throw new IllegalArgumentException("Invalid HuertoPaymentType value: " + i);
}
}

View File

@@ -0,0 +1,27 @@
package net.miarma.api.microservices.huertos.enums;
import net.miarma.api.backlib.interfaces.IValuableEnum;
public enum HuertosRequestStatus implements IValuableEnum {
PENDING(0),
APPROVED(1),
REJECTED(2);
private final int value;
HuertosRequestStatus(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static HuertosRequestStatus fromInt(int i) {
for (HuertosRequestStatus status : values()) {
if (status.value == i) return status;
}
throw new IllegalArgumentException("Invalid HuertoRequestStatus value: " + i);
}
}

View File

@@ -0,0 +1,30 @@
package net.miarma.api.microservices.huertos.enums;
import net.miarma.api.backlib.interfaces.IValuableEnum;
public enum HuertosRequestType implements IValuableEnum {
REGISTER(0),
UNREGISTER(1),
ADD_COLLABORATOR(2),
REMOVE_COLLABORATOR(3),
ADD_GREENHOUSE(4),
REMOVE_GREENHOUSE(5);
private final int value;
HuertosRequestType(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static HuertosRequestType fromInt(int i) {
for (HuertosRequestType type : values()) {
if (type.value == i) return type;
}
throw new IllegalArgumentException("Invalid HuertoRequestType value: " + i);
}
}

View File

@@ -0,0 +1,27 @@
package net.miarma.api.microservices.huertos.enums;
import net.miarma.api.backlib.interfaces.IUserRole;
public enum HuertosUserRole implements IUserRole {
USER(0),
ADMIN(1),
DEV(2);
private final int value;
HuertosUserRole(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static HuertosUserRole fromInt(int i) {
for (HuertosUserRole role : values()) {
if (role.value == i) return role;
}
throw new IllegalArgumentException("Invalid HuertosUserRole value: " + i);
}
}

View File

@@ -0,0 +1,30 @@
package net.miarma.api.microservices.huertos.enums;
import net.miarma.api.backlib.interfaces.IValuableEnum;
public enum HuertosUserType implements IValuableEnum {
WAIT_LIST(0),
MEMBER(1),
WITH_GREENHOUSE(2),
COLLABORATOR(3),
DEVELOPER(5),
SUBSIDY(4);
private final int value;
HuertosUserType(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static HuertosUserType fromInt(int i) {
for (HuertosUserType type : values()) {
if (type.value == i) return type;
}
throw new IllegalArgumentException("Invalid HuertosUserType value: " + i);
}
}

View File

@@ -10,8 +10,8 @@ import io.vertx.core.WorkerExecutor;
import io.vertx.core.json.Json; import io.vertx.core.json.Json;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.ConfigManager;
import net.miarma.api.backlib.Constants; import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.http.ApiStatus; import net.miarma.api.backlib.http.ApiStatus;
import net.miarma.api.backlib.security.JWTManager; import net.miarma.api.backlib.security.JWTManager;
import net.miarma.api.microservices.huertos.entities.MemberEntity; import net.miarma.api.microservices.huertos.entities.MemberEntity;

View File

@@ -14,7 +14,7 @@ import jakarta.mail.Session;
import jakarta.mail.Store; import jakarta.mail.Store;
import jakarta.mail.internet.MimeMessage; import jakarta.mail.internet.MimeMessage;
import jakarta.mail.internet.MimeUtility; import jakarta.mail.internet.MimeUtility;
import net.miarma.api.backlib.ConfigManager; import net.miarma.api.backlib.config.ConfigManager;
public class ImapReader { public class ImapReader {

View File

@@ -23,7 +23,7 @@ import jakarta.mail.Session;
import jakarta.mail.Store; import jakarta.mail.Store;
import jakarta.mail.internet.InternetAddress; import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage; import jakarta.mail.internet.MimeMessage;
import net.miarma.api.backlib.ConfigManager; import net.miarma.api.backlib.config.ConfigManager;
public class MailManager { public class MailManager {
private final Vertx vertx; private final Vertx vertx;
@@ -31,6 +31,9 @@ public class MailManager {
private final int smtpPort; private final int smtpPort;
private final Map<String, MailClient> clientCache = new ConcurrentHashMap<>(); private final Map<String, MailClient> clientCache = new ConcurrentHashMap<>();
public static final List<String> HUERTOS_ALLOWED_MAIL_FOLDERS =
List.of("INBOX", "Drafts", "Sent", "Spam", "Trash");
public MailManager(Vertx vertx) { public MailManager(Vertx vertx) {
this.vertx = vertx; this.vertx = vertx;
this.smtpHost = ConfigManager.getInstance().getStringProperty("smtp.server"); this.smtpHost = ConfigManager.getInstance().getStringProperty("smtp.server");

View File

@@ -9,10 +9,10 @@ import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router; import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.ConfigManager;
import net.miarma.api.backlib.Constants; import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.Constants.HuertosUserStatus; import net.miarma.api.backlib.Constants.HuertosUserStatus;
import net.miarma.api.backlib.Constants.HuertosUserType; import net.miarma.api.backlib.Constants.HuertosUserType;
import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.db.DatabaseProvider; import net.miarma.api.backlib.db.DatabaseProvider;
import net.miarma.api.backlib.util.EventBusUtil; import net.miarma.api.backlib.util.EventBusUtil;
import net.miarma.api.backlib.util.NameCensorer; import net.miarma.api.backlib.util.NameCensorer;

View File

@@ -5,7 +5,7 @@ import io.vertx.core.Promise;
import io.vertx.ext.web.Router; import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.ConfigManager; import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.db.DatabaseProvider; import net.miarma.api.backlib.db.DatabaseProvider;
import net.miarma.api.backlib.util.RouterUtil; import net.miarma.api.backlib.util.RouterUtil;
import net.miarma.api.microservices.huertos.routing.HuertosLogicRouter; import net.miarma.api.microservices.huertos.routing.HuertosLogicRouter;

View File

@@ -2,9 +2,9 @@ package net.miarma.api.microservices.huertos.verticles;
import io.vertx.core.AbstractVerticle; import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise; import io.vertx.core.Promise;
import net.miarma.api.backlib.ConfigManager;
import net.miarma.api.backlib.Constants; import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.LogAccumulator; import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.log.LogAccumulator;
import net.miarma.api.backlib.util.DeploymentUtil; import net.miarma.api.backlib.util.DeploymentUtil;
public class HuertosMainVerticle extends AbstractVerticle { public class HuertosMainVerticle extends AbstractVerticle {

View File

@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>net.miarma.api</groupId> <groupId>net.miarma.api</groupId>
<artifactId>huertosdecine</artifactId> <artifactId>huertosdecine</artifactId>
<version>1.2.1</version> <version>2.0.0</version>
<properties> <properties>
<maven.compiler.source>23</maven.compiler.source> <maven.compiler.source>23</maven.compiler.source>
@@ -20,7 +20,7 @@
<dependency> <dependency>
<groupId>net.miarma.api</groupId> <groupId>net.miarma.api</groupId>
<artifactId>backlib</artifactId> <artifactId>backlib</artifactId>
<version>1.2.1</version> <version>2.0.0</version>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@@ -0,0 +1,26 @@
package net.miarma.api.microservices.huertosdecine.enums;
import net.miarma.api.backlib.interfaces.IUserRole;
public enum CineUserRole implements IUserRole {
USER(0),
ADMIN(1);
private final int value;
CineUserRole(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static CineUserRole fromInt(int i) {
for (CineUserRole role : values()) {
if (role.value == i) return role;
}
throw new IllegalArgumentException("Invalid CineUserRole value: " + i);
}
}

View File

@@ -2,8 +2,8 @@ package net.miarma.api.microservices.huertosdecine.handlers;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.ConfigManager;
import net.miarma.api.backlib.Constants; import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.http.ApiStatus; import net.miarma.api.backlib.http.ApiStatus;
import net.miarma.api.backlib.http.QueryParams; import net.miarma.api.backlib.http.QueryParams;
import net.miarma.api.microservices.huertosdecine.entities.MovieEntity; import net.miarma.api.microservices.huertosdecine.entities.MovieEntity;

View File

@@ -9,8 +9,8 @@ import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router; import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.ConfigManager;
import net.miarma.api.backlib.Constants; import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.db.DatabaseProvider; import net.miarma.api.backlib.db.DatabaseProvider;
import net.miarma.api.backlib.util.EventBusUtil; import net.miarma.api.backlib.util.EventBusUtil;
import net.miarma.api.backlib.util.RouterUtil; import net.miarma.api.backlib.util.RouterUtil;

View File

@@ -5,7 +5,7 @@ import io.vertx.core.Promise;
import io.vertx.ext.web.Router; import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.ConfigManager; import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.db.DatabaseProvider; import net.miarma.api.backlib.db.DatabaseProvider;
import net.miarma.api.backlib.util.RouterUtil; import net.miarma.api.backlib.util.RouterUtil;
import net.miarma.api.microservices.huertosdecine.routing.CineLogicRouter; import net.miarma.api.microservices.huertosdecine.routing.CineLogicRouter;

View File

@@ -2,9 +2,9 @@ package net.miarma.api.microservices.huertosdecine.verticles;
import io.vertx.core.AbstractVerticle; import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise; import io.vertx.core.Promise;
import net.miarma.api.backlib.ConfigManager;
import net.miarma.api.backlib.Constants; import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.LogAccumulator; import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.log.LogAccumulator;
import net.miarma.api.backlib.util.DeploymentUtil; import net.miarma.api.backlib.util.DeploymentUtil;
public class CineMainVerticle extends AbstractVerticle { public class CineMainVerticle extends AbstractVerticle {

View File

@@ -1,8 +1,8 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>net.miarma.api</groupId> <groupId>net.miarma.api</groupId>
<artifactId>miarmacraft</artifactId> <artifactId>minecraft</artifactId>
<version>1.2.1</version> <version>2.0.0</version>
<properties> <properties>
<maven.compiler.source>23</maven.compiler.source> <maven.compiler.source>23</maven.compiler.source>
@@ -20,7 +20,7 @@
<dependency> <dependency>
<groupId>net.miarma.api</groupId> <groupId>net.miarma.api</groupId>
<artifactId>backlib</artifactId> <artifactId>backlib</artifactId>
<version>1.2.1</version> <version>2.0.0</version>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@@ -1,5 +0,0 @@
package net.miarma.api.microservices.miarmacraft.handlers;
public class ModLogicHandler {
}

View File

@@ -1,4 +1,4 @@
package net.miarma.api.microservices.miarmacraft.dao; package net.miarma.api.microservices.minecraft.dao;
import io.vertx.core.Future; import io.vertx.core.Future;
import io.vertx.core.Promise; import io.vertx.core.Promise;
@@ -8,7 +8,7 @@ import net.miarma.api.backlib.db.DatabaseManager;
import net.miarma.api.backlib.db.QueryBuilder; import net.miarma.api.backlib.db.QueryBuilder;
import net.miarma.api.backlib.http.QueryFilters; import net.miarma.api.backlib.http.QueryFilters;
import net.miarma.api.backlib.http.QueryParams; import net.miarma.api.backlib.http.QueryParams;
import net.miarma.api.microservices.miarmacraft.entities.ModEntity; import net.miarma.api.microservices.minecraft.entities.ModEntity;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;

View File

@@ -1,4 +1,4 @@
package net.miarma.api.microservices.miarmacraft.dao; package net.miarma.api.microservices.minecraft.dao;
import io.vertx.core.Future; import io.vertx.core.Future;
import io.vertx.core.Promise; import io.vertx.core.Promise;
@@ -8,7 +8,7 @@ import net.miarma.api.backlib.db.DatabaseManager;
import net.miarma.api.backlib.db.QueryBuilder; import net.miarma.api.backlib.db.QueryBuilder;
import net.miarma.api.backlib.http.QueryFilters; import net.miarma.api.backlib.http.QueryFilters;
import net.miarma.api.backlib.http.QueryParams; import net.miarma.api.backlib.http.QueryParams;
import net.miarma.api.microservices.miarmacraft.entities.PlayerEntity; import net.miarma.api.microservices.minecraft.entities.PlayerEntity;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;

View File

@@ -1,4 +1,4 @@
package net.miarma.api.microservices.miarmacraft.dao; package net.miarma.api.microservices.minecraft.dao;
import io.vertx.core.Future; import io.vertx.core.Future;
import io.vertx.core.Promise; import io.vertx.core.Promise;
@@ -8,7 +8,7 @@ import net.miarma.api.backlib.db.DatabaseManager;
import net.miarma.api.backlib.db.QueryBuilder; import net.miarma.api.backlib.db.QueryBuilder;
import net.miarma.api.backlib.http.QueryFilters; import net.miarma.api.backlib.http.QueryFilters;
import net.miarma.api.backlib.http.QueryParams; import net.miarma.api.backlib.http.QueryParams;
import net.miarma.api.microservices.miarmacraft.entities.UserMetadataEntity; import net.miarma.api.microservices.minecraft.entities.UserMetadataEntity;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;

View File

@@ -1,4 +1,4 @@
package net.miarma.api.microservices.miarmacraft.entities; package net.miarma.api.microservices.minecraft.entities;
import io.vertx.sqlclient.Row; import io.vertx.sqlclient.Row;
import net.miarma.api.backlib.Constants.MMCModStatus; import net.miarma.api.backlib.Constants.MMCModStatus;

View File

@@ -1,4 +1,4 @@
package net.miarma.api.microservices.miarmacraft.entities; package net.miarma.api.microservices.minecraft.entities;
import java.time.LocalDateTime; import java.time.LocalDateTime;

View File

@@ -1,4 +1,4 @@
package net.miarma.api.microservices.miarmacraft.entities; package net.miarma.api.microservices.minecraft.entities;
import io.vertx.sqlclient.Row; import io.vertx.sqlclient.Row;
import net.miarma.api.backlib.Constants.MMCUserRole; import net.miarma.api.backlib.Constants.MMCUserRole;

View File

@@ -0,0 +1,26 @@
package net.miarma.api.microservices.minecraft.enums;
import net.miarma.api.backlib.interfaces.IValuableEnum;
public enum MinecraftModStatus implements IValuableEnum {
ACTIVE(0),
INACTIVE(1);
private final int value;
MinecraftModStatus(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static MinecraftModStatus fromInt(int i) {
for (MinecraftModStatus status : values()) {
if (status.value == i) return status;
}
throw new IllegalArgumentException("Invalid MinecraftModStatus value: " + i);
}
}

View File

@@ -0,0 +1,27 @@
package net.miarma.api.microservices.minecraft.enums;
import net.miarma.api.backlib.interfaces.IUserRole;
import net.miarma.api.backlib.interfaces.IValuableEnum;
public enum MinecraftUserRole implements IUserRole, IValuableEnum {
PLAYER(0),
ADMIN(1);
private final int value;
MinecraftUserRole(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static MinecraftUserRole fromInt(int i) {
for (MinecraftUserRole role : values()) {
if (role.value == i) return role;
}
throw new IllegalArgumentException("Invalid MinecraftUserRole value: " + i);
}
}

View File

@@ -1,13 +1,13 @@
package net.miarma.api.microservices.miarmacraft.handlers; package net.miarma.api.microservices.minecraft.handlers;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.ConfigManager;
import net.miarma.api.backlib.Constants; import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.http.ApiStatus; import net.miarma.api.backlib.http.ApiStatus;
import net.miarma.api.microservices.miarmacraft.entities.ModEntity;
import net.miarma.api.microservices.miarmacraft.services.ModService;
import net.miarma.api.backlib.util.JsonUtil; import net.miarma.api.backlib.util.JsonUtil;
import net.miarma.api.microservices.minecraft.entities.ModEntity;
import net.miarma.api.microservices.minecraft.services.ModService;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;

View File

@@ -0,0 +1,5 @@
package net.miarma.api.microservices.minecraft.handlers;
public class ModLogicHandler {
}

View File

@@ -1,12 +1,12 @@
package net.miarma.api.microservices.miarmacraft.handlers; package net.miarma.api.microservices.minecraft.handlers;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.Constants; import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.http.ApiStatus; import net.miarma.api.backlib.http.ApiStatus;
import net.miarma.api.microservices.miarmacraft.entities.PlayerEntity;
import net.miarma.api.microservices.miarmacraft.services.PlayerService;
import net.miarma.api.backlib.util.JsonUtil; import net.miarma.api.backlib.util.JsonUtil;
import net.miarma.api.microservices.minecraft.entities.PlayerEntity;
import net.miarma.api.microservices.minecraft.services.PlayerService;
public class PlayerDataHandler { public class PlayerDataHandler {
private final PlayerService playerService; private final PlayerService playerService;

View File

@@ -1,4 +1,4 @@
package net.miarma.api.microservices.miarmacraft.handlers; package net.miarma.api.microservices.minecraft.handlers;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject; import io.vertx.core.json.JsonObject;

View File

@@ -1,14 +1,14 @@
package net.miarma.api.microservices.miarmacraft.routing; package net.miarma.api.microservices.minecraft.routing;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
import io.vertx.ext.web.Router; import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import net.miarma.api.backlib.Constants.MMCUserRole; import net.miarma.api.backlib.Constants.MMCUserRole;
import net.miarma.api.microservices.miarmacraft.handlers.ModDataHandler; import net.miarma.api.microservices.minecraft.handlers.ModDataHandler;
import net.miarma.api.microservices.miarmacraft.handlers.PlayerDataHandler; import net.miarma.api.microservices.minecraft.handlers.PlayerDataHandler;
import net.miarma.api.microservices.miarmacraft.routing.middlewares.MMCAuthGuard; import net.miarma.api.microservices.minecraft.routing.middlewares.MMCAuthGuard;
import net.miarma.api.microservices.miarmacraft.services.PlayerService; import net.miarma.api.microservices.minecraft.services.PlayerService;
public class MMCDataRouter { public class MMCDataRouter {
public static void mount(Router router, Vertx vertx, Pool pool) { public static void mount(Router router, Vertx vertx, Pool pool) {

View File

@@ -1,4 +1,4 @@
package net.miarma.api.microservices.miarmacraft.routing; package net.miarma.api.microservices.minecraft.routing;
import net.miarma.api.backlib.Constants; import net.miarma.api.backlib.Constants;

View File

@@ -1,12 +1,12 @@
package net.miarma.api.microservices.miarmacraft.routing; package net.miarma.api.microservices.minecraft.routing;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
import io.vertx.ext.web.Router; import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import net.miarma.api.microservices.miarmacraft.handlers.PlayerLogicHandler; import net.miarma.api.microservices.minecraft.handlers.PlayerLogicHandler;
import net.miarma.api.microservices.miarmacraft.routing.middlewares.MMCAuthGuard; import net.miarma.api.microservices.minecraft.routing.middlewares.MMCAuthGuard;
import net.miarma.api.microservices.miarmacraft.services.PlayerService; import net.miarma.api.microservices.minecraft.services.PlayerService;
public class MMCLogicRouter { public class MMCLogicRouter {
public static void mount(Router router, Vertx vertx, Pool pool) { public static void mount(Router router, Vertx vertx, Pool pool) {

View File

@@ -1,12 +1,12 @@
package net.miarma.api.microservices.miarmacraft.routing.middlewares; package net.miarma.api.microservices.minecraft.routing.middlewares;
import java.util.function.Consumer; import java.util.function.Consumer;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.miarma.api.backlib.Constants.MMCUserRole; import net.miarma.api.backlib.Constants.MMCUserRole;
import net.miarma.api.backlib.middlewares.AbstractAuthGuard; import net.miarma.api.backlib.middlewares.AbstractAuthGuard;
import net.miarma.api.microservices.miarmacraft.entities.PlayerEntity; import net.miarma.api.microservices.minecraft.entities.PlayerEntity;
import net.miarma.api.microservices.miarmacraft.services.PlayerService; import net.miarma.api.microservices.minecraft.services.PlayerService;
public class MMCAuthGuard extends AbstractAuthGuard<PlayerEntity, MMCUserRole> { public class MMCAuthGuard extends AbstractAuthGuard<PlayerEntity, MMCUserRole> {
private final PlayerService playerService; private final PlayerService playerService;

Some files were not shown because too many files have changed in this diff Show More