bump: backlib and all microservices to v2.0, add: decoupled auth from identity using new Credential Entity model, still ongoing changes...
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>net.miarma.api</groupId>
|
||||
<artifactId>backlib</artifactId>
|
||||
<version>1.2.1</version>
|
||||
<version>2.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>23</maven.compiler.source>
|
||||
@@ -24,11 +24,11 @@
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>gitea</id>
|
||||
<id>MiarmaGit</id>
|
||||
<url>https://git.miarma.net/api/packages/Gallardo7761/maven</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>gitea</id>
|
||||
<id>MiarmaGit</id>
|
||||
<url>https://git.miarma.net/api/packages/Gallardo7761/maven</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package net.miarma.api.backlib;
|
||||
package net.miarma.api.backlib.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -8,6 +8,10 @@ import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
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.
|
||||
* 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 {
|
||||
private static ConfigManager instance;
|
||||
private final String appName;
|
||||
private final File configFile;
|
||||
private final Properties config;
|
||||
private static final String CONFIG_FILE_NAME = "config.properties";
|
||||
|
||||
private final Logger LOGGER = LoggerProvider.getLogger();
|
||||
|
||||
private ConfigManager() {
|
||||
String path = getBaseDir() + CONFIG_FILE_NAME;
|
||||
this.configFile = new File(path);
|
||||
this.appName = System.getenv().getOrDefault("APP_NAME", "miarma-backend");
|
||||
this.config = new Properties();
|
||||
this.configFile = new File(getBaseDir() + CONFIG_FILE_NAME);
|
||||
}
|
||||
|
||||
public static synchronized ConfigManager getInstance() {
|
||||
@@ -41,7 +47,7 @@ public class ConfigManager {
|
||||
InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8)) {
|
||||
config.load(isr);
|
||||
} catch (IOException e) {
|
||||
Constants.LOGGER.error("Error loading configuration file: ", e);
|
||||
LOGGER.error("Error loading configuration file: ", e);
|
||||
}
|
||||
}
|
||||
public File getConfigFile() {
|
||||
@@ -138,9 +144,9 @@ public class ConfigManager {
|
||||
|
||||
private void saveConfig() {
|
||||
try (FileOutputStream fos = new FileOutputStream(configFile)) {
|
||||
config.store(fos, "Configuration for: " + Constants.APP_NAME);
|
||||
config.store(fos, "Configuration for: " + this.appName);
|
||||
} catch (IOException e) {
|
||||
Constants.LOGGER.error("Error saving configuration file: ", e);
|
||||
LOGGER.error("Error saving configuration file: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,148 +0,0 @@
|
||||
package net.miarma.api.backlib.core.dao;
|
||||
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.sqlclient.Pool;
|
||||
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;
|
||||
import net.miarma.api.backlib.core.entities.FileEntity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class FileDAO implements DataAccessObject<FileEntity, Integer> {
|
||||
|
||||
private final DatabaseManager db;
|
||||
|
||||
public FileDAO(Pool pool) {
|
||||
this.db = DatabaseManager.getInstance(pool);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<List<FileEntity>> getAll() {
|
||||
return getAll(new QueryParams(Map.of(), new QueryFilters()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<FileEntity> getById(Integer id) {
|
||||
Promise<FileEntity> promise = Promise.promise();
|
||||
String query = QueryBuilder
|
||||
.select(FileEntity.class)
|
||||
.where(Map.of("file_id", id.toString()))
|
||||
.build();
|
||||
|
||||
db.executeOne(query, FileEntity.class,
|
||||
promise::complete,
|
||||
promise::fail
|
||||
);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
public Future<List<FileEntity>> getAll(QueryParams params) {
|
||||
Promise<List<FileEntity>> promise = Promise.promise();
|
||||
String query = QueryBuilder
|
||||
.select(FileEntity.class)
|
||||
.where(params.getFilters())
|
||||
.orderBy(params.getQueryFilters().getSort(), params.getQueryFilters().getOrder())
|
||||
.limit(params.getQueryFilters().getLimit())
|
||||
.offset(params.getQueryFilters().getOffset())
|
||||
.build();
|
||||
|
||||
db.execute(query, FileEntity.class,
|
||||
list -> promise.complete(list.isEmpty() ? List.of() : list),
|
||||
promise::fail
|
||||
);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
public Future<List<FileEntity>> getUserFiles(Integer userId) {
|
||||
Promise<List<FileEntity>> promise = Promise.promise();
|
||||
String query = QueryBuilder
|
||||
.select(FileEntity.class)
|
||||
.where(Map.of("uploaded_by", userId.toString()))
|
||||
.build();
|
||||
|
||||
db.execute(query, FileEntity.class,
|
||||
list -> promise.complete(list.isEmpty() ? List.of() : list),
|
||||
promise::fail
|
||||
);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<FileEntity> insert(FileEntity file) {
|
||||
Promise<FileEntity> promise = Promise.promise();
|
||||
String query = QueryBuilder.insert(file).build();
|
||||
|
||||
db.executeOne(query, FileEntity.class,
|
||||
promise::complete,
|
||||
promise::fail
|
||||
);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<FileEntity> upsert(FileEntity file, String... conflictKeys) {
|
||||
Promise<FileEntity> promise = Promise.promise();
|
||||
String query = QueryBuilder.upsert(file, conflictKeys).build();
|
||||
|
||||
db.executeOne(query, FileEntity.class,
|
||||
promise::complete,
|
||||
promise::fail
|
||||
);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<FileEntity> update(FileEntity file) {
|
||||
Promise<FileEntity> promise = Promise.promise();
|
||||
String query = QueryBuilder.update(file).build();
|
||||
|
||||
db.executeOne(query, FileEntity.class,
|
||||
promise::complete,
|
||||
promise::fail
|
||||
);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> exists(Integer id) {
|
||||
Promise<Boolean> promise = Promise.promise();
|
||||
String query = QueryBuilder
|
||||
.select(FileEntity.class)
|
||||
.where(Map.of("file_id", id.toString()))
|
||||
.build();
|
||||
|
||||
db.executeOne(query, FileEntity.class,
|
||||
result -> promise.complete(result != null),
|
||||
promise::fail
|
||||
);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> delete(Integer id) {
|
||||
Promise<Boolean> promise = Promise.promise();
|
||||
FileEntity file = new FileEntity();
|
||||
file.setFile_id(id);
|
||||
|
||||
String query = QueryBuilder.delete(file).build();
|
||||
|
||||
db.executeOne(query, FileEntity.class,
|
||||
result -> promise.complete(result != null),
|
||||
promise::fail
|
||||
);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
package net.miarma.api.backlib.core.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.sqlclient.Pool;
|
||||
import net.miarma.api.backlib.core.entities.UserEntity;
|
||||
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 UserDAO implements DataAccessObject<UserEntity, Integer> {
|
||||
|
||||
private final DatabaseManager db;
|
||||
|
||||
public UserDAO(Pool pool) {
|
||||
this.db = DatabaseManager.getInstance(pool);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<List<UserEntity>> getAll() {
|
||||
return getAll(new QueryParams(Map.of(), new QueryFilters()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<UserEntity> getById(Integer id) {
|
||||
Promise<UserEntity> promise = Promise.promise();
|
||||
String query = QueryBuilder
|
||||
.select(UserEntity.class)
|
||||
.where(Map.of("user_id", id.toString()))
|
||||
.build();
|
||||
|
||||
db.executeOne(query, UserEntity.class,
|
||||
promise::complete,
|
||||
promise::fail
|
||||
);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
public Future<List<UserEntity>> getAll(QueryParams params) {
|
||||
Promise<List<UserEntity>> promise = Promise.promise();
|
||||
String query = QueryBuilder
|
||||
.select(UserEntity.class)
|
||||
.where(params.getFilters())
|
||||
.orderBy(params.getQueryFilters().getSort(), params.getQueryFilters().getOrder())
|
||||
.limit(params.getQueryFilters().getLimit())
|
||||
.offset(params.getQueryFilters().getOffset())
|
||||
.build();
|
||||
|
||||
db.execute(query, UserEntity.class,
|
||||
list -> promise.complete(list.isEmpty() ? List.of() : list),
|
||||
promise::fail
|
||||
);
|
||||
|
||||
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
|
||||
public Future<UserEntity> insert(UserEntity user) {
|
||||
Promise<UserEntity> promise = Promise.promise();
|
||||
String query = QueryBuilder.insert(user).build();
|
||||
|
||||
db.executeOne(query, UserEntity.class,
|
||||
promise::complete,
|
||||
promise::fail
|
||||
);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<UserEntity> upsert(UserEntity userEntity, String... conflictKeys) {
|
||||
Promise<UserEntity> promise = Promise.promise();
|
||||
String query = QueryBuilder.upsert(userEntity, conflictKeys).build();
|
||||
|
||||
db.executeOne(query, UserEntity.class,
|
||||
promise::complete,
|
||||
promise::fail
|
||||
);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<UserEntity> update(UserEntity user) {
|
||||
Promise<UserEntity> promise = Promise.promise();
|
||||
String query = QueryBuilder.update(user).build();
|
||||
|
||||
db.executeOne(query, UserEntity.class,
|
||||
_ -> promise.complete(user),
|
||||
promise::fail
|
||||
);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> delete(Integer id) {
|
||||
Promise<Boolean> promise = Promise.promise();
|
||||
UserEntity user = new UserEntity();
|
||||
user.setUser_id(id);
|
||||
|
||||
String query = QueryBuilder.delete(user).build();
|
||||
|
||||
db.executeOne(query, UserEntity.class,
|
||||
result -> promise.complete(result != null),
|
||||
promise::fail
|
||||
);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> exists(Integer id) {
|
||||
Promise<Boolean> promise = Promise.promise();
|
||||
String query = QueryBuilder
|
||||
.select(UserEntity.class)
|
||||
.where(Map.of("user_id", id.toString()))
|
||||
.build();
|
||||
|
||||
db.executeOne(query, UserEntity.class,
|
||||
result -> promise.complete(result != null),
|
||||
promise::fail
|
||||
);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package net.miarma.api.backlib.core.handlers;
|
||||
|
||||
import io.vertx.core.buffer.Buffer;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.FileUpload;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import io.vertx.sqlclient.Pool;
|
||||
import net.miarma.api.backlib.Constants;
|
||||
import net.miarma.api.backlib.Constants.CoreFileContext;
|
||||
import net.miarma.api.backlib.http.ApiStatus;
|
||||
import net.miarma.api.backlib.http.QueryParams;
|
||||
import net.miarma.api.backlib.core.entities.FileEntity;
|
||||
import net.miarma.api.backlib.core.services.FileService;
|
||||
import net.miarma.api.backlib.util.JsonUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class FileDataHandler {
|
||||
|
||||
private final FileService fileService;
|
||||
|
||||
public FileDataHandler(Pool pool) {
|
||||
this.fileService = new FileService(pool);
|
||||
}
|
||||
|
||||
public void getAll(RoutingContext ctx) {
|
||||
QueryParams params = QueryParams.from(ctx);
|
||||
|
||||
fileService.getAll(params)
|
||||
.onSuccess(files -> JsonUtil.sendJson(ctx, ApiStatus.OK, files))
|
||||
.onFailure(err -> JsonUtil.sendJson(ctx, ApiStatus.fromException(err), null, err.getMessage()));
|
||||
}
|
||||
|
||||
public void getById(RoutingContext ctx) {
|
||||
Integer fileId = Integer.parseInt(ctx.pathParam("file_id"));
|
||||
|
||||
fileService.getById(fileId)
|
||||
.onSuccess(file -> JsonUtil.sendJson(ctx, ApiStatus.OK, file))
|
||||
.onFailure(err -> JsonUtil.sendJson(ctx, ApiStatus.fromException(err), null, err.getMessage()));
|
||||
}
|
||||
|
||||
public void create(RoutingContext ctx) {
|
||||
try {
|
||||
String fileName = ctx.request().getFormAttribute("file_name");
|
||||
String mimeType = ctx.request().getFormAttribute("mime_type");
|
||||
int uploadedBy = Integer.parseInt(ctx.request().getFormAttribute("uploaded_by"));
|
||||
int contextValue = Integer.parseInt(ctx.request().getFormAttribute("context"));
|
||||
|
||||
FileUpload upload = ctx.fileUploads().stream()
|
||||
.filter(f -> f.name().equals("file"))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Archivo no encontrado"));
|
||||
|
||||
Buffer buffer = ctx.vertx().fileSystem().readFileBlocking(upload.uploadedFileName());
|
||||
byte[] fileBinary = buffer.getBytes();
|
||||
|
||||
FileEntity file = new FileEntity();
|
||||
file.setFile_name(fileName);
|
||||
file.setMime_type(mimeType);
|
||||
file.setUploaded_by(uploadedBy);
|
||||
file.setContext(CoreFileContext.fromInt(contextValue));
|
||||
|
||||
fileService.create(file, fileBinary)
|
||||
.onSuccess(result -> JsonUtil.sendJson(ctx, ApiStatus.CREATED, result))
|
||||
.onFailure(err -> JsonUtil.sendJson(ctx, ApiStatus.fromException(err), null, err.getMessage()));
|
||||
} catch (Exception e) {
|
||||
Constants.LOGGER.error(e.getMessage(), e);
|
||||
JsonUtil.sendJson(ctx, ApiStatus.INTERNAL_SERVER_ERROR, null, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void update(RoutingContext ctx) {
|
||||
FileEntity file = Constants.GSON.fromJson(ctx.body().asString(), FileEntity.class);
|
||||
|
||||
fileService.update(file)
|
||||
.onSuccess(result -> JsonUtil.sendJson(ctx, ApiStatus.OK, result))
|
||||
.onFailure(err -> JsonUtil.sendJson(ctx, ApiStatus.fromException(err), null, err.getMessage()));
|
||||
}
|
||||
|
||||
public void delete(RoutingContext ctx) {
|
||||
Integer fileId = Integer.parseInt(ctx.pathParam("file_id"));
|
||||
JsonObject body = ctx.body().asJsonObject();
|
||||
String filePath = body.getString("file_path");
|
||||
|
||||
try {
|
||||
Files.deleteIfExists(Paths.get(filePath));
|
||||
} catch (IOException e) {
|
||||
Constants.LOGGER.error(e.getMessage(), e);
|
||||
JsonUtil.sendJson(ctx, ApiStatus.INTERNAL_SERVER_ERROR, null, e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
fileService.delete(fileId)
|
||||
.onSuccess(result -> JsonUtil.sendJson(ctx, ApiStatus.NO_CONTENT, null))
|
||||
.onFailure(err -> JsonUtil.sendJson(ctx, ApiStatus.fromException(err), null, err.getMessage()));
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package net.miarma.api.backlib.core.handlers;
|
||||
|
||||
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.JsonUtil;
|
||||
|
||||
public class FileLogicHandler {
|
||||
|
||||
private final Vertx vertx;
|
||||
|
||||
public FileLogicHandler(Vertx vertx) {
|
||||
this.vertx = vertx;
|
||||
}
|
||||
|
||||
private boolean validateAuth(RoutingContext ctx, JsonObject request) {
|
||||
String authHeader = ctx.request().getHeader("Authorization");
|
||||
|
||||
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
||||
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, null, "Unauthorized");
|
||||
return false;
|
||||
}
|
||||
|
||||
String token = authHeader.substring(7);
|
||||
int userId = JWTManager.getInstance().getUserId(token);
|
||||
|
||||
if (userId <= 0) {
|
||||
JsonUtil.sendJson(ctx, ApiStatus.UNAUTHORIZED, null, "Invalid token");
|
||||
return false;
|
||||
}
|
||||
|
||||
request.put("userId", userId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void getUserFiles(RoutingContext ctx) {
|
||||
JsonObject request = new JsonObject().put("action", "getUserFiles");
|
||||
if (!validateAuth(ctx, request)) return;
|
||||
|
||||
vertx.eventBus().request(Constants.CORE_EVENT_BUS, request, ar -> {
|
||||
if (ar.succeeded()) {
|
||||
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
|
||||
} else {
|
||||
JsonUtil.sendJson(ctx, ApiStatus.NOT_FOUND, null, "The user has no files");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void downloadFile(RoutingContext ctx) {
|
||||
JsonObject request = new JsonObject()
|
||||
.put("action", "downloadFile")
|
||||
.put("fileId", Integer.parseInt(ctx.pathParam("file_id")));
|
||||
|
||||
if (!validateAuth(ctx, request)) return;
|
||||
|
||||
vertx.eventBus().request(Constants.CORE_EVENT_BUS, request, ar -> {
|
||||
if (ar.succeeded()) {
|
||||
JsonUtil.sendJson(ctx, ApiStatus.OK, ar.result().body());
|
||||
} else {
|
||||
JsonUtil.sendJson(ctx, ApiStatus.NOT_FOUND, null, "Error downloading file");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package net.miarma.api.backlib.core.handlers;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
import net.miarma.api.backlib.http.ApiStatus;
|
||||
import net.miarma.api.backlib.util.JsonUtil;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class ScreenshotHandler {
|
||||
|
||||
private final WebClient webClient;
|
||||
|
||||
public ScreenshotHandler(Vertx vertx) {
|
||||
this.webClient = WebClient.create(vertx);
|
||||
}
|
||||
|
||||
public void getScreenshot(RoutingContext ctx) {
|
||||
String url = ctx.request().getParam("url");
|
||||
|
||||
if (url == null || url.isEmpty()) {
|
||||
ctx.response().setStatusCode(400).end("URL parameter is required");
|
||||
return;
|
||||
}
|
||||
|
||||
String encodedUrl = URLEncoder.encode(url, StandardCharsets.UTF_8);
|
||||
String microserviceUrl = "http://screenshoter:7000/screenshot?url=" + encodedUrl;
|
||||
|
||||
webClient.getAbs(microserviceUrl)
|
||||
.send(ar -> {
|
||||
if (ar.succeeded()) {
|
||||
ctx.response()
|
||||
.putHeader("Content-Type", "image/png")
|
||||
.end(ar.result().body());
|
||||
} else {
|
||||
JsonUtil.sendJson(ctx, ApiStatus.INTERNAL_SERVER_ERROR, null, "Could not generate the screenshot");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package net.miarma.api.backlib.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.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
||||
public class FileService {
|
||||
|
||||
private final FileDAO fileDAO;
|
||||
private final FileValidator fileValidator;
|
||||
|
||||
public FileService(Pool pool) {
|
||||
this.fileDAO = new FileDAO(pool);
|
||||
this.fileValidator = new FileValidator();
|
||||
}
|
||||
|
||||
public Future<List<FileEntity>> getAll(QueryParams params) {
|
||||
return fileDAO.getAll(params);
|
||||
}
|
||||
|
||||
public Future<FileEntity> getById(Integer id) {
|
||||
return fileDAO.getById(id).compose(file -> {
|
||||
if (file == null) {
|
||||
return Future.failedFuture(new NotFoundException("File not found with id: " + id));
|
||||
}
|
||||
return Future.succeededFuture(file);
|
||||
});
|
||||
}
|
||||
|
||||
public Future<List<FileEntity>> getUserFiles(Integer userId) {
|
||||
return fileDAO.getUserFiles(userId);
|
||||
}
|
||||
|
||||
public Future<FileEntity> create(FileEntity file, byte[] fileBinary) {
|
||||
return fileValidator.validate(file, fileBinary.length).compose(validation -> {
|
||||
if (!validation.isValid()) {
|
||||
return Future.failedFuture(new ValidationException(Constants.GSON.toJson(validation.getErrors())));
|
||||
}
|
||||
|
||||
String dir = ConfigManager.getInstance()
|
||||
.getFilesDir(file.getContext().toCtxString());
|
||||
|
||||
String pathString = dir + file.getFile_name();
|
||||
Path filePath = Paths.get(dir + file.getFile_name());
|
||||
file.setFile_path(ConfigManager.getOS() == OSType.WINDOWS ?
|
||||
pathString.replace("\\", "\\\\") : pathString);
|
||||
|
||||
try {
|
||||
Files.write(filePath, fileBinary);
|
||||
} catch (IOException e) {
|
||||
Constants.LOGGER.error("Error writing file to disk: ", e);
|
||||
return Future.failedFuture(e);
|
||||
}
|
||||
|
||||
return fileDAO.insert(file);
|
||||
});
|
||||
}
|
||||
|
||||
public Future<FileEntity> downloadFile(Integer fileId) {
|
||||
return getById(fileId);
|
||||
}
|
||||
|
||||
public Future<FileEntity> update(FileEntity file) {
|
||||
return fileValidator.validate(file).compose(validation -> {
|
||||
if (!validation.isValid()) {
|
||||
return Future.failedFuture(new ValidationException(Constants.GSON.toJson(validation.getErrors())));
|
||||
}
|
||||
|
||||
return fileDAO.update(file);
|
||||
});
|
||||
}
|
||||
|
||||
public Future<FileEntity> upsert(FileEntity file) {
|
||||
return fileValidator.validate(file).compose(validation -> {
|
||||
if (!validation.isValid()) {
|
||||
return Future.failedFuture(new ValidationException(Constants.GSON.toJson(validation.getErrors())));
|
||||
}
|
||||
|
||||
return fileDAO.upsert(file, "file_id");
|
||||
});
|
||||
}
|
||||
|
||||
public Future<Boolean> delete(Integer fileId) {
|
||||
return getById(fileId).compose(file -> {
|
||||
String dir = ConfigManager.getInstance()
|
||||
.getFilesDir(file.getContext().toCtxString());
|
||||
|
||||
String filePath = dir + file.getFile_name();
|
||||
Path path = Paths.get(filePath);
|
||||
|
||||
try {
|
||||
Files.deleteIfExists(path);
|
||||
} catch (IOException e) {
|
||||
Constants.LOGGER.error("Error deleting file from disk: ", e);
|
||||
return Future.failedFuture(e);
|
||||
}
|
||||
|
||||
return fileDAO.delete(fileId).compose(deleted -> {
|
||||
if (!deleted) {
|
||||
return Future.failedFuture(new NotFoundException("File not found with id: " + fileId));
|
||||
}
|
||||
return Future.succeededFuture(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public Future<Boolean> exists(Integer fileId) {
|
||||
return fileDAO.exists(fileId);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
package net.miarma.api.backlib.db;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import io.vertx.core.json.JsonObject;
|
||||
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 java.lang.reflect.Field;
|
||||
import net.miarma.api.backlib.interfaces.IValuableEnum;
|
||||
import net.miarma.api.backlib.log.LoggerProvider;
|
||||
|
||||
/**
|
||||
* Clase base para todas las entidades persistentes del sistema.
|
||||
@@ -24,6 +26,7 @@ import java.lang.reflect.Field;
|
||||
* @author José Manuel Amador Gallardo
|
||||
*/
|
||||
public abstract class AbstractEntity {
|
||||
private final Logger LOGGER = LoggerProvider.getLogger();
|
||||
|
||||
/**
|
||||
* Constructor por defecto. Requerido para instanciación sin datos.
|
||||
@@ -86,7 +89,7 @@ public abstract class AbstractEntity {
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
Constants.LOGGER.error("Type not supported yet: {} for field {}", type.getName(), name);
|
||||
LOGGER.error("Type not supported yet: {} for field {}", type.getName(), name);
|
||||
yield null;
|
||||
}
|
||||
};
|
||||
@@ -95,7 +98,7 @@ public abstract class AbstractEntity {
|
||||
|
||||
field.set(this, value);
|
||||
} catch (Exception e) {
|
||||
Constants.LOGGER.error("Error populating field {}: {}", field.getName(), e.getMessage());
|
||||
LOGGER.error("Error populating field {}: {}", field.getName(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,7 +106,7 @@ public abstract class AbstractEntity {
|
||||
/**
|
||||
* 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>
|
||||
* <p>Si un campo implementa {@link IValuableEnum}, se usará su valor en lugar del nombre del enum.</p>
|
||||
*
|
||||
* @return Representación JSON de esta entidad.
|
||||
*/
|
||||
@@ -119,13 +122,13 @@ public abstract class AbstractEntity {
|
||||
try {
|
||||
Object value = field.get(this);
|
||||
|
||||
if (value instanceof ValuableEnum ve) {
|
||||
if (value instanceof IValuableEnum ve) {
|
||||
json.put(field.getName(), ve.getValue());
|
||||
} else {
|
||||
json.put(field.getName(), value);
|
||||
}
|
||||
} 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();
|
||||
@@ -150,7 +153,7 @@ public abstract class AbstractEntity {
|
||||
try {
|
||||
sb.append(field.getName()).append("= ").append(field.get(this)).append(", ");
|
||||
} catch (IllegalAccessException e) {
|
||||
Constants.LOGGER.error("Error stringing field {}: {}", field.getName(), e.getMessage());
|
||||
LOGGER.error("Error stringing field {}: {}", field.getName(), e.getMessage());
|
||||
}
|
||||
}
|
||||
sb.append("]");
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
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.Handler;
|
||||
import io.vertx.sqlclient.Pool;
|
||||
import io.vertx.sqlclient.Row;
|
||||
import io.vertx.sqlclient.RowSet;
|
||||
import net.miarma.api.backlib.Constants;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.miarma.api.backlib.log.LoggerProvider;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
private final Logger LOGGER = LoggerProvider.getLogger();
|
||||
private static DatabaseManager instance;
|
||||
private final Pool pool;
|
||||
|
||||
@@ -86,7 +89,7 @@ public class DatabaseManager {
|
||||
results.add(constructor.newInstance(row));
|
||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException
|
||||
| InvocationTargetException e) {
|
||||
Constants.LOGGER.error("Error instantiating class: {}", e.getMessage());
|
||||
LOGGER.error("Error instantiating class: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
return results;
|
||||
@@ -116,7 +119,7 @@ public class DatabaseManager {
|
||||
Constructor<T> constructor = clazz.getConstructor(Row.class);
|
||||
return constructor.newInstance(row);
|
||||
} catch (Exception e) {
|
||||
Constants.LOGGER.error("Error instantiating class: {}", e.getMessage());
|
||||
LOGGER.error("Error instantiating class: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
return null; // Si no hay filas
|
||||
|
||||
@@ -4,7 +4,7 @@ import io.vertx.core.Vertx;
|
||||
import io.vertx.mysqlclient.MySQLConnectOptions;
|
||||
import io.vertx.sqlclient.Pool;
|
||||
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.
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
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.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
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.stream.Collectors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
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}.
|
||||
@@ -21,6 +31,7 @@ import java.util.stream.Collectors;
|
||||
* @author José Manuel Amador Gallardo
|
||||
*/
|
||||
public class QueryBuilder {
|
||||
private final static Logger LOGGER = LoggerProvider.getLogger();
|
||||
private final StringBuilder query;
|
||||
private String sort;
|
||||
private String order;
|
||||
@@ -141,7 +152,7 @@ public class QueryBuilder {
|
||||
String value = entry.getValue();
|
||||
|
||||
if (!validFields.contains(key)) {
|
||||
Constants.LOGGER.warn("[QueryBuilder] Ignorando campo invalido en WHERE: {}", key);
|
||||
LOGGER.warn("[QueryBuilder] Ignorando campo invalido en WHERE: {}", key);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -186,7 +197,7 @@ public class QueryBuilder {
|
||||
if (fieldValue != null) {
|
||||
String key = field.getName();
|
||||
if (!validFields.contains(key)) {
|
||||
Constants.LOGGER.warn("[QueryBuilder] Ignorando campo invalido en WHERE: {}", key);
|
||||
LOGGER.warn("[QueryBuilder] Ignorando campo invalido en WHERE: {}", key);
|
||||
continue;
|
||||
}
|
||||
Object value = extractValue(fieldValue);
|
||||
@@ -197,7 +208,7 @@ public class QueryBuilder {
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
Constants.LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
|
||||
LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
this.query.append(joiner).append(" ");
|
||||
@@ -239,7 +250,7 @@ public class QueryBuilder {
|
||||
values.add("NULL");
|
||||
}
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
Constants.LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
|
||||
LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
qb.query.append(columns).append(") ");
|
||||
@@ -289,7 +300,7 @@ public class QueryBuilder {
|
||||
setJoiner.add(fieldName + " = " + (value instanceof String
|
||||
|| value instanceof LocalDateTime ? "'" + value + "'" : value));
|
||||
} catch (Exception e) {
|
||||
Constants.LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
|
||||
LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,7 +354,7 @@ public class QueryBuilder {
|
||||
setJoiner.add(fieldName + " = " + (value instanceof String || value instanceof LocalDateTime ? "'" + value + "'" : value));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Constants.LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
|
||||
LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,7 +405,7 @@ public class QueryBuilder {
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Constants.LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
|
||||
LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,7 +447,7 @@ public class QueryBuilder {
|
||||
|| value instanceof LocalDateTime ? "'" + value + "'" : value.toString()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Constants.LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
|
||||
LOGGER.error("(REFLECTION) Error reading field: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,7 +471,7 @@ public class QueryBuilder {
|
||||
.anyMatch(f -> f.equals(c));
|
||||
|
||||
if (!isValid) {
|
||||
Constants.LOGGER.warn("[QueryBuilder] Ignorando campo invalido en ORDER BY: {}", c);
|
||||
LOGGER.warn("[QueryBuilder] Ignorando campo invalido en ORDER BY: {}", c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {}
|
||||
}
|
||||
@@ -4,7 +4,8 @@ import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
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.util.Arrays;
|
||||
@@ -15,15 +16,15 @@ import java.util.Arrays;
|
||||
*
|
||||
* @author José Manuel Amador Gallardo
|
||||
*/
|
||||
public class ValuableEnumDeserializer implements JsonDeserializer<ValuableEnum> {
|
||||
public class ValuableEnumDeserializer implements JsonDeserializer<IValuableEnum> {
|
||||
|
||||
@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;
|
||||
int value = json.getAsInt();
|
||||
|
||||
return (ValuableEnum) Arrays.stream(enumClass.getEnumConstants())
|
||||
.filter(e -> ((ValuableEnum) e).getValue() == value)
|
||||
return (IValuableEnum) Arrays.stream(enumClass.getEnumConstants())
|
||||
.filter(e -> ((IValuableEnum) e).getValue() == value)
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new JsonParseException("Invalid enum value: " + value));
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import net.miarma.api.backlib.ValuableEnum;
|
||||
|
||||
import net.miarma.api.backlib.interfaces.IValuableEnum;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
@@ -14,10 +15,10 @@ import java.lang.reflect.Type;
|
||||
*
|
||||
* @author José Manuel Amador Gallardo
|
||||
*/
|
||||
public class ValuableEnumTypeAdapter implements JsonSerializer<ValuableEnum> {
|
||||
public class ValuableEnumTypeAdapter implements JsonSerializer<IValuableEnum> {
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(ValuableEnum src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
public JsonElement serialize(IValuableEnum src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package net.miarma.api.backlib.interfaces;
|
||||
|
||||
import net.miarma.api.backlib.ValuableEnum;
|
||||
|
||||
public interface IUserRole extends ValuableEnum {
|
||||
public interface IUserRole extends IValuableEnum {
|
||||
String name();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
* @author José Manuel Amador Gallardo
|
||||
*/
|
||||
public interface ValuableEnum {
|
||||
public interface IValuableEnum {
|
||||
int getValue();
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package net.miarma.api.backlib;
|
||||
package net.miarma.api.backlib.log;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package net.miarma.api.backlib;
|
||||
package net.miarma.api.backlib.log;
|
||||
|
||||
public record LogEntry(int order, String message) {
|
||||
public static LogEntry of(int order, String message) {
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.miarma.api.backlib.middlewares;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
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> {
|
||||
|
||||
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);
|
||||
|
||||
public Handler<RoutingContext> check(R... allowedRoles) {
|
||||
@@ -29,7 +30,7 @@ public abstract class AbstractAuthGuard<U, R extends Enum<R> & IUserRole> {
|
||||
return;
|
||||
}
|
||||
|
||||
int userId = JWTManager.getInstance().extractUserId(token);
|
||||
UUID userId = JWTManager.getInstance().extractUserId(token);
|
||||
String roleStr = JWTManager.getInstance().extractRole(token);
|
||||
|
||||
R role;
|
||||
|
||||
@@ -1,83 +1,87 @@
|
||||
package net.miarma.api.backlib.security;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import java.util.UUID;
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||
import com.auth0.jwt.interfaces.JWTVerifier;
|
||||
|
||||
import net.miarma.api.backlib.ConfigManager;
|
||||
import net.miarma.api.backlib.Constants;
|
||||
import net.miarma.api.backlib.config.ConfigManager;
|
||||
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 {
|
||||
|
||||
private final ConfigManager config = ConfigManager.getInstance();
|
||||
private final ConfigManager config = ConfigManager.getInstance();
|
||||
private final Algorithm algorithm;
|
||||
private final JWTVerifier verifier;
|
||||
private static JWTManager instance;
|
||||
|
||||
private JWTManager() {
|
||||
this.algorithm = Algorithm.HMAC256(config.getStringProperty("jwt.secret"));
|
||||
this.verifier = JWT.require(algorithm).build();
|
||||
}
|
||||
this.algorithm = Algorithm.HMAC256(config.getStringProperty("jwt.secret"));
|
||||
this.verifier = JWT.require(algorithm).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene la instancia única de JWTManager.
|
||||
*
|
||||
* @return La instancia única de JWTManager.
|
||||
*/
|
||||
public static synchronized JWTManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new JWTManager();
|
||||
}
|
||||
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);
|
||||
if (instance == null) {
|
||||
instance = new JWTManager();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodifica un token JWT sin verificar su firma.
|
||||
*
|
||||
* @param token El token JWT a decodificar.
|
||||
* @return Un objeto DecodedJWT que contiene la información del token.
|
||||
* Genera un token JWT usando UUID para el usuario.
|
||||
*/
|
||||
public DecodedJWT decodeWithoutVerification(String token) {
|
||||
return JWT.decode(token);
|
||||
public String generateToken(
|
||||
String userName,
|
||||
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(userName)
|
||||
.withClaim("userId", userId.toString())
|
||||
.withClaim("serviceId", serviceId)
|
||||
.withClaim("role", role.name())
|
||||
.withIssuedAt(new Date())
|
||||
.withExpiresAt(new Date(System.currentTimeMillis() + expiration))
|
||||
.sign(algorithm);
|
||||
}
|
||||
|
||||
public UUID extractUserId(String token) {
|
||||
try {
|
||||
DecodedJWT jwt = verifier.verify(token);
|
||||
String uuidStr = jwt.getClaim("userId").asString();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica la validez de un token JWT.
|
||||
*
|
||||
* @param token El token JWT a verificar.
|
||||
* @return true si el token es válido, false en caso contrario.
|
||||
*/
|
||||
public boolean isValid(String token) {
|
||||
try {
|
||||
verifier.verify(token);
|
||||
@@ -86,77 +90,4 @@ public class JWTManager {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package net.miarma.api.backlib.security;
|
||||
|
||||
import net.miarma.api.backlib.ConfigManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -10,6 +8,8 @@ import java.security.SecureRandom;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.miarma.api.backlib.config.ConfigManager;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
@@ -3,7 +3,7 @@ package net.miarma.api.backlib.util;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
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.ApiStatus;
|
||||
|
||||
@@ -26,7 +26,7 @@ public class JsonUtil {
|
||||
.put("data", data);
|
||||
ctx.response().end(response.encode());
|
||||
} else {
|
||||
ctx.response().end(Constants.GSON.toJson(new ApiResponse<>(status, message, data)));
|
||||
ctx.response().end(GsonProvider.get().toJson(new ApiResponse<>(status, message, data)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package net.miarma.api.backlib.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
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.
|
||||
* @author José Manuel Amador Gallardo
|
||||
*/
|
||||
public class RouterUtil {
|
||||
|
||||
private final static Logger LOGGER = LoggerProvider.getLogger();
|
||||
|
||||
public static void attachLogger(Router router) {
|
||||
router.route().handler(ctx -> {
|
||||
long startTime = System.currentTimeMillis();
|
||||
@@ -46,7 +49,7 @@ public class RouterUtil {
|
||||
duration
|
||||
);
|
||||
|
||||
Constants.LOGGER.info(log);
|
||||
LOGGER.info(log);
|
||||
});
|
||||
|
||||
ctx.next();
|
||||
|
||||
Reference in New Issue
Block a user