diff --git a/backlib/src/main/java/net/miarma/api/backlib/config/ConfigManager.java b/backlib/src/main/java/net/miarma/api/backlib/config/ConfigManager.java
index 0f36fd9..79df56f 100644
--- a/backlib/src/main/java/net/miarma/api/backlib/config/ConfigManager.java
+++ b/backlib/src/main/java/net/miarma/api/backlib/config/ConfigManager.java
@@ -124,6 +124,11 @@ public class ConfigManager {
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) {
return config.getProperty(key);
}
diff --git a/backlib/src/main/java/net/miarma/api/backlib/db/AbstractEntity.java b/backlib/src/main/java/net/miarma/api/backlib/db/AbstractEntity.java
index 4db8b79..bb52627 100644
--- a/backlib/src/main/java/net/miarma/api/backlib/db/AbstractEntity.java
+++ b/backlib/src/main/java/net/miarma/api/backlib/db/AbstractEntity.java
@@ -1,67 +1,45 @@
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.sqlclient.Row;
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;
-/**
- * Clase base para todas las entidades persistentes del sistema.
- *
- * Proporciona utilidades para:
- *
- * - Construir una entidad a partir de una fila de base de datos ({@link Row})
- * - Serializar una entidad a {@link JsonObject}
- * - Generar una representación en texto
- *
- *
- * 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 {
- private final Logger LOGGER = LoggerProvider.getLogger();
+ private final Logger LOGGER = LoggerProvider.getLogger();
- /**
- * Constructor por defecto. Requerido para instanciación sin datos.
- */
- public AbstractEntity() {}
+ 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) {
populateFromRow(row);
}
- /**
- * Rellena los campos del objeto usando reflexión a partir de una {@link Row} de Vert.x.
- * Se soportan tipos básicos (String, int, boolean, etc.), enums con método estático {@code fromInt(int)},
- * y {@link java.math.BigDecimal} (a través del tipo {@code Numeric} de Vert.x).
- *
- * 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 String getColumnName(Field field) {
+ if (field.isAnnotationPresent(SerializedName.class)) {
+ return field.getAnnotation(SerializedName.class).value();
+ }
+ return field.getName();
+ }
+
private void populateFromRow(Row row) {
Field[] fields = this.getClass().getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
Class> type = field.getType();
- String name = field.getName();
+ String columnName = getColumnName(field);
Object value;
if (type.isEnum()) {
- Integer intValue = row.getInteger(name);
+ Integer intValue = row.getInteger(columnName);
if (intValue != null) {
try {
var method = type.getMethod("fromInt", int.class);
@@ -73,43 +51,44 @@ public abstract class AbstractEntity {
value = null;
}
} else {
- value = switch (type.getSimpleName()) {
- case "Integer", "int" -> row.getInteger(name);
- case "String" -> row.getString(name);
- case "double", "Double" -> row.getDouble(name);
- case "long", "Long" -> row.getLong(name);
- case "boolean", "Boolean" -> row.getBoolean(name);
- case "LocalDateTime" -> row.getLocalDateTime(name);
- case "BigDecimal" -> {
- try {
- var numeric = row.get(io.vertx.sqlclient.data.Numeric.class, row.getColumnIndex(name));
- yield numeric != null ? numeric.bigDecimalValue() : null;
- } catch (Exception e) {
- yield null;
- }
- }
- default -> {
- LOGGER.error("Type not supported yet: {} for field {}", type.getName(), name);
- yield null;
- }
- };
-
+ value = switch (type.getSimpleName()) {
+ case "Integer", "int" -> row.getInteger(columnName);
+ case "String" -> row.getString(columnName);
+ case "double", "Double" -> row.getDouble(columnName);
+ case "long", "Long" -> row.getLong(columnName);
+ case "boolean", "Boolean" -> row.getBoolean(columnName);
+ case "LocalDateTime" -> row.getLocalDateTime(columnName);
+ case "UUID" -> {
+ Object raw = row.getValue(columnName);
+ if (raw instanceof UUID) yield raw;
+ if (raw instanceof String s) yield UUID.fromString(s);
+ if (raw instanceof Buffer b) {
+ yield BufferUtil.uuidFromBuffer(b);
+ }
+ yield null;
+ }
+ case "BigDecimal" -> {
+ 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) {
LOGGER.error("Error populating field {}: {}", field.getName(), e.getMessage());
}
}
}
- /**
- * Codifica esta entidad como un objeto JSON, omitiendo los campos anotados con {@link APIDontReturn}.
- *
- *
Si un campo implementa {@link IValuableEnum}, se usará su valor en lugar del nombre del enum.
- *
- * @return Representación JSON de esta entidad.
- */
public String encode() {
JsonObject json = new JsonObject();
Class> clazz = this.getClass();
@@ -117,13 +96,13 @@ public abstract class AbstractEntity {
while (clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(APIDontReturn.class)) continue;
-
field.setAccessible(true);
try {
Object value = field.get(this);
-
if (value instanceof IValuableEnum ve) {
json.put(field.getName(), ve.getValue());
+ } else if (value instanceof UUID u) {
+ json.put(field.getName(), u.toString());
} else {
json.put(field.getName(), value);
}
@@ -133,31 +112,22 @@ public abstract class AbstractEntity {
}
clazz = clazz.getSuperclass();
}
-
return json.encode();
}
- /**
- * Devuelve una representación en texto de la entidad, mostrando todos los campos y sus valores.
- *
- * Útil para logs y debugging.
- *
- * @return Cadena de texto con el nombre de la clase y todos los campos.
- */
public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(this.getClass().getSimpleName()).append(" [ ");
- Field[] fields = this.getClass().getDeclaredFields();
- for (Field field : fields) {
- field.setAccessible(true);
- try {
- sb.append(field.getName()).append("= ").append(field.get(this)).append(", ");
- } catch (IllegalAccessException e) {
- LOGGER.error("Error stringing field {}: {}", field.getName(), e.getMessage());
+ StringBuilder sb = new StringBuilder();
+ sb.append(this.getClass().getSimpleName()).append(" [ ");
+ Field[] fields = this.getClass().getDeclaredFields();
+ for (Field field : fields) {
+ field.setAccessible(true);
+ try {
+ sb.append(field.getName()).append("=").append(field.get(this)).append(", ");
+ } catch (IllegalAccessException e) {
+ LOGGER.error("Error stringing field {}: {}", field.getName(), e.getMessage());
}
- }
- sb.append("]");
- return sb.toString();
+ }
+ sb.append("]");
+ return sb.toString();
}
-
-}
+}
\ No newline at end of file
diff --git a/backlib/src/main/java/net/miarma/api/backlib/db/QueryBuilder.java b/backlib/src/main/java/net/miarma/api/backlib/db/QueryBuilder.java
index 2dce697..c400acb 100644
--- a/backlib/src/main/java/net/miarma/api/backlib/db/QueryBuilder.java
+++ b/backlib/src/main/java/net/miarma/api/backlib/db/QueryBuilder.java
@@ -3,73 +3,40 @@ package net.miarma.api.backlib.db;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.StringJoiner;
+import java.util.*;
import java.util.stream.Collectors;
import org.slf4j.Logger;
-
+import com.google.gson.annotations.SerializedName;
import net.miarma.api.backlib.annotations.Table;
import net.miarma.api.backlib.log.LoggerProvider;
-/**
- * Clase utilitaria para construir queries SQL dinámicamente mediante reflexión,
- * usando entidades anotadas con {@link Table}.
- *
- * 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.
- *
- * ¡Ojo! No ejecuta la query, solo la construye.
- *
- * @author José Manuel Amador Gallardo
- */
public class QueryBuilder {
- private final static Logger LOGGER = LoggerProvider.getLogger();
+
+ private static final Logger LOGGER = LoggerProvider.getLogger();
private final StringBuilder query;
- private String sort;
- private String order;
- private String limit;
+ private String orderByClause;
+ private String limitClause;
+ private String offsetClause;
private Class> entityClass;
public QueryBuilder() {
this.query = new StringBuilder();
}
- /**
- * Obtiene el nombre de la tabla desde la anotación @Table de la clase dada.
- */
private static String getTableName(Class clazz) {
- if (clazz == null) {
- throw new IllegalArgumentException("Class cannot be null");
- }
-
- if (clazz.isAnnotationPresent(Table.class)) {
- Table annotation = clazz.getAnnotation(Table.class);
- return annotation.value();
- }
- throw new IllegalArgumentException("Class does not have @Table annotation");
+ if (clazz == 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();
}
- /**
- * Devuelve la consulta SQL construida hasta el momento.
- */
- public String getQuery() {
- return query.toString();
+ private static String getColumnName(Field field) {
+ SerializedName annotation = field.getAnnotation(SerializedName.class);
+ return annotation != null ? annotation.value() : field.getName();
}
- /**
- * 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) {
+ if (fieldValue == null) return null;
if (fieldValue instanceof Enum>) {
try {
var method = fieldValue.getClass().getMethod("getValue");
@@ -78,199 +45,137 @@ public class QueryBuilder {
return ((Enum>) fieldValue).name();
}
}
-
if (fieldValue instanceof LocalDateTime ldt) {
return ldt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
-
return fieldValue;
}
- /**
- * Escapa los caracteres especiales en una cadena para evitar inyecciones SQL.
- * @param value the string value to escape
- * @return the escaped string
- */
+ private String formatValue(Object value) {
+ if (value == null) return "NULL";
+ if (value instanceof UUID uuid) {
+ 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) {
return value.replace("'", "''");
}
/**
- * Construye una consulta SELECT para la clase dada, con columnas opcionales.
- * @param clazz the entity class to query
- * @param columns optional columns to select; if empty, selects all columns
- * @return the current QueryBuilder instance
- * @param the type of the entity class
+ * @param clazz La clase de la entidad a seleccionar
+ * @param columns Columnas a seleccionar, si no se pasan selecciona "*"
*/
public static QueryBuilder select(Class clazz, String... columns) {
- if (clazz == null) {
- throw new IllegalArgumentException("Class cannot be null");
- }
-
QueryBuilder qb = new QueryBuilder();
qb.entityClass = clazz;
- String tableName = getTableName(clazz);
-
qb.query.append("SELECT ");
-
- if (columns.length == 0) {
- qb.query.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(" ");
+ if (columns.length == 0) qb.query.append("* ");
+ else qb.query.append(String.join(", ", columns)).append(" ");
+ qb.query.append("FROM ").append(getTableName(clazz)).append(" ");
return qb;
}
/**
- * Añade una cláusula WHERE a la consulta actual, filtrando por los campos del mapa.
- * 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
+ * @param filters Mapa clave = valor para el WHERE. Detecta UUID y Strings tipo IN
*/
- public QueryBuilder where(Map filters) {
- if (filters == null || filters.isEmpty()) {
- return this;
+ public QueryBuilder where(Map filters) {
+ if (filters == null || filters.isEmpty() || entityClass == null) return this;
+
+ Map javaToSql = new HashMap<>();
+ Map sqlToSql = new HashMap<>();
+ for (Field f : entityClass.getDeclaredFields()) {
+ String sqlCol = getColumnName(f);
+ javaToSql.put(f.getName(), sqlCol);
+ sqlToSql.put(sqlCol, sqlCol);
}
- Set validFields = entityClass != null
- ? Arrays.stream(entityClass.getDeclaredFields()).map(Field::getName).collect(Collectors.toSet())
- : Collections.emptySet();
-
List conditions = new ArrayList<>();
-
- for (Map.Entry entry : filters.entrySet()) {
+ for (Map.Entry entry : filters.entrySet()) {
String key = entry.getKey();
- String value = entry.getValue();
-
- if (!validFields.contains(key)) {
+ String sqlCol = javaToSql.getOrDefault(key, sqlToSql.get(key));
+ if (sqlCol == null) {
LOGGER.warn("[QueryBuilder] Ignorando campo invalido en WHERE: {}", key);
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(")")) {
- conditions.add(key + " IN " + value);
- } else if (value.matches("-?\\d+(\\.\\d+)?")) {
- conditions.add(key + " = " + value);
- } else {
- conditions.add(key + " = '" + value + "'");
+ if (!conditions.isEmpty()) {
+ String prefix = query.toString().contains("WHERE") ? "AND " : "WHERE ";
+ query.append(prefix).append(String.join(" AND ", conditions)).append(" ");
+ }
+ return this;
+ }
+
+ /**
+ * @param object Objeto con campos para WHERE. Soporta UUID y LocalDateTime
+ */
+ public QueryBuilder where(T object) {
+ if (object == null) throw new IllegalArgumentException("Object cannot be null");
+ if (entityClass == null) return this;
+
+ Set validColumns = Arrays.stream(entityClass.getDeclaredFields())
+ .map(QueryBuilder::getColumnName)
+ .collect(Collectors.toSet());
+
+ List 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()) {
- 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;
}
- /**
- * 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 QueryBuilder where(T object) {
- if (object == null) {
- throw new IllegalArgumentException("Object cannot be null");
- }
-
- Set 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)) {
- 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) {
- 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 el tipo del objeto a insertar
- */
public static QueryBuilder insert(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();
String table = getTableName(object.getClass());
qb.query.append("INSERT INTO ").append(table).append(" ");
- qb.query.append("(");
- StringJoiner columns = new StringJoiner(", ");
- StringJoiner values = new StringJoiner(", ");
+
+ StringJoiner cols = new StringJoiner(", ");
+ StringJoiner vals = new StringJoiner(", ");
+
for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
- columns.add(field.getName());
- Object fieldValue = field.get(object);
- if (fieldValue != null) {
- Object value = extractValue(fieldValue);
- if (value instanceof String || value instanceof LocalDateTime) {
- values.add("'" + escapeSql((String) value) + "'");
- } else {
- values.add(value.toString());
- }
- } else {
- values.add("NULL");
- }
- } catch (IllegalArgumentException | IllegalAccessException e) {
+ cols.add(getColumnName(field));
+ Object value = extractValue(field.get(object));
+ vals.add(qb.formatValue(value));
+ } catch (IllegalAccessException e) {
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;
}
- /**
- * Construye una consulta UPDATE para el objeto dado, actualizando todos sus campos.
- * 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 el tipo del objeto a actualizar
- */
- public static QueryBuilder update(T object) {
- if (object == null) {
- throw new IllegalArgumentException("Object cannot be null");
- }
+ private static QueryBuilder buildUpdate(T object, boolean includeNulls) {
+ if (object == null) throw new IllegalArgumentException("Object cannot be null");
QueryBuilder qb = new QueryBuilder();
String table = getTableName(object.getClass());
@@ -278,103 +183,36 @@ public class QueryBuilder {
StringJoiner setJoiner = new StringJoiner(", ");
StringJoiner whereJoiner = new StringJoiner(" AND ");
-
Field idField = null;
for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
- Object fieldValue = field.get(object);
- if (fieldValue == null) continue;
-
- String fieldName = field.getName();
- Object value = extractValue(fieldValue);
-
- if (fieldName.endsWith("_id")) {
+ String col = getColumnName(field);
+ Object value = extractValue(field.get(object));
+ if (col.endsWith("_id")) {
idField = field;
- whereJoiner.add(fieldName + " = " + (value instanceof String
- || value instanceof LocalDateTime ? "'" + value + "'" : value));
+ if (value != null) whereJoiner.add(col + " = " + qb.formatValue(value));
+ else throw new IllegalArgumentException("ID field cannot be null");
continue;
}
-
- setJoiner.add(fieldName + " = " + (value instanceof String
- || value instanceof LocalDateTime ? "'" + value + "'" : value));
- } catch (Exception e) {
+ if (value != null) setJoiner.add(col + " = " + qb.formatValue(value));
+ else if (includeNulls) setJoiner.add(col + " = NULL");
+ } catch (IllegalAccessException e) {
LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
}
}
- if (idField == null) {
- throw new IllegalArgumentException("No ID field (ending with _id) found for WHERE clause");
- }
+ if (idField == null) 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;
}
- /**
- * 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 el tipo del objeto a actualizar
- */
- public static QueryBuilder updateWithNulls(T object) {
- if (object == null) {
- throw new IllegalArgumentException("Object cannot be null");
- }
+ public static QueryBuilder update(T object) { return buildUpdate(object, false); }
- QueryBuilder qb = new QueryBuilder();
- String table = getTableName(object.getClass());
- qb.query.append("UPDATE ").append(table).append(" SET ");
+ public static QueryBuilder updateWithNulls(T object) { return buildUpdate(object, true); }
- 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) {
- 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 el tipo del objeto a insertar o actualizar
- */
public static QueryBuilder upsert(T object, String... conflictKeys) {
if (object == null) throw new IllegalArgumentException("Object cannot be null");
@@ -382,146 +220,75 @@ public class QueryBuilder {
String table = getTableName(object.getClass());
qb.query.append("INSERT INTO ").append(table).append(" ");
- StringJoiner columns = new StringJoiner(", ");
- StringJoiner values = new StringJoiner(", ");
+ StringJoiner cols = new StringJoiner(", ");
+ StringJoiner vals = new StringJoiner(", ");
Map updates = new HashMap<>();
for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
- Object fieldValue = field.get(object);
- String columnName = field.getName();
- columns.add(columnName);
-
- Object value = extractValue(fieldValue);
- String valueStr = value == null ? "NULL"
- : (value instanceof String || value instanceof LocalDateTime ? "'" + value + "'" : value.toString());
- values.add(valueStr);
-
- // no actualizamos la clave duplicada
- boolean isConflictKey = Arrays.asList(conflictKeys).contains(columnName);
- if (!isConflictKey) {
- updates.put(columnName, valueStr);
- }
-
- } catch (Exception e) {
+ String col = getColumnName(field);
+ Object value = extractValue(field.get(object));
+ String formatted = qb.formatValue(value);
+ cols.add(col);
+ vals.add(formatted);
+ if (!Arrays.asList(conflictKeys).contains(col)) updates.put(col, formatted);
+ } catch (IllegalAccessException e) {
LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
}
}
- qb.query.append("(").append(columns).append(") VALUES (").append(values).append(")");
-
- if (conflictKeys.length > 0 && !updates.isEmpty()) {
+ qb.query.append("(").append(cols).append(") VALUES (").append(vals).append(")");
+ if (!updates.isEmpty() && conflictKeys.length > 0) {
qb.query.append(" ON DUPLICATE KEY UPDATE ");
- StringJoiner updateSet = new StringJoiner(", ");
- updates.forEach((k, v) -> updateSet.add(k + " = " + v));
- qb.query.append(updateSet);
+ qb.query.append(updates.entrySet().stream()
+ .map(e -> e.getKey() + " = " + e.getValue())
+ .collect(Collectors.joining(", ")));
}
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 el tipo del objeto a eliminar
- */
public static QueryBuilder delete(T object) {
if (object == null) throw new IllegalArgumentException("Object cannot be null");
QueryBuilder qb = new QueryBuilder();
- String table = getTableName(object.getClass());
- qb.query.append("DELETE FROM ").append(table).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) {
- Object value = extractValue(fieldValue);
- joiner.add(field.getName() + " = " + (value instanceof String
- || value instanceof LocalDateTime ? "'" + value + "'" : value.toString()));
- }
- } catch (Exception e) {
- LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
- }
- }
-
- qb.query.append(joiner).append(" ");
- return qb;
+ qb.entityClass = object.getClass();
+ String table = getTableName(qb.entityClass);
+ qb.query.append("DELETE FROM ").append(table).append(" ");
+ return qb.where(object);
}
- /**
- * 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 column, Optional order) {
column.ifPresent(c -> {
if (entityClass != null) {
- boolean isValid = Arrays.stream(entityClass.getDeclaredFields())
- .map(Field::getName)
- .anyMatch(f -> f.equals(c));
-
- if (!isValid) {
+ boolean valid = Arrays.stream(entityClass.getDeclaredFields())
+ .map(QueryBuilder::getColumnName)
+ .anyMatch(f -> f.equals(c));
+ if (!valid) {
LOGGER.warn("[QueryBuilder] Ignorando campo invalido en ORDER BY: {}", c);
return;
}
}
-
- sort = "ORDER BY " + c + " ";
- order.ifPresent(o -> sort += o.equalsIgnoreCase("asc") ? "ASC" : "DESC" + " ");
+ orderByClause = "ORDER BY " + c + " " + order.orElse("ASC") + " ";
});
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 limitParam) {
- limitParam.ifPresent(param -> limit = "LIMIT " + param + " ");
+ limitParam.ifPresent(l -> limitClause = "LIMIT " + l + " ");
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 offsetParam) {
- offsetParam.ifPresent(param -> limit += "OFFSET " + param + " ");
+ offsetParam.ifPresent(o -> offsetClause = "OFFSET " + o + " ");
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() {
- if (order != null && !order.isEmpty()) {
- query.append(order);
- }
- if (sort != null && !sort.isEmpty()) {
- query.append(sort);
- }
- if (limit != null && !limit.isEmpty()) {
- query.append(limit);
- }
+ if (orderByClause != null) query.append(orderByClause);
+ if (limitClause != null) query.append(limitClause);
+ if (offsetClause != null) query.append(offsetClause);
return query.toString().trim() + ";";
}
-}
\ No newline at end of file
+}
diff --git a/backlib/src/main/java/net/miarma/api/backlib/security/JWTManager.java b/backlib/src/main/java/net/miarma/api/backlib/security/JWTManager.java
index c86f85b..1094ca5 100644
--- a/backlib/src/main/java/net/miarma/api/backlib/security/JWTManager.java
+++ b/backlib/src/main/java/net/miarma/api/backlib/security/JWTManager.java
@@ -32,7 +32,7 @@ public class JWTManager {
* Genera un token JWT usando UUID para el usuario.
*/
public String generateToken(
- String userName,
+ String displayName,
UUID userId,
IUserRole role,
Integer serviceId,
@@ -45,7 +45,7 @@ public class JWTManager {
);
return JWT.create()
- .withSubject(userName)
+ .withSubject(displayName)
.withClaim("userId", userId.toString())
.withClaim("serviceId", serviceId)
.withClaim("role", role.name())
@@ -54,6 +54,10 @@ public class JWTManager {
.sign(algorithm);
}
+ public DecodedJWT decode(String token) {
+ return JWT.decode(token);
+ }
+
public UUID extractUserId(String token) {
try {
DecodedJWT jwt = verifier.verify(token);
diff --git a/backlib/src/main/java/net/miarma/api/backlib/util/BufferUtil.java b/backlib/src/main/java/net/miarma/api/backlib/util/BufferUtil.java
new file mode 100644
index 0000000..3f631eb
--- /dev/null
+++ b/backlib/src/main/java/net/miarma/api/backlib/util/BufferUtil.java
@@ -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);
+ }
+}
diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml
index 0a33e7f..84b1e44 100644
--- a/bootstrap/pom.xml
+++ b/bootstrap/pom.xml
@@ -39,7 +39,8 @@
core
${project.version}
-
+
diff --git a/bootstrap/src/main/java/net/miarma/api/MasterVerticle.java b/bootstrap/src/main/java/net/miarma/api/MasterVerticle.java
index c782e0f..532d6d2 100644
--- a/bootstrap/src/main/java/net/miarma/api/MasterVerticle.java
+++ b/bootstrap/src/main/java/net/miarma/api/MasterVerticle.java
@@ -1,5 +1,7 @@
package net.miarma.api;
+import java.util.List;
+
import org.slf4j.Logger;
import io.vertx.core.AbstractVerticle;
@@ -9,10 +11,10 @@ import net.miarma.api.backlib.log.LogAccumulator;
import net.miarma.api.backlib.log.LoggerProvider;
import net.miarma.api.backlib.util.DeploymentUtil;
import net.miarma.api.microservices.core.verticles.CoreMainVerticle;
-import net.miarma.api.microservices.huertos.verticles.HuertosMainVerticle;
-import net.miarma.api.microservices.huertosdecine.verticles.CineMainVerticle;
-import net.miarma.api.microservices.minecraft.verticles.MMCMainVerticle;
-import net.miarma.api.microservices.mpaste.verticles.MPasteMainVerticle;
+//import net.miarma.api.microservices.huertos.verticles.HuertosMainVerticle;
+//import net.miarma.api.microservices.huertosdecine.verticles.CineMainVerticle;
+//import net.miarma.api.microservices.minecraft.verticles.MMCMainVerticle;
+//import net.miarma.api.microservices.mpaste.verticles.MPasteMainVerticle;
@SuppressWarnings("unused")
public class MasterVerticle extends AbstractVerticle {
@@ -38,7 +40,7 @@ public class MasterVerticle extends AbstractVerticle {
.onSuccess(id -> LogAccumulator.add(DeploymentUtil.successMessage(CoreMainVerticle.class)))
.onFailure(err -> LogAccumulator.add(DeploymentUtil.failMessage(CoreMainVerticle.class, err)));
- Future huertos = vertx.deployVerticle(new HuertosMainVerticle())
+ /*Future huertos = vertx.deployVerticle(new HuertosMainVerticle())
.onSuccess(id -> LogAccumulator.add(DeploymentUtil.successMessage(HuertosMainVerticle.class)))
.onFailure(err -> LogAccumulator.add(DeploymentUtil.failMessage(HuertosMainVerticle.class, err)));
@@ -52,9 +54,10 @@ public class MasterVerticle extends AbstractVerticle {
Future mpaste = vertx.deployVerticle(new MPasteMainVerticle())
.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())
.onFailure(promise::fail);
diff --git a/bootstrap/src/main/resources/default.properties b/bootstrap/src/main/resources/default.properties
index 4037d3c..5d47e5a 100644
--- a/bootstrap/src/main/resources/default.properties
+++ b/bootstrap/src/main/resources/default.properties
@@ -31,8 +31,8 @@ dp.poolSize=5
# HTTP Server Configuration
inet.host=localhost
-sso.logic.port=8080
-sso.data.port=8081
+core.logic.port=8080
+core.data.port=8081
minecraft.logic.port=8100
minecraft.data.port=8101
huertos.logic.port=8120
@@ -60,4 +60,4 @@ smtp.password.admin=
smtp.password.noreply=
# Discord Configuration
-discord.webhook=
\ No newline at end of file
+discord.webhook=
diff --git a/microservices/core/src/main/java/net/miarma/api/microservices/core/dao/UserDAO.java b/microservices/core/src/main/java/net/miarma/api/microservices/core/dao/UserDAO.java
index 22d90bb..a624a79 100644
--- a/microservices/core/src/main/java/net/miarma/api/microservices/core/dao/UserDAO.java
+++ b/microservices/core/src/main/java/net/miarma/api/microservices/core/dao/UserDAO.java
@@ -32,9 +32,11 @@ public class UserDAO implements DataAccessObject {
Promise promise = Promise.promise();
String query = QueryBuilder
.select(UserEntity.class)
- .where(Map.of("user_id", id.toString()))
+ .where(Map.of("user_id", id))
.build();
+ System.out.println(query);
+
db.executeOne(query, UserEntity.class,
promise::complete,
promise::fail
diff --git a/microservices/core/src/main/java/net/miarma/api/microservices/core/entities/CredentialEntity.java b/microservices/core/src/main/java/net/miarma/api/microservices/core/entities/CredentialEntity.java
index 8ee297c..aae903a 100644
--- a/microservices/core/src/main/java/net/miarma/api/microservices/core/entities/CredentialEntity.java
+++ b/microservices/core/src/main/java/net/miarma/api/microservices/core/entities/CredentialEntity.java
@@ -1,14 +1,17 @@
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 java.util.UUID;
-import java.time.LocalDateTime;
+import net.miarma.api.backlib.db.AbstractEntity;
@Table("credentials")
-public class CredentialEntity {
+public class CredentialEntity extends AbstractEntity {
@SerializedName("credential_id")
private UUID credentialId;
@@ -34,7 +37,8 @@ public class CredentialEntity {
@SerializedName("updated_at")
private LocalDateTime updatedAt;
- public CredentialEntity() {}
+ public CredentialEntity() { }
+ public CredentialEntity(Row row) { super(row); }
public UUID getCredentialId() {
return credentialId;
diff --git a/microservices/core/src/main/java/net/miarma/api/microservices/core/entities/UserEntity.java b/microservices/core/src/main/java/net/miarma/api/microservices/core/entities/UserEntity.java
index b4ea1fd..bd2536e 100644
--- a/microservices/core/src/main/java/net/miarma/api/microservices/core/entities/UserEntity.java
+++ b/microservices/core/src/main/java/net/miarma/api/microservices/core/entities/UserEntity.java
@@ -3,6 +3,8 @@ 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;
@@ -11,14 +13,24 @@ import net.miarma.api.microservices.core.enums.CoreUserGlobalStatus;
@Table("users")
public class UserEntity extends AbstractEntity {
+ @SerializedName("user_id")
private UUID userId;
- private String userName;
- private String email;
+
+ @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() { }
@@ -27,12 +39,6 @@ public class UserEntity extends AbstractEntity {
public UUID getUserId() { return userId; }
public void setUserId(UUID userId) { this.userId = userId; }
- 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 getDisplayName() { return displayName; }
public void setDisplayName(String displayName) { this.displayName = displayName; }
diff --git a/microservices/core/src/main/java/net/miarma/api/microservices/core/handlers/AuthHandler.java b/microservices/core/src/main/java/net/miarma/api/microservices/core/handlers/AuthHandler.java
new file mode 100644
index 0000000..0915ad3
--- /dev/null
+++ b/microservices/core/src/main/java/net/miarma/api/microservices/core/handlers/AuthHandler.java
@@ -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));
+ }
+}
\ No newline at end of file
diff --git a/microservices/core/src/main/java/net/miarma/api/microservices/core/handlers/UserDataHandler.java b/microservices/core/src/main/java/net/miarma/api/microservices/core/handlers/UserDataHandler.java
index 34b6c59..daa0829 100644
--- a/microservices/core/src/main/java/net/miarma/api/microservices/core/handlers/UserDataHandler.java
+++ b/microservices/core/src/main/java/net/miarma/api/microservices/core/handlers/UserDataHandler.java
@@ -1,10 +1,10 @@
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.Constants;
import net.miarma.api.backlib.gson.GsonProvider;
import net.miarma.api.backlib.http.ApiStatus;
import net.miarma.api.backlib.http.QueryParams;
@@ -15,7 +15,7 @@ import net.miarma.api.microservices.core.services.UserService;
@SuppressWarnings("unused")
public class UserDataHandler {
- private final Gson GSON = GsonProvider.get();
+ private final Gson GSON = GsonProvider.get();
private final UserService userService;
public UserDataHandler(Pool pool) {
@@ -23,52 +23,32 @@ public class UserDataHandler {
}
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());
- });
+ 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) {
- 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 = 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());
- });
+ 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) {
- UserEntity user = 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());
- });
+ 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) {
- 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());
- });
+ 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"));
}
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/microservices/core/src/main/java/net/miarma/api/microservices/core/handlers/UserLogicHandler.java b/microservices/core/src/main/java/net/miarma/api/microservices/core/handlers/UserLogicHandler.java
index b2b7887..c300b32 100644
--- a/microservices/core/src/main/java/net/miarma/api/microservices/core/handlers/UserLogicHandler.java
+++ b/microservices/core/src/main/java/net/miarma/api/microservices/core/handlers/UserLogicHandler.java
@@ -1,162 +1,26 @@
package net.miarma.api.microservices.core.handlers;
-import com.auth0.jwt.interfaces.DecodedJWT;
+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.Constants;
+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;
-import net.miarma.api.microservices.core.entities.UserEntity;
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 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");
@@ -166,9 +30,9 @@ public class UserLogicHandler {
}
String token = authHeader.substring(7);
- int userId = net.miarma.api.backlib.security.JWTManager.getInstance().getUserId(token);
+ UUID userId = JWTManager.getInstance().extractUserId(token);
- if (userId <= 0) {
+ if (userId == null) {
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, null, "Invalid token");
return;
}
@@ -177,7 +41,7 @@ public class UserLogicHandler {
.put("action", "getInfo")
.put("userId", userId);
- vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
+ vertx.eventBus().request(CORE_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else {
@@ -193,7 +57,7 @@ public class UserLogicHandler {
.put("action", "userExists")
.put("userId", userId);
- vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
+ vertx.eventBus().request(CORE_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else {
@@ -209,7 +73,7 @@ public class UserLogicHandler {
.put("action", "getStatus")
.put("userId", userId);
- vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
+ vertx.eventBus().request(CORE_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else {
@@ -225,7 +89,7 @@ public class UserLogicHandler {
.put("action", "getRole")
.put("userId", userId);
- vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
+ vertx.eventBus().request(CORE_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else {
@@ -241,7 +105,7 @@ public class UserLogicHandler {
.put("action", "getAvatar")
.put("userId", userId);
- vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
+ vertx.eventBus().request(CORE_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
} else {
@@ -258,7 +122,7 @@ public class UserLogicHandler {
.put("userId", body.getInteger("userId"))
.put("status", body.getInteger("status"));
- vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
+ vertx.eventBus().request(CORE_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.NO_CONTENT, null);
} else {
@@ -275,7 +139,7 @@ public class UserLogicHandler {
.put("userId", body.getInteger("userId"))
.put("role", body.getInteger("role"));
- vertx.eventBus().request(Constants.AUTH_EVENT_BUS, request, ar -> {
+ vertx.eventBus().request(CORE_EVENT_BUS, request, ar -> {
if (ar.succeeded()) {
JsonUtil.sendJson(ctx, ApiStatus.NO_CONTENT, null);
} else {
diff --git a/microservices/core/src/main/java/net/miarma/api/microservices/core/routing/CoreDataRouter.java b/microservices/core/src/main/java/net/miarma/api/microservices/core/routing/CoreDataRouter.java
index 6dc361a..1a53037 100644
--- a/microservices/core/src/main/java/net/miarma/api/microservices/core/routing/CoreDataRouter.java
+++ b/microservices/core/src/main/java/net/miarma/api/microservices/core/routing/CoreDataRouter.java
@@ -4,32 +4,30 @@ import io.vertx.core.Vertx;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.sqlclient.Pool;
-import net.miarma.api.backlib.Constants.CoreUserRole;
+import net.miarma.api.microservices.core.enums.CoreUserGlobalRole;
import net.miarma.api.microservices.core.handlers.FileDataHandler;
import net.miarma.api.microservices.core.handlers.UserDataHandler;
-import net.miarma.api.microservices.core.services.UserService;
import net.miarma.api.microservices.core.routing.middlewares.CoreAuthGuard;
+import net.miarma.api.microservices.core.services.UserService;
public class CoreDataRouter {
- public static void mount(Router router, Vertx vertx, Pool pool) {
- UserDataHandler hUserData = new UserDataHandler(pool);
- FileDataHandler hFileData = new FileDataHandler(pool);
- UserService userService = new UserService(pool);
- CoreAuthGuard authGuard = new CoreAuthGuard(userService);
-
- router.route().handler(BodyHandler.create());
+ public static void mount(Router router, Vertx vertx, Pool pool) {
+ UserDataHandler hUserData = new UserDataHandler(pool);
+ FileDataHandler hFileData = new FileDataHandler(pool);
+ UserService userService = new UserService(pool);
+ CoreAuthGuard authGuard = new CoreAuthGuard(userService);
+
+ router.route().handler(BodyHandler.create());
- router.get(CoreEndpoints.USERS).handler(authGuard.check(CoreUserRole.ADMIN)).handler(hUserData::getAll);
- router.get(CoreEndpoints.USER).handler(authGuard.check(CoreUserRole.ADMIN)).handler(hUserData::getById);
- router.post(CoreEndpoints.USERS).handler(hUserData::create);
- router.put(CoreEndpoints.USER).handler(authGuard.check(CoreUserRole.ADMIN)).handler(hUserData::update);
- 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.FILE).handler(authGuard.check()).handler(hFileData::getById);
- router.post(CoreEndpoints.FILE_UPLOAD).handler(authGuard.check()).handler(hFileData::create);
- router.put(CoreEndpoints.FILE).handler(authGuard.check()).handler(hFileData::update);
- router.delete(CoreEndpoints.FILE).handler(authGuard.check()).handler(hFileData::delete);
-
- }
-}
+ router.get(CoreEndpoints.USERS).handler(authGuard.check(CoreUserGlobalRole.ADMIN)).handler(hUserData::getAll);
+ router.get(CoreEndpoints.USER).handler(authGuard.check(CoreUserGlobalRole.ADMIN)).handler(hUserData::getById);
+ router.put(CoreEndpoints.USER).handler(authGuard.check(CoreUserGlobalRole.ADMIN)).handler(hUserData::update);
+ router.delete(CoreEndpoints.USER).handler(authGuard.check(CoreUserGlobalRole.ADMIN)).handler(hUserData::delete);
+
+ router.get(CoreEndpoints.FILES).handler(authGuard.check(CoreUserGlobalRole.ADMIN)).handler(hFileData::getAll);
+ router.get(CoreEndpoints.FILE).handler(authGuard.check()).handler(hFileData::getById);
+ router.post(CoreEndpoints.FILE_UPLOAD).handler(authGuard.check()).handler(hFileData::create);
+ router.put(CoreEndpoints.FILE).handler(authGuard.check()).handler(hFileData::update);
+ router.delete(CoreEndpoints.FILE).handler(authGuard.check()).handler(hFileData::delete);
+ }
+}
\ No newline at end of file
diff --git a/microservices/core/src/main/java/net/miarma/api/microservices/core/routing/CoreEndpoints.java b/microservices/core/src/main/java/net/miarma/api/microservices/core/routing/CoreEndpoints.java
index d8e5ba2..7ccc6cb 100644
--- a/microservices/core/src/main/java/net/miarma/api/microservices/core/routing/CoreEndpoints.java
+++ b/microservices/core/src/main/java/net/miarma/api/microservices/core/routing/CoreEndpoints.java
@@ -1,39 +1,36 @@
package net.miarma.api.microservices.core.routing;
-import net.miarma.api.backlib.Constants;
+import net.miarma.api.backlib.config.ConfigManager;
public class CoreEndpoints {
-
- /*
- * RUTAS DE LA API DE DATOS
- * DE NEGOCIO DEL SSO
- */
-
- // Usuarios
- public static final String USERS = Constants.CORE_PREFIX + "/users"; // GET, POST, PUT, DELETE
- public static final String USER = Constants.CORE_PREFIX + "/users/:user_id"; // GET, PUT, DELETE
- public static final String USER_STATUS = Constants.CORE_PREFIX + "/users/:user_id/status"; // GET, PUT
- public static final String USER_ROLE = Constants.CORE_PREFIX + "/users/:user_id/role"; // GET, PUT
- public static final String USER_EXISTS = Constants.CORE_PREFIX + "/users/:user_id/exists"; // GET
- 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
- public static final String FILES = Constants.CORE_PREFIX + "/files"; // GET, POST
- public static final String FILE = Constants.CORE_PREFIX + "/files/:file_id"; // GET, PUT, DELETE
- public static final String FILE_UPLOAD = Constants.CORE_PREFIX + "/files/upload"; // POST
- public static final String FILE_DOWNLOAD = Constants.CORE_PREFIX + "/files/:file_id/download"; // GET
- public static final String USER_FILES = Constants.CORE_PREFIX + "/files/myfiles"; // GET
-
- /*
- * RUTAS DE LA API DE LOGICA
- * DE NEGOCIO DEL SSO
- */
- public static final String LOGIN = Constants.AUTH_PREFIX + "/login"; // POST
- 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 CHANGE_PASSWORD = Constants.AUTH_PREFIX + "/change-password"; // POST
- 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 = Constants.CORE_PREFIX + "/screenshot"; // GET
-}
+
+ private static final ConfigManager config = ConfigManager.getInstance();
+ private static final String CORE = config.getApiPrefix("core");
+ private static final String AUTH = config.getApiPrefix("auth");
+
+ /* API DE DATOS (UserDataHandler / FileDataHandler) */
+ public static final String USERS = CORE + "/users";
+ public static final String USER = CORE + "/users/:user_id";
+ public static final String FILES = CORE + "/files";
+ public static final String FILE = CORE + "/files/:file_id";
+ public static final String FILE_UPLOAD = CORE + "/files/upload";
+
+ /* API DE LÓGICA (AuthHandler / UserLogicHandler / FileLogicHandler) */
+ public static final String LOGIN = AUTH + "/login";
+ public static final String LOGIN_VALID = AUTH + "/login/validate";
+ public static final String REGISTER = AUTH + "/register";
+ public static final String CHANGE_PASSWORD = AUTH + "/change-password";
+ 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";
+ public static final String USER_STATUS = CORE + "/users/:user_id/status";
+ 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 USER_AVATAR = CORE + "/users/:user_id/avatar";
+
+ public static final String FILE_DOWNLOAD = CORE + "/files/:file_id/download";
+ public static final String USER_FILES = CORE + "/files/myfiles";
+
+ public static final String SCREENSHOT = CORE + "/screenshot";
+}
\ No newline at end of file
diff --git a/microservices/core/src/main/java/net/miarma/api/microservices/core/routing/CoreLogicRouter.java b/microservices/core/src/main/java/net/miarma/api/microservices/core/routing/CoreLogicRouter.java
index 27c3658..8d61dd3 100644
--- a/microservices/core/src/main/java/net/miarma/api/microservices/core/routing/CoreLogicRouter.java
+++ b/microservices/core/src/main/java/net/miarma/api/microservices/core/routing/CoreLogicRouter.java
@@ -4,41 +4,45 @@ import io.vertx.core.Vertx;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.sqlclient.Pool;
-import net.miarma.api.backlib.Constants.CoreUserRole;
+import net.miarma.api.microservices.core.enums.CoreUserGlobalRole;
+import net.miarma.api.microservices.core.handlers.AuthHandler;
import net.miarma.api.microservices.core.handlers.FileLogicHandler;
import net.miarma.api.microservices.core.handlers.ScreenshotHandler;
import net.miarma.api.microservices.core.handlers.UserLogicHandler;
-import net.miarma.api.microservices.core.services.UserService;
import net.miarma.api.microservices.core.routing.middlewares.CoreAuthGuard;
+import net.miarma.api.microservices.core.services.UserService;
public class CoreLogicRouter {
- public static void mount(Router router, Vertx vertx, Pool pool) {
- UserLogicHandler hUserLogic = new UserLogicHandler(vertx);
- FileLogicHandler hFileLogic = new FileLogicHandler(vertx);
- ScreenshotHandler hScreenshot = new ScreenshotHandler(vertx);
+ public static void mount(Router router, Vertx vertx, Pool pool) {
+ UserLogicHandler hUserLogic = new UserLogicHandler(vertx);
+ FileLogicHandler hFileLogic = new FileLogicHandler(vertx);
+ AuthHandler hAuth = new AuthHandler(vertx);
+ ScreenshotHandler hScreenshot = new ScreenshotHandler(vertx);
+
UserService userService = new UserService(pool);
CoreAuthGuard authGuard = new CoreAuthGuard(userService);
router.route().handler(BodyHandler.create());
- router.post(CoreEndpoints.LOGIN).handler(hUserLogic::login);
- router.get(CoreEndpoints.USER_INFO).handler(authGuard.check()).handler(hUserLogic::getInfo);
- router.post(CoreEndpoints.REGISTER).handler(hUserLogic::register);
- 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.post(CoreEndpoints.LOGIN).handler(hAuth::login);
+ router.post(CoreEndpoints.REGISTER).handler(hAuth::register);
+ router.post(CoreEndpoints.LOGIN_VALID).handler(hAuth::loginValidate);
+
+ 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_STATUS).handler(authGuard.check()).handler(hUserLogic::getStatus);
- router.put(CoreEndpoints.USER_STATUS).handler(authGuard.check(CoreUserRole.ADMIN)).handler(hUserLogic::updateStatus);
- 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.get(CoreEndpoints.USER_AVATAR).handler(authGuard.check()).handler(hUserLogic::getAvatar);
-
- 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.SCREENSHOT).handler(hScreenshot::getScreenshot);
- }
-}
+ router.get(CoreEndpoints.USER_STATUS).handler(authGuard.check()).handler(hUserLogic::getStatus);
+ 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.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.FILE_DOWNLOAD).handler(authGuard.check()).handler(hFileLogic::downloadFile);
+ router.get(CoreEndpoints.USER_FILES).handler(authGuard.check()).handler(hFileLogic::getUserFiles);
+
+ router.get(CoreEndpoints.SCREENSHOT).handler(hScreenshot::getScreenshot);
+ }
+}
\ No newline at end of file
diff --git a/microservices/core/src/main/java/net/miarma/api/microservices/core/routing/middlewares/CoreAuthGuard.java b/microservices/core/src/main/java/net/miarma/api/microservices/core/routing/middlewares/CoreAuthGuard.java
index 5cef285..e62d566 100644
--- a/microservices/core/src/main/java/net/miarma/api/microservices/core/routing/middlewares/CoreAuthGuard.java
+++ b/microservices/core/src/main/java/net/miarma/api/microservices/core/routing/middlewares/CoreAuthGuard.java
@@ -1,14 +1,15 @@
package net.miarma.api.microservices.core.routing.middlewares;
+import java.util.UUID;
import java.util.function.Consumer;
import io.vertx.ext.web.RoutingContext;
-import net.miarma.api.backlib.Constants.CoreUserRole;
import net.miarma.api.backlib.middlewares.AbstractAuthGuard;
import net.miarma.api.microservices.core.entities.UserEntity;
+import net.miarma.api.microservices.core.enums.CoreUserGlobalRole;
import net.miarma.api.microservices.core.services.UserService;
-public class CoreAuthGuard extends AbstractAuthGuard {
+public class CoreAuthGuard extends AbstractAuthGuard {
private final UserService userService;
public CoreAuthGuard(UserService userService) {
@@ -16,12 +17,12 @@ public class CoreAuthGuard extends AbstractAuthGuard {
}
@Override
- protected CoreUserRole parseRole(String roleStr) {
- return CoreUserRole.valueOf(roleStr.toUpperCase());
+ protected CoreUserGlobalRole parseRole(String roleStr) {
+ return CoreUserGlobalRole.valueOf(roleStr.toUpperCase());
}
@Override
- protected void getUserEntity(int userId, RoutingContext ctx, Consumer callback) {
+ protected void getUserEntity(UUID userId, RoutingContext ctx, Consumer callback) {
userService.getById(userId).onComplete(ar -> {
if (ar.succeeded()) callback.accept(ar.result());
else callback.accept(null);
@@ -29,8 +30,8 @@ public class CoreAuthGuard extends AbstractAuthGuard {
}
@Override
- protected boolean hasPermission(UserEntity user, CoreUserRole userRole) {
- return user.getGlobal_role() == CoreUserRole.ADMIN;
+ protected boolean hasPermission(UserEntity user, CoreUserGlobalRole userRole) {
+ return user.getGlobalRole() == CoreUserGlobalRole.ADMIN;
}
}
diff --git a/microservices/core/src/main/java/net/miarma/api/microservices/core/services/AuthService.java b/microservices/core/src/main/java/net/miarma/api/microservices/core/services/AuthService.java
index bbc217a..2a7ed9b 100644
--- a/microservices/core/src/main/java/net/miarma/api/microservices/core/services/AuthService.java
+++ b/microservices/core/src/main/java/net/miarma/api/microservices/core/services/AuthService.java
@@ -35,13 +35,13 @@ public class AuthService {
public Future login(String login, String plainPassword, boolean keepLoggedIn) {
return credentialDAO.getByServiceAndUsername(this.serviceId, login).compose(cred -> {
if (cred == null) {
- return Future.failedFuture(new BadRequestException("Invalid credentials"));
+ 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"));
diff --git a/microservices/core/src/main/java/net/miarma/api/microservices/core/services/UserService.java b/microservices/core/src/main/java/net/miarma/api/microservices/core/services/UserService.java
index 0861c25..5e7f083 100644
--- a/microservices/core/src/main/java/net/miarma/api/microservices/core/services/UserService.java
+++ b/microservices/core/src/main/java/net/miarma/api/microservices/core/services/UserService.java
@@ -31,7 +31,7 @@ public class UserService {
});
}
- public Future updateProfile(UserEntity user) {
+ public Future update(UserEntity user) {
return userDAO.update(user);
}
@@ -49,7 +49,7 @@ public class UserService {
});
}
- public Future deleteUser(UUID id) {
+ public Future delete(UUID id) {
return userDAO.delete(id);
}
}
\ No newline at end of file
diff --git a/microservices/core/src/main/java/net/miarma/api/microservices/core/verticles/CoreDataVerticle.java b/microservices/core/src/main/java/net/miarma/api/microservices/core/verticles/CoreDataVerticle.java
index 810a6ca..da6d822 100644
--- a/microservices/core/src/main/java/net/miarma/api/microservices/core/verticles/CoreDataVerticle.java
+++ b/microservices/core/src/main/java/net/miarma/api/microservices/core/verticles/CoreDataVerticle.java
@@ -1,196 +1,123 @@
package net.miarma.api.microservices.core.verticles;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.UUID;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
-import io.vertx.ext.web.handler.BodyHandler;
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.config.ConfigManager;
-import net.miarma.api.microservices.core.entities.UserEntity;
-import net.miarma.api.microservices.core.services.FileService;
-import net.miarma.api.microservices.core.services.UserService;
import net.miarma.api.backlib.db.DatabaseProvider;
import net.miarma.api.backlib.util.EventBusUtil;
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.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")
public class CoreDataVerticle extends AbstractVerticle {
private ConfigManager configManager;
private UserService userService;
private FileService fileService;
+ private AuthService authService;
@Override
public void start(Promise startPromise) {
configManager = ConfigManager.getInstance();
Pool pool = DatabaseProvider.createPool(vertx, configManager);
+
userService = new UserService(pool);
fileService = new FileService(pool);
+ authService = new AuthService(pool, 0);
+
Router router = Router.router(vertx);
RouterUtil.attachLogger(router);
CoreDataRouter.mount(router, vertx, pool);
+
registerLogicVerticleConsumer();
vertx.createHttpServer()
.requestHandler(router)
- .listen(configManager.getIntProperty("sso.data.port"), res -> {
+ .listen(configManager.getIntProperty("core.data.port"), res -> {
if (res.succeeded()) startPromise.complete();
else startPromise.fail(res.cause());
});
}
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();
String action = body.getString("action");
- switch (action) {
- case "login" -> {
- String email = body.getString("email");
- String userName = body.getString("userName");
- String password = body.getString("password");
- boolean keepLoggedIn = body.getBoolean("keepLoggedIn", false);
-
- userService.login(email != null ? email : userName, password, keepLoggedIn)
- .onSuccess(message::reply)
- .onFailure(EventBusUtil.fail(message));
+ try {
+ switch (action) {
+ case "login" -> {
+ authService.login(
+ body.getString("login"),
+ body.getString("password"),
+ body.getBoolean("keepLoggedIn", false)
+ ).onSuccess(message::reply).onFailure(EventBusUtil.fail(message));
+ }
+
+ case "register" -> {
+ UserEntity profile = new UserEntity();
+ 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);
}
-
- case "register" -> {
- 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 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 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 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 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);
+ } catch (Exception e) {
+ message.fail(400, "Invalid data format or UUID: " + e.getMessage());
}
});
}
diff --git a/microservices/core/src/main/java/net/miarma/api/microservices/core/verticles/CoreLogicVerticle.java b/microservices/core/src/main/java/net/miarma/api/microservices/core/verticles/CoreLogicVerticle.java
index 2e2da70..5aeb45e 100644
--- a/microservices/core/src/main/java/net/miarma/api/microservices/core/verticles/CoreLogicVerticle.java
+++ b/microservices/core/src/main/java/net/miarma/api/microservices/core/verticles/CoreLogicVerticle.java
@@ -24,7 +24,7 @@ public class CoreLogicVerticle extends AbstractVerticle {
vertx.createHttpServer()
.requestHandler(router)
- .listen(configManager.getIntProperty("sso.logic.port"), res -> {
+ .listen(configManager.getIntProperty("core.logic.port"), res -> {
if (res.succeeded()) startPromise.complete();
else startPromise.fail(res.cause());
});
diff --git a/microservices/core/src/main/java/net/miarma/api/microservices/core/verticles/CoreMainVerticle.java b/microservices/core/src/main/java/net/miarma/api/microservices/core/verticles/CoreMainVerticle.java
index b7dd9bf..c42e6f0 100644
--- a/microservices/core/src/main/java/net/miarma/api/microservices/core/verticles/CoreMainVerticle.java
+++ b/microservices/core/src/main/java/net/miarma/api/microservices/core/verticles/CoreMainVerticle.java
@@ -1,16 +1,19 @@
package net.miarma.api.microservices.core.verticles;
+import org.slf4j.Logger;
+
import io.vertx.core.AbstractVerticle;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Promise;
import io.vertx.core.ThreadingModel;
-import net.miarma.api.backlib.Constants;
import net.miarma.api.backlib.config.ConfigManager;
import net.miarma.api.backlib.log.LogAccumulator;
+import net.miarma.api.backlib.log.LoggerProvider;
import net.miarma.api.backlib.util.DeploymentUtil;
public class CoreMainVerticle extends AbstractVerticle {
+ private final Logger LOGGER = LoggerProvider.getLogger();
private ConfigManager configManager;
@Override
@@ -20,7 +23,7 @@ public class CoreMainVerticle extends AbstractVerticle {
deployVerticles();
startPromise.complete();
} catch (Exception e) {
- Constants.LOGGER.error(DeploymentUtil.failMessage(CoreMainVerticle.class, e));
+ LOGGER.error(DeploymentUtil.failMessage(CoreMainVerticle.class, e));
startPromise.fail(e);
}
}
@@ -35,7 +38,7 @@ public class CoreMainVerticle extends AbstractVerticle {
DeploymentUtil.successMessage(CoreDataVerticle.class),
DeploymentUtil.apiUrlMessage(
configManager.getHost(),
- configManager.getIntProperty("sso.data.port")
+ configManager.getIntProperty("core.data.port")
)
);
LogAccumulator.add(message);
@@ -50,7 +53,7 @@ public class CoreMainVerticle extends AbstractVerticle {
DeploymentUtil.successMessage(CoreLogicVerticle.class),
DeploymentUtil.apiUrlMessage(
configManager.getHost(),
- configManager.getIntProperty("sso.logic.port")
+ configManager.getIntProperty("core.logic.port")
)
);
LogAccumulator.add(message);
diff --git a/pom.xml b/pom.xml
index 60af061..d5b33e9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,10 +11,10 @@
backlib
bootstrap
- microservices/huertos
+
microservices/core
@@ -29,7 +29,7 @@
core
${project.version}
-
+