Refactored many things and removed unnecessary things like IDEA files and stuff
8
.idea/.gitignore
generated
vendored
@@ -1,8 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
13
.idea/compiler.xml
generated
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile name="Maven default annotation processors profile" enabled="true">
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<module name="contaminus" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
</project>
|
||||
20
.idea/jarRepositories.xml
generated
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Central Repository" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
||||
14
.idea/misc.xml
generated
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/backend/vertx/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_23" project-jdk-name="23" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/classes" />
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/ContaminUS.iml" filepath="$PROJECT_DIR$/ContaminUS.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
11
.project
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>ContaminUS</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<output url="file://$MODULE_DIR$/bin" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="jdk" jdkName="23" jdkType="JavaSDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,53 @@
|
||||
package net.miarma.contaminus.common;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import io.vertx.core.impl.logging.Logger;
|
||||
import io.vertx.core.impl.logging.LoggerFactory;
|
||||
|
||||
public class Constants {
|
||||
public static final String APP_NAME = "ContaminUS";
|
||||
public static final String API_PREFIX = "/api/v1";
|
||||
public static final String HOME_DIR = System.getProperty("user.home") + File.separator;
|
||||
public static final String BASE_DIR = HOME_DIR +
|
||||
(SystemInfo.getOS() == OSType.WINDOWS ? ".contaminus" :
|
||||
SystemInfo.getOS() == OSType.LINUX ? ".config" + File.separator +
|
||||
"contaminus" : null);
|
||||
public static final String CONFIG_FILE = BASE_DIR + File.separator + "config.properties";
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger(APP_NAME);
|
||||
|
||||
public static final String GET_GROUPS = API_PREFIX + "/groups";
|
||||
public static final String GET_GROUP_BY_ID = API_PREFIX + "/groups/:groupId";
|
||||
public static final String GET_GROUP_DEVICES = API_PREFIX + "/groups/:groupId/devices";
|
||||
public static final String POST_GROUPS = API_PREFIX + "/groups";
|
||||
public static final String PUT_GROUP_BY_ID = API_PREFIX + "/groups/:groupId";
|
||||
|
||||
public static final String GET_DEVICES = API_PREFIX + "/devices";
|
||||
public static final String GET_DEVICE_BY_ID = API_PREFIX + "/devices/:deviceId";
|
||||
public static final String GET_DEVICE_SENSORS = API_PREFIX + "/devices/:deviceId/sensors";
|
||||
public static final String POST_DEVICES = API_PREFIX + "/devices";
|
||||
public static final String PUT_DEVICE_BY_ID = API_PREFIX + "/devices/:deviceId";
|
||||
|
||||
public static final String GET_SENSORS = API_PREFIX + "/sensors";
|
||||
public static final String GET_SENSOR_BY_ID = API_PREFIX + "/sensors/:sensorId";
|
||||
public static final String GET_SENSOR_VALUES = API_PREFIX + "/sensors/:sensorId/values";
|
||||
public static final String POST_SENSORS = API_PREFIX + "/sensors";
|
||||
public static final String PUT_SENSOR_BY_ID = API_PREFIX + "/sensors/:sensorId";
|
||||
|
||||
public static final String GET_ACTUATORS = API_PREFIX + "/actuators";
|
||||
public static final String GET_ACTUATOR_BY_ID = API_PREFIX + "/actuators/:actuatorId";
|
||||
public static final String POST_ACTUATORS = API_PREFIX + "/actuators";
|
||||
public static final String PUT_ACTUATOR_BY_ID = API_PREFIX + "/actuators/:actuatorId";
|
||||
|
||||
public static final String GET_GPS_VALUES = API_PREFIX + "/gps-values";
|
||||
public static final String GET_GPS_VALUE_BY_ID = API_PREFIX + "/gps-values/:valueId";
|
||||
public static final String POST_GPS_VALUES = API_PREFIX + "/gps-values";
|
||||
|
||||
public static final String GET_AIR_VALUES = API_PREFIX + "/air-values";
|
||||
public static final String GET_AIR_VALUE_BY_ID = API_PREFIX + "/air-values/:valueId";
|
||||
public static final String POST_AIR_VALUES = API_PREFIX + "/air-values";
|
||||
|
||||
private Constants() {
|
||||
throw new AssertionError("Utility class cannot be instantiated.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package net.miarma.contaminus.common;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
public class LocalDateTimeSerializer implements JsonSerializer<LocalDateTime>, JsonDeserializer<LocalDateTime> {
|
||||
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(LocalDateTime src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src.format(FORMATTER));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
return LocalDateTime.parse(json.getAsString(), FORMATTER);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,6 +42,21 @@ public class QueryBuilder {
|
||||
return qb;
|
||||
}
|
||||
|
||||
public static QueryBuilder update(String table) {
|
||||
QueryBuilder qb = new QueryBuilder();
|
||||
qb.query.append("UPDATE ").append(table).append(" ");
|
||||
return qb;
|
||||
}
|
||||
|
||||
public QueryBuilder set(String column, Object value) {
|
||||
if(value instanceof String) {
|
||||
query.append("SET ").append(column).append(" = '").append(value).append("' ");
|
||||
} else {
|
||||
query.append("SET ").append(column).append(" = ").append(value).append(" ");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryBuilder values(Object... values) {
|
||||
StringJoiner joiner = new StringJoiner(", ", "VALUES (", ")");
|
||||
for (Object value : values) {
|
||||
@@ -83,7 +98,7 @@ public class QueryBuilder {
|
||||
limit = limitParam.isPresent() ? "LIMIT " + limitParam.get() + " " : "";
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public String build() {
|
||||
if (!conditions.isEmpty()) {
|
||||
query.append("WHERE ");
|
||||
@@ -104,4 +119,6 @@ public class QueryBuilder {
|
||||
}
|
||||
return query.toString().trim() + ";";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
package net.miarma.contaminus.server;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import io.vertx.core.AbstractVerticle;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.eventbus.Message;
|
||||
import io.vertx.core.http.HttpMethod;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.Router;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import io.vertx.ext.web.handler.BodyHandler;
|
||||
import io.vertx.ext.web.handler.CorsHandler;
|
||||
import net.miarma.contaminus.common.Constants;
|
||||
import net.miarma.contaminus.common.Host;
|
||||
import net.miarma.contaminus.database.QueryBuilder;
|
||||
|
||||
public class ApiVerticle extends AbstractVerticle {
|
||||
|
||||
@Override
|
||||
public void start(Promise<Void> startPromise) {
|
||||
Constants.LOGGER.info("🟢 Iniciando ApiVerticle...");
|
||||
Router router = Router.router(vertx);
|
||||
|
||||
Set<HttpMethod> allowedMethods = new HashSet<>(Arrays.asList(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT)); // Por ejemplo
|
||||
Set<String> allowedHeaders = new HashSet<>(Arrays.asList("Content-Type", "Authorization"));
|
||||
|
||||
router.route().handler(CorsHandler.create()
|
||||
.addOrigin(Host.getOrigin())
|
||||
.allowCredentials(true)
|
||||
.allowedHeaders(allowedHeaders)
|
||||
.allowedMethods(allowedMethods));
|
||||
router.route().handler(BodyHandler.create());
|
||||
|
||||
// Group Routes
|
||||
router.route(HttpMethod.GET, Constants.GET_GROUPS).handler(this::getGroupsHandler);
|
||||
router.route(HttpMethod.GET, Constants.GET_GROUP_BY_ID).handler(this::getGroupByIdHandler);
|
||||
router.route(HttpMethod.GET, Constants.GET_GROUP_DEVICES).handler(this::getGroupDevicesHandler);
|
||||
router.route(HttpMethod.POST, Constants.POST_GROUPS).handler(this::postGroupHandler);
|
||||
router.route(HttpMethod.PUT, Constants.PUT_GROUP_BY_ID).handler(this::putGroupByIdHandler);
|
||||
|
||||
// Device Routes
|
||||
router.route(HttpMethod.GET, Constants.GET_DEVICES).handler(this::getDevicesHandler);
|
||||
router.route(HttpMethod.GET, Constants.GET_DEVICE_BY_ID).handler(this::getDeviceByIdHandler);
|
||||
router.route(HttpMethod.GET, Constants.GET_DEVICE_SENSORS).handler(this::getDeviceSensorsHandler);
|
||||
router.route(HttpMethod.POST, Constants.POST_DEVICES).handler(this::postDeviceHandler);
|
||||
router.route(HttpMethod.PUT, Constants.PUT_DEVICE_BY_ID).handler(this::putDeviceByIdHandler);
|
||||
|
||||
// Sensor Routes
|
||||
router.route(HttpMethod.GET, Constants.GET_SENSORS).handler(this::getSensorsHandler);
|
||||
router.route(HttpMethod.GET, Constants.GET_SENSOR_BY_ID).handler(this::getSensorByIdHandler);
|
||||
router.route(HttpMethod.GET, Constants.GET_SENSOR_VALUES).handler(this::getSensorValuesHandler);
|
||||
router.route(HttpMethod.POST, Constants.POST_SENSORS).handler(this::postSensorHandler);
|
||||
router.route(HttpMethod.PUT, Constants.PUT_SENSOR_BY_ID).handler(this::putSensorByIdHandler);
|
||||
|
||||
// Actuator Routes
|
||||
router.route(HttpMethod.GET, Constants.GET_ACTUATORS).handler(this::getActuatorsHandler);
|
||||
router.route(HttpMethod.GET, Constants.GET_ACTUATOR_BY_ID).handler(this::getActuatorByIdHandler);
|
||||
router.route(HttpMethod.POST, Constants.POST_ACTUATORS).handler(this::postActuatorHandler);
|
||||
router.route(HttpMethod.PUT, Constants.PUT_ACTUATOR_BY_ID).handler(this::putActuatorByIdHandler);
|
||||
|
||||
vertx.createHttpServer()
|
||||
.requestHandler(router)
|
||||
.listen(
|
||||
Host.getApiPort(),
|
||||
Host.getHost(),
|
||||
result -> {
|
||||
if (result.succeeded()) {
|
||||
Constants.LOGGER.info(String.format(
|
||||
"📡 ApiVerticle desplegado. (http://%s:%d)", Host.getHost(), Host.getApiPort()
|
||||
));
|
||||
startPromise.complete();
|
||||
} else {
|
||||
Constants.LOGGER.error("❌ Error al desplegar ApiVerticle", result.cause());
|
||||
startPromise.fail(result.cause());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendQuery(String query, RoutingContext context) {
|
||||
vertx.eventBus().request("db.query", query, req -> {
|
||||
if (req.succeeded()) {
|
||||
Message<Object> msg = req.result();
|
||||
JsonArray jsonArray = new JsonArray(msg.body().toString());
|
||||
context.json(jsonArray);
|
||||
} else {
|
||||
context.fail(500, req.cause());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Group Handlers
|
||||
private void getGroupsHandler(RoutingContext context) {
|
||||
String query = QueryBuilder.select("*").from("groups").build();
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
private void getGroupByIdHandler(RoutingContext context) {
|
||||
String groupId = context.request().getParam("groupId");
|
||||
String query = QueryBuilder
|
||||
.select("*")
|
||||
.from("groups")
|
||||
.where("groupId = ?", groupId)
|
||||
.build();
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
private void getGroupDevicesHandler(RoutingContext context) {
|
||||
String groupId = context.request().getParam("groupId");
|
||||
String query = QueryBuilder
|
||||
.select("*")
|
||||
.from("devices")
|
||||
.where("groupId = ?", groupId)
|
||||
.build();
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
private void postGroupHandler(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
|
||||
if(body == null) {
|
||||
context.fail(400, new IllegalArgumentException("Bad request"));
|
||||
return;
|
||||
}
|
||||
|
||||
String groupName = body.getString("groupName");
|
||||
|
||||
String query = QueryBuilder
|
||||
.insert("groups", "groupName")
|
||||
.values(groupName)
|
||||
.build();
|
||||
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
private void putGroupByIdHandler(RoutingContext context) {
|
||||
String groupId = context.request().getParam("groupId");
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
|
||||
if(body == null) {
|
||||
context.fail(400, new IllegalArgumentException("Bad request"));
|
||||
return;
|
||||
}
|
||||
|
||||
String groupName = body.getString("groupName");
|
||||
|
||||
String query = QueryBuilder
|
||||
.update("groups")
|
||||
.set("groupName", groupName)
|
||||
.where("groupId = ?", groupId)
|
||||
.build();
|
||||
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
// Device Handlers
|
||||
private void getDevicesHandler(RoutingContext context) {
|
||||
String query = QueryBuilder.select("*").from("devices").build();
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
private void getDeviceByIdHandler(RoutingContext context) {
|
||||
String deviceId = context.request().getParam("deviceId");
|
||||
String query = QueryBuilder
|
||||
.select("*")
|
||||
.from("devices")
|
||||
.where("deviceId = ?", deviceId)
|
||||
.build();
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
private void getDeviceSensorsHandler(RoutingContext context) {
|
||||
String deviceId = context.request().getParam("deviceId");
|
||||
String query = QueryBuilder
|
||||
.select("*")
|
||||
.from("sensors")
|
||||
.where("deviceId = ?", deviceId)
|
||||
.build();
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
private void postDeviceHandler(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
|
||||
if(body == null) {
|
||||
context.fail(400, new IllegalArgumentException("Bad request"));
|
||||
return;
|
||||
}
|
||||
|
||||
Integer groupId = body.getInteger("groupId");
|
||||
String deviceName = body.getString("groupName");
|
||||
|
||||
String query = QueryBuilder
|
||||
.insert("devices", "groupId", "deviceName")
|
||||
.values(groupId, deviceName)
|
||||
.build();
|
||||
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
private void putDeviceByIdHandler(RoutingContext context) {
|
||||
String deviceId = context.request().getParam("deviceId");
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
|
||||
if(body == null) {
|
||||
context.fail(400, new IllegalArgumentException("Bad request"));
|
||||
return;
|
||||
}
|
||||
|
||||
Integer groupId = body.getInteger("groupId");
|
||||
String deviceName = body.getString("deviceName");
|
||||
|
||||
String query = QueryBuilder
|
||||
.update("devices")
|
||||
.set("groupId", groupId)
|
||||
.set("deviceName", deviceName)
|
||||
.where("deviceId = ?", deviceId)
|
||||
.build();
|
||||
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
// Sensor Handlers
|
||||
private void getSensorsHandler(RoutingContext context) {
|
||||
String query = QueryBuilder.select("*").from("sensors").build();
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
private void getSensorByIdHandler(RoutingContext context) {
|
||||
String sensorId = context.request().getParam("sensorId");
|
||||
String query = QueryBuilder
|
||||
.select("*")
|
||||
.from("sensors")
|
||||
.where("sensorId = ?", sensorId)
|
||||
.build();
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
private void getSensorValuesHandler(RoutingContext context) {
|
||||
String sensorId = context.request().getParam("sensorId");
|
||||
String query = QueryBuilder
|
||||
.select("*")
|
||||
.from("v_sensor_values")
|
||||
.where("sensorId = ?", sensorId)
|
||||
.build();
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
private void postSensorHandler(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
|
||||
if(body == null) {
|
||||
context.fail(400, new IllegalArgumentException("Bad request"));
|
||||
return;
|
||||
}
|
||||
|
||||
Integer deviceId = body.getInteger("deviceId");
|
||||
String sensorType = body.getString("sensorType");
|
||||
String unit = body.getString("unit");
|
||||
Integer status = body.getInteger("status");
|
||||
|
||||
String query = QueryBuilder
|
||||
.insert("sensors", "deviceId", "sensorType", "unit", "status")
|
||||
.values(deviceId, sensorType, unit, status)
|
||||
.build();
|
||||
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
private void putSensorByIdHandler(RoutingContext context) {
|
||||
String sensorId = context.request().getParam("sensorId");
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
|
||||
if(body == null) {
|
||||
context.fail(400, new IllegalArgumentException("Bad request"));
|
||||
return;
|
||||
}
|
||||
|
||||
Integer deviceId = body.getInteger("deviceId");
|
||||
String sensorType = body.getString("sensorType");
|
||||
String unit = body.getString("unit");
|
||||
Integer status = body.getInteger("status");
|
||||
|
||||
String query = QueryBuilder
|
||||
.update("sensors")
|
||||
.set("deviceId", deviceId)
|
||||
.set("sensorType", sensorType)
|
||||
.set("unit", unit)
|
||||
.set("status", status)
|
||||
.where("sensorId = ?", sensorId)
|
||||
.build();
|
||||
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
// Actuator Handlers
|
||||
private void getActuatorsHandler(RoutingContext context) {
|
||||
String query = QueryBuilder.select("*").from("actuators").build();
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
private void getActuatorByIdHandler(RoutingContext context) {
|
||||
String actuatorId = context.request().getParam("actuatorId");
|
||||
|
||||
String query = QueryBuilder
|
||||
.select("*")
|
||||
.from("actuators")
|
||||
.where("actuatorId = ?", actuatorId)
|
||||
.build();
|
||||
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
private void postActuatorHandler(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
|
||||
if(body == null) {
|
||||
context.fail(400, new IllegalArgumentException("Bad request"));
|
||||
return;
|
||||
}
|
||||
|
||||
Integer deviceId = body.getInteger("deviceId");
|
||||
Integer status = body.getInteger("status");
|
||||
|
||||
String query = QueryBuilder
|
||||
.insert("actuators", "deviceId", "status")
|
||||
.values(deviceId, status)
|
||||
.build();
|
||||
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
private void putActuatorByIdHandler(RoutingContext context) {
|
||||
String actuatorId = context.request().getParam("actuatorId");
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
|
||||
if(body == null) {
|
||||
context.fail(400, new IllegalArgumentException("Bad request"));
|
||||
return;
|
||||
}
|
||||
|
||||
Integer deviceId = body.getInteger("deviceId");
|
||||
Integer status = body.getInteger("status");
|
||||
|
||||
String query = QueryBuilder
|
||||
.update("actuators")
|
||||
.set("deviceId", deviceId)
|
||||
.set("status", status)
|
||||
.where("actuatorId = ?", actuatorId)
|
||||
.build();
|
||||
|
||||
sendQuery(query, context);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package net.miarma.contaminus.server;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import io.vertx.core.AbstractVerticle;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.eventbus.EventBus;
|
||||
import io.vertx.core.eventbus.Message;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.jdbcclient.JDBCPool;
|
||||
import io.vertx.sqlclient.Row;
|
||||
import net.miarma.contaminus.common.Constants;
|
||||
import net.miarma.contaminus.common.LocalDateTimeSerializer;
|
||||
import net.miarma.contaminus.database.DatabaseManager;
|
||||
|
||||
public class DatabaseVerticle extends AbstractVerticle {
|
||||
private JDBCPool pool;
|
||||
private EventBus eventBus;
|
||||
private Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer())
|
||||
.create();;
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Override
|
||||
public void start(Promise<Void> startPromise) {
|
||||
Constants.LOGGER.info("🟢 Iniciando DatabaseVerticle...");
|
||||
|
||||
DatabaseManager dbManager = new DatabaseManager(vertx);
|
||||
pool = dbManager.getPool();
|
||||
eventBus = vertx.eventBus();
|
||||
|
||||
pool.query("SELECT 1")
|
||||
.execute()
|
||||
.onSuccess(_res -> {
|
||||
Constants.LOGGER.info("✅ Database connection ok");
|
||||
Constants.LOGGER.info("📡 DatabaseVerticle desplegado");
|
||||
startPromise.complete();
|
||||
})
|
||||
.onFailure(err -> {
|
||||
Constants.LOGGER.error("❌ Database connection failed");
|
||||
Constants.LOGGER.error("❌ Error al desplegar DatabaseVerticle", err);
|
||||
startPromise.fail(err);
|
||||
});
|
||||
|
||||
eventBus.consumer("db.query", this::handleDatabaseQuery);
|
||||
}
|
||||
|
||||
private void handleDatabaseQuery(Message<String> msg) {
|
||||
String query = msg.body();
|
||||
Constants.LOGGER.info("📥 Query: " + query);
|
||||
|
||||
if(query == null || query.isEmpty()) {
|
||||
msg.fail(400, "Empty query");
|
||||
return;
|
||||
}
|
||||
|
||||
if(query.startsWith("SELECT")) {
|
||||
handleSelectQuery(query, msg);
|
||||
} else if(query.startsWith("INSERT")) {
|
||||
handleInsertQuery(query, msg);
|
||||
} else if(query.startsWith("UPDATE")) {
|
||||
handleUpdateQuery(query, msg);
|
||||
} else {
|
||||
msg.fail(400, "Invalid operation");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSelectQuery(String query, Message<String> msg) {
|
||||
pool.query(query).execute()
|
||||
.onSuccess(res -> {
|
||||
List<Map<String, Object>> rowsList = new ArrayList<>();
|
||||
|
||||
for (Row row : res) {
|
||||
Map<String, Object> rowMap = new HashMap<>();
|
||||
for (int i = 0; i < row.size(); i++) {
|
||||
String columnName = res.columnsNames().get(i);
|
||||
Object columnValue = row.getValue(i);
|
||||
rowMap.put(columnName, columnValue);
|
||||
}
|
||||
rowsList.add(rowMap);
|
||||
}
|
||||
|
||||
String jsonResponse = gson.toJson(rowsList);
|
||||
msg.reply(jsonResponse);
|
||||
})
|
||||
.onFailure(err -> msg.fail(500, err.getMessage()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void handleInsertQuery(String query, Message<String> msg) {
|
||||
pool.query(query).execute()
|
||||
.onSuccess(_res -> {
|
||||
JsonObject response = new JsonObject();
|
||||
response.put("status", "success");
|
||||
String jsonResponse = gson.toJson(response);
|
||||
msg.reply(jsonResponse);
|
||||
})
|
||||
.onFailure(err -> msg.fail(500, err.getMessage()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void handleUpdateQuery(String query, Message<String> msg) {
|
||||
pool.query(query).execute()
|
||||
.onSuccess(_res -> {
|
||||
JsonObject response = new JsonObject();
|
||||
response.put("status", "updated");
|
||||
String jsonResponse = gson.toJson(response);
|
||||
msg.reply(jsonResponse);
|
||||
})
|
||||
.onFailure(err -> msg.fail(500, err.getMessage()));
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 412 KiB After Width: | Height: | Size: 412 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 412 KiB After Width: | Height: | Size: 412 KiB |
@@ -1,22 +0,0 @@
|
||||
package net.miarma.contaminus.common;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import io.vertx.core.impl.logging.Logger;
|
||||
import io.vertx.core.impl.logging.LoggerFactory;
|
||||
|
||||
public class Constants {
|
||||
public static final String APP_NAME = "ContaminUS";
|
||||
public static final String API_PREFIX = "/api/v1";
|
||||
public static final String HOME_DIR = System.getProperty("user.home") + File.separator;
|
||||
public static final String BASE_DIR = HOME_DIR +
|
||||
(SystemInfo.getOS() == OSType.WINDOWS ? ".contaminus" :
|
||||
SystemInfo.getOS() == OSType.LINUX ? ".config" + File.separator +
|
||||
"contaminus" : null);
|
||||
public static final String CONFIG_FILE = BASE_DIR + File.separator + "config.properties";
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger(APP_NAME);
|
||||
|
||||
private Constants() {
|
||||
throw new AssertionError("Utility class cannot be instantiated.");
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package net.miarma.contaminus.database.models;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
public class Sensor {
|
||||
private int id;
|
||||
private String sensorType;
|
||||
private float value;
|
||||
private float lat;
|
||||
private float lon;
|
||||
private Timestamp timestamp;
|
||||
|
||||
public Sensor() {}
|
||||
|
||||
public Sensor(int id, String sensorType, float value, float lat, float lon, Timestamp timestamp) {
|
||||
this.id = id;
|
||||
this.sensorType = sensorType;
|
||||
this.value = value;
|
||||
this.lat = lat;
|
||||
this.lon = lon;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getSensorType() {
|
||||
return sensorType;
|
||||
}
|
||||
|
||||
public void setSensorType(String sensorType) {
|
||||
this.sensorType = sensorType;
|
||||
}
|
||||
|
||||
public float getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(float value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public float getLat() {
|
||||
return lat;
|
||||
}
|
||||
|
||||
public void setLat(float lat) {
|
||||
this.lat = lat;
|
||||
}
|
||||
|
||||
public float getLon() {
|
||||
return lon;
|
||||
}
|
||||
|
||||
public void setLon(float lon) {
|
||||
this.lon = lon;
|
||||
}
|
||||
|
||||
public Timestamp getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(Timestamp timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Sensor{" +
|
||||
"id=" + id +
|
||||
", sensorType='" + sensorType + '\'' +
|
||||
", value=" + value +
|
||||
", lat=" + lat +
|
||||
", lon=" + lon +
|
||||
", timestamp=" + timestamp +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
package net.miarma.contaminus.server;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import io.vertx.core.AbstractVerticle;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.eventbus.Message;
|
||||
import io.vertx.core.http.HttpMethod;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.Router;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import io.vertx.ext.web.handler.BodyHandler;
|
||||
import io.vertx.ext.web.handler.CorsHandler;
|
||||
import net.miarma.contaminus.common.Constants;
|
||||
import net.miarma.contaminus.common.Host;
|
||||
import net.miarma.contaminus.database.QueryBuilder;
|
||||
|
||||
public class ApiVerticle extends AbstractVerticle {
|
||||
|
||||
@Override
|
||||
public void start(Promise<Void> startPromise) {
|
||||
Constants.LOGGER.info("🟢 Iniciando ApiVerticle...");
|
||||
Router router = Router.router(vertx);
|
||||
|
||||
Set<HttpMethod> allowedMethods = new HashSet<>(Arrays.asList(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT)); // Por ejemplo
|
||||
Set<String> allowedHeaders = new HashSet<>(Arrays.asList("Content-Type", "Authorization"));
|
||||
|
||||
router.route().handler(CorsHandler.create()
|
||||
.addOrigin(Host.getOrigin())
|
||||
.allowCredentials(true)
|
||||
.allowedHeaders(allowedHeaders)
|
||||
.allowedMethods(allowedMethods));
|
||||
router.route().handler(BodyHandler.create());
|
||||
router.get(Constants.API_PREFIX + "/devices").blockingHandler(this::getAllDevices);
|
||||
router.get(Constants.API_PREFIX + "/devices/:id").blockingHandler(this::getDeviceById);
|
||||
router.post(Constants.API_PREFIX + "/devices").blockingHandler(this::insertSensor);
|
||||
router.get(Constants.API_PREFIX + "/status").handler(ctx -> ctx.json(new JsonObject().put("status", "OK")));
|
||||
|
||||
vertx.createHttpServer()
|
||||
.requestHandler(router)
|
||||
.listen(
|
||||
Host.getApiPort(),
|
||||
Host.getHost(),
|
||||
result -> {
|
||||
if (result.succeeded()) {
|
||||
Constants.LOGGER.info(String.format(
|
||||
"📡 ApiVerticle desplegado. (http://%s:%d)", Host.getHost(), Host.getApiPort()
|
||||
));
|
||||
startPromise.complete();
|
||||
} else {
|
||||
Constants.LOGGER.error("❌ Error al desplegar ApiVerticle", result.cause());
|
||||
startPromise.fail(result.cause());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void getAllDevices(RoutingContext context) {
|
||||
Optional<String> sort = Optional.ofNullable(context.request().getParam("_sort"));
|
||||
Optional<String> order = Optional.ofNullable(context.request().getParam("_order"));
|
||||
Optional<Integer> limit = Optional.ofNullable(context.request().getParam("_limit"))
|
||||
.map(s -> {
|
||||
try {
|
||||
return Integer.parseInt(s);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
String query = QueryBuilder
|
||||
.select("*")
|
||||
.from("v_DevicesMeasures")
|
||||
.orderBy(sort, order)
|
||||
.limit(limit)
|
||||
.build();
|
||||
|
||||
vertx.eventBus().request("db.query", query, req -> {
|
||||
if (req.succeeded()) {
|
||||
Message<Object> result = req.result();
|
||||
JsonArray jsonArray = (JsonArray) result.body();
|
||||
context.json(jsonArray);
|
||||
} else {
|
||||
context.fail(500, req.cause());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void getDeviceById(RoutingContext context) {
|
||||
Optional<String> sort = Optional.ofNullable(context.request().getParam("_sort"));
|
||||
Optional<String> order = Optional.ofNullable(context.request().getParam("_order"));
|
||||
Optional<Integer> limit = Optional.ofNullable(context.request().getParam("_limit"))
|
||||
.map(s -> {
|
||||
try {
|
||||
return Integer.parseInt(s);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
String id = context.request().getParam("id");
|
||||
String query = QueryBuilder
|
||||
.select("*")
|
||||
.from("v_DevicesMeasures")
|
||||
.where("deviceId = ?", id)
|
||||
.orderBy(sort, order)
|
||||
.limit(limit)
|
||||
.build();
|
||||
|
||||
vertx.eventBus().request("db.query", query, req -> {
|
||||
if (req.succeeded()) {
|
||||
Message<Object> result = req.result();
|
||||
JsonArray jsonArray = (JsonArray) result.body();
|
||||
context.json(jsonArray);
|
||||
} else {
|
||||
context.fail(500, req.cause());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void insertSensor(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
|
||||
if (body == null) {
|
||||
context.fail(400, new IllegalArgumentException("Body is missing or invalid"));
|
||||
return;
|
||||
}
|
||||
|
||||
Integer deviceId = body.getInteger("deviceId");
|
||||
String sensorType = body.getString("sensorType");
|
||||
Float lat = body.getFloat("lat");
|
||||
Float lon = body.getFloat("lon");
|
||||
Float value = body.getFloat("value");
|
||||
|
||||
if (sensorType == null || lat == null || lon == null || value == null) {
|
||||
context.fail(400, new IllegalArgumentException("Missing required fields"));
|
||||
return;
|
||||
}
|
||||
|
||||
String query = QueryBuilder
|
||||
.insert("measures", "deviceId", "sensorType", "lat", "lon", "value")
|
||||
.values(deviceId, sensorType, lat, lon, value)
|
||||
.build();
|
||||
|
||||
vertx.eventBus().request("db.query", query, req -> {
|
||||
if (req.succeeded()) {
|
||||
context.json(new JsonObject().put("result", "OK"));
|
||||
} else {
|
||||
context.fail(500, req.cause());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package net.miarma.contaminus.server;
|
||||
|
||||
import io.vertx.core.AbstractVerticle;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.eventbus.EventBus;
|
||||
import io.vertx.core.eventbus.Message;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.jdbcclient.JDBCPool;
|
||||
import io.vertx.sqlclient.Row;
|
||||
import io.vertx.sqlclient.RowSet;
|
||||
import net.miarma.contaminus.common.Constants;
|
||||
import net.miarma.contaminus.database.DatabaseManager;
|
||||
|
||||
public class DatabaseVerticle extends AbstractVerticle {
|
||||
private JDBCPool pool;
|
||||
private EventBus eventBus;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Override
|
||||
public void start(Promise<Void> startPromise) {
|
||||
Constants.LOGGER.info("🟢 Iniciando DatabaseVerticle...");
|
||||
|
||||
DatabaseManager dbManager = new DatabaseManager(vertx);
|
||||
pool = dbManager.getPool();
|
||||
eventBus = vertx.eventBus();
|
||||
|
||||
pool.query("SELECT 1")
|
||||
.execute()
|
||||
.onSuccess(_res -> {
|
||||
Constants.LOGGER.info("✅ Database connection ok");
|
||||
Constants.LOGGER.info("📡 DatabaseVerticle desplegado");
|
||||
startPromise.complete();
|
||||
})
|
||||
.onFailure(err -> {
|
||||
Constants.LOGGER.error("❌ Database connection failed");
|
||||
Constants.LOGGER.error("❌ Error al desplegar DatabaseVerticle", err);
|
||||
startPromise.fail(err);
|
||||
});
|
||||
|
||||
eventBus.consumer("db.query", this::handleDatabaseQuery);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void handleDatabaseQuery(Message<String> msg) {
|
||||
String query = msg.body();
|
||||
Constants.LOGGER.info("📥 Query: " + query);
|
||||
|
||||
if(query == null || query.isEmpty()) {
|
||||
msg.fail(400, "Empty query");
|
||||
return;
|
||||
}
|
||||
|
||||
if(query.startsWith("SELECT")) {
|
||||
pool.query(query).execute()
|
||||
.onSuccess(res -> {
|
||||
RowSet<Row> rows = res;
|
||||
JsonArray jsonArray = new JsonArray();
|
||||
for (Row row : rows) {
|
||||
jsonArray.add(new JsonObject()
|
||||
.put("deviceId", row.getInteger("deviceId"))
|
||||
.put("deviceName", row.getString("deviceName"))
|
||||
.put("measureId", row.getInteger("measureId"))
|
||||
.put("sensorType", row.getString("sensorType"))
|
||||
.put("lat", row.getFloat("lat"))
|
||||
.put("lon", row.getFloat("lon"))
|
||||
.put("value", row.getFloat("value"))
|
||||
.put("timestamp", row.getLocalDateTime("timestamp").toString())
|
||||
);
|
||||
}
|
||||
msg.reply(jsonArray);
|
||||
})
|
||||
.onFailure(err -> msg.fail(500, err.getMessage()));
|
||||
} else if(query.startsWith("INSERT")) {
|
||||
pool.query(msg.body()).execute()
|
||||
.onSuccess(_res -> msg.reply(new JsonObject().put("status", "success")))
|
||||
.onFailure(err -> msg.fail(500, err.getMessage()));
|
||||
} else {
|
||||
msg.fail(400, "Invalid operation");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
6
package-lock.json
generated
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "ContaminUS",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||