1
0

web server and connection to db/api

This commit is contained in:
Jose
2025-03-03 19:46:10 +01:00
parent 7e4d1e92fc
commit 874c92c4b5
37 changed files with 1897 additions and 206 deletions

View File

@@ -1,13 +1,2 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=23
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=23
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=23

View File

@@ -4,16 +4,49 @@
<artifactId>contaminus</artifactId>
<version>1.0.0</version>
<name>ContaminUS</name>
<dependencies>
<!-- Vert.X Core -->
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>4.5.13</version>
</dependency>
<!-- Vert.X Web -->
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
<version>4.5.13</version>
</dependency>
<!-- Vert.X MQTT -->
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-mqtt</artifactId>
<version>4.4.2</version>
</dependency>
<!-- Vert.X MariaDB/MySQL Client -->
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-jdbc-client</artifactId>
<version>4.5.13</version>
</dependency>
<!-- JDBC Driver -->
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>3.5.2</version>
</dependency>
<!-- Gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.12.1</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,22 +0,0 @@
package net.miarma.contaminus;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
public class ConsumerVerticle1 extends AbstractVerticle {
@Override
public void start(Promise<Void> startPromise) {
getVertx().eventBus().consumer("__addr_ConsumerBox", message -> {
String customMessage = (String) message.body();
System.out.println("[1] Mensaje recibido (" + message.address() + "): " + customMessage);
String replyMessage = "[1] Mensaje recibido: \"" + message.body().toString() + "\"";
message.reply(replyMessage);
});
startPromise.complete();
}
@Override
public void stop(Promise<Void> stopPromise) throws Exception {
super.stop(stopPromise);
}
}

View File

@@ -1,22 +0,0 @@
package net.miarma.contaminus;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
public class ConsumerVerticle2 extends AbstractVerticle {
@Override
public void start(Promise<Void> startPromise) {
getVertx().eventBus().consumer("__addr_ConsumerBox", message -> {
String customMessage = (String) message.body();
System.out.println("[2] Mensaje recibido (" + message.address() + "): " + customMessage);
String replyMessage = "[2] Mensaje recibido: \"" + message.body().toString() + "\"";
message.reply(replyMessage);
});
startPromise.complete();
}
@Override
public void stop(Promise<Void> stopPromise) throws Exception {
super.stop(stopPromise);
}
}

View File

@@ -1,59 +0,0 @@
package net.miarma.contaminus;
import com.google.gson.Gson;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Promise;
import io.vertx.core.json.JsonObject;
import net.miarma.contaminus.p2p.VerticleConfig;
public class Main extends AbstractVerticle {
private Gson gson;
@Override
public void start(Promise<Void> promise) {
gson = new Gson();
VerticleConfig config = new VerticleConfig();
config.setNum(2);
config.setName("[MESSAGE] ");
DeploymentOptions options = new DeploymentOptions();
options.setConfig(new JsonObject(gson.toJson(config)));
String consumer1Name = ConsumerVerticle1.class.getName();
getVertx().deployVerticle(consumer1Name, options, result -> {
if (result.succeeded()) {
System.out.println(consumer1Name + " (" + result.result() + ") ha sido desplegado correctamente");
} else {
result.cause().printStackTrace();
}
});
String consumer2Name = ConsumerVerticle1.class.getName();
getVertx().deployVerticle(consumer2Name, options, result -> {
if (result.succeeded()) {
System.out.println(consumer2Name + " (" + result.result() + ") ha sido desplegado correctamente");
} else {
result.cause().printStackTrace();
}
});
String senderName = SenderVerticle.class.getName();
getVertx().deployVerticle(senderName, options, result -> {
if (result.succeeded()) {
System.out.println(senderName + " (" + result.result() + ") ha sido desplegado correctamente");
} else {
result.cause().printStackTrace();
}
});
}
@Override
public void stop(Promise<Void> stopFuture) throws Exception {
getVertx().undeploy(ConsumerVerticle1.class.getName());
getVertx().undeploy(SenderVerticle.class.getName());
super.stop(stopFuture);
}
}

View File

@@ -1,29 +0,0 @@
package net.miarma.contaminus;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.eventbus.Message;
public class SenderVerticle extends AbstractVerticle {
String verticleID = "";
@Override
public void start(Promise<Void> promise) {
EventBus eventBus = vertx.eventBus();
vertx.setPeriodic(4000, _id -> {
String message = "Hola papu";
eventBus.request("__addr_ConsumerBox", message, reply -> {
Message<Object> res = reply.result();
verticleID = res.address();
if(reply.succeeded()) {
String replyMsg = (String) res.body();
System.out.println("Respuesta recibida (" + res.address() + "): " + replyMsg + "\n\n\n");
} else {
System.out.println("No ha habido respuesta");
}
});
});
}
}

View File

@@ -0,0 +1,76 @@
package net.miarma.contaminus.common;
import java.io.*;
import java.util.Properties;
public class ConfigManager {
private static ConfigManager instance;
private final File configFile;
private final Properties config;
private ConfigManager() {
this.configFile = new File(Constants.CONFIG_FILE);
this.config = new Properties();
if (!configFile.exists()) {
try {
createFiles();
} catch (IOException e) {
Constants.LOGGER.error("Error creating configuration files: ", e);
}
}
loadConfig();
}
public static synchronized ConfigManager getInstance() {
if (instance == null) {
instance = new ConfigManager();
}
return instance;
}
private void createFiles() throws IOException {
File baseDir = new File(Constants.BASE_DIR);
if (!baseDir.exists()) baseDir.mkdirs();
try (InputStream defaultConfigStream = getClass().getClassLoader().getResourceAsStream("default.properties");
FileOutputStream fos = new FileOutputStream(configFile)) {
if (defaultConfigStream != null) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = defaultConfigStream.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
} else {
Constants.LOGGER.error("File not found: default.properties");
}
}
}
private void loadConfig() {
try (FileInputStream fis = new FileInputStream(configFile)) {
config.load(fis);
} catch (IOException e) {
Constants.LOGGER.error("Error loading configuration file: ", e);
}
}
public String getProperty(String key) {
return config.getProperty(key);
}
public void setProperty(String key, String value) {
config.setProperty(key, value);
saveConfig();
}
private void saveConfig() {
try (FileOutputStream fos = new FileOutputStream(configFile)) {
config.store(fos, "Configuration for: " + Constants.APP_NAME);
} catch (IOException e) {
Constants.LOGGER.error("Error saving configuration file: ", e);
}
}
}

View File

@@ -0,0 +1,22 @@
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.");
}
}

View File

@@ -0,0 +1,5 @@
package net.miarma.contaminus.common;
public enum OSType {
LINUX, WINDOWS, INVALID_OS;
}

View File

@@ -0,0 +1,14 @@
package net.miarma.contaminus.common;
public class SystemInfo {
public static OSType getOS() {
String envProperty = System.getProperty("os.name").toLowerCase();
if(envProperty.contains("windows")) {
return OSType.WINDOWS;
} else if(envProperty.contains("linux")) {
return OSType.LINUX;
} else {
return OSType.INVALID_OS;
}
}
}

View File

@@ -0,0 +1,30 @@
package net.miarma.contaminus.database;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.jdbcclient.JDBCPool;
import net.miarma.contaminus.common.ConfigManager;
public class DatabaseManager {
private final JDBCPool pool;
@SuppressWarnings("deprecation")
public DatabaseManager(Vertx vertx) {
ConfigManager config = ConfigManager.getInstance();
JsonObject dbConfig = new JsonObject()
.put("url", config.getProperty("db.protocol") + "//" +
config.getProperty("db.host") + ":" +
config.getProperty("db.port") + "/" +
config.getProperty("db.name"))
.put("user", config.getProperty("db.user"))
.put("password", config.getProperty("db.pwd"))
.put("max_pool_size", 5);
pool = JDBCPool.pool(vertx, dbConfig);
}
public JDBCPool getPool() {
return pool;
}
}

View File

@@ -0,0 +1,83 @@
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 +
'}';
}
}

View File

@@ -1,43 +0,0 @@
package net.miarma.contaminus.p2p;
public class VerticleConfig {
private String name;
private int num;
private boolean isDup;
public VerticleConfig() {
}
public VerticleConfig(String name, int num, boolean isDup) {
this.isDup = isDup;
this.num = num;
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public boolean isDup() {
return isDup;
}
public void setDup(boolean isDup) {
this.isDup = isDup;
}
}

View File

@@ -0,0 +1,65 @@
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");
startPromise.complete();
})
.onFailure(err -> {
Constants.LOGGER.error("❌ Database connection failed");
startPromise.fail(err);
});
eventBus.consumer("db.query", this::handleDatabaseQuery);
Constants.LOGGER.info("📡 DatabaseVerticle desplegado.");
}
private void handleDatabaseQuery(Message<String> msg) {
String query = msg.body();
pool.query(query).execute()
.onSuccess(res -> {
RowSet<Row> rows = res;
JsonArray jsonArray = new JsonArray();
for (Row row : rows) {
jsonArray.add(new JsonObject()
.put("id", row.getInteger("id"))
.put("sensorType", row.getString("sensor_type"))
.put("value", row.getFloat("value"))
.put("lat", row.getFloat("lat"))
.put("lon", row.getFloat("lon"))
.put("timestamp", row.getLocalDateTime("timestamp").toString())
);
}
msg.reply(jsonArray);
})
.onFailure(err -> msg.fail(500, err.getMessage()));
}
}

View File

@@ -0,0 +1,46 @@
package net.miarma.contaminus.server;
import io.vertx.core.AbstractVerticle;
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.StaticHandler;
import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.eventbus.Message;
import net.miarma.contaminus.common.Constants;
public class HttpServerVerticle extends AbstractVerticle {
@Override
public void start() {
Constants.LOGGER.info("🟢 Iniciando HttpServerVerticle...");
Router router = Router.router(vertx);
router.route("/*").handler(StaticHandler.create("webroot").setDefaultContentEncoding("UTF-8"));
router.get(Constants.API_PREFIX + "/sensors").blockingHandler(this::getAllSensors);
router.get(Constants.API_PREFIX + "/status").handler(ctx ->
ctx.json(new JsonObject().put("status", "OK"))
);
vertx.createHttpServer().requestHandler(router).listen(80, response -> {
if (response.succeeded()) {
Constants.LOGGER.info("🚀 Servidor HTTP desplegado en http://localhost:80");
} else {
Constants.LOGGER.error("❌ Error al desplegar el servidor HTTP", response.cause());
}
});
}
private void getAllSensors(RoutingContext context) {
vertx.eventBus().request("db.query", "SELECT * FROM sensor_mq_data", new DeliveryOptions(), ar -> {
if (ar.succeeded()) {
Message<Object> result = ar.result();
JsonArray jsonArray = (JsonArray) result.body();
context.json(jsonArray);
} else {
context.fail(500, ar.cause());
}
});
}
}

View File

@@ -0,0 +1,20 @@
package net.miarma.contaminus.server;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
public class MainVerticle extends AbstractVerticle {
@Override
public void start(Promise<Void> startPromise) {
getVertx().deployVerticle(new DatabaseVerticle());
getVertx().deployVerticle(new HttpServerVerticle());
}
@Override
public void stop(Promise<Void> stopPromise) throws Exception {
getVertx().deploymentIDs()
.forEach(v -> getVertx().undeploy(v));
}
}

View File

@@ -0,0 +1,7 @@
package net.miarma.contaminus.server;
import io.vertx.core.AbstractVerticle;
public class MqttVerticle extends AbstractVerticle {
}

View File

@@ -0,0 +1,7 @@
db.protocol=jdbc:mariadb:
db.host=localhost
db.port=3306
db.name=dad
db.user=root
db.pwd=root
dp.poolSize=5

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,71 @@
{
"userConfig": {
"city": [
37.38283,
-5.97317
]
},
"appConfig": {
"endpoints": {
"baseUrl": "http://localhost:80/api/v1",
"sensors": "sensors",
"sensor": "sensors/sensor"
},
"historyChartConfig": {
"chartOptionsDark": {
"responsive": true,
"maintainAspectRatio": false,
"scales": {
"x": {
"grid": {
"color": "rgba(255, 255, 255, 0.1)"
},
"ticks": {
"color": "#E0E0E0"
}
},
"y": {
"grid": {
"color": "rgba(255, 255, 255, 0.1)"
},
"ticks": {
"color": "#E0E0E0"
}
}
},
"plugins": {
"legend": {
"display": false
}
}
},
"chartOptionsLight": {
"responsive": true,
"maintainAspectRatio": false,
"scales": {
"x": {
"grid": {
"color": "rgba(0, 0, 0, 0.1)"
},
"ticks": {
"color": "#333"
}
},
"y": {
"grid": {
"color": "rgba(0, 0, 0, 0.1)"
},
"ticks": {
"color": "#333"
}
}
},
"plugins": {
"legend": {
"display": false
}
}
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

View File

@@ -1,10 +1,27 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Vert.X test</title>
</head>
<body>
<h1>Vert.X test</h1>
<p>Hola</p>
</body>
</html>
<!doctype html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta property="og:title" content="ContaminUS" />
<meta property="og:description" content="Midiendo la calidad del aire y las calles en Sevilla 🌿🚛" />
<meta property="og:image" content="https://contaminus.miarma.net/logo.png" />
<meta property="og:url" content="https://contaminus.miarma.net/" />
<meta property="og:type" content="website" />
<meta property="og:locale" content="es_ES" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="ContaminUS" />
<meta name="twitter:description" content="Midiendo la calidad del aire y las calles en Sevilla 🌿🚛" />
<meta name="twitter:image" content="https://contaminus.miarma.net/logo.png" />
<link rel="shortcut icon" href="/images/favicon.ico" type="image/x-icon">
<title>ContaminUS</title>
<script type="module" crossorigin src="/assets/index-Ch0_cdBw.js"></script>
<link rel="modulepreload" crossorigin href="/assets/react-vendors-DbHEDQBy.js">
<link rel="modulepreload" crossorigin href="/assets/leaflet-DYDK0jU3.js">
<link rel="modulepreload" crossorigin href="/assets/chartjs-C6LAl0aW.js">
<link rel="stylesheet" crossorigin href="/assets/index-DhzIL-fx.css">
</head>
<body>
<div id="root"></div>
</body>

View File

@@ -0,0 +1,7 @@
db.protocol=jdbc:mariadb:
db.host=localhost
db.port=3306
db.name=dad
db.user=root
db.pwd=root
dp.poolSize=5

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,71 @@
{
"userConfig": {
"city": [
37.38283,
-5.97317
]
},
"appConfig": {
"endpoints": {
"baseUrl": "http://localhost:80/api/v1",
"sensors": "sensors",
"sensor": "sensors/sensor"
},
"historyChartConfig": {
"chartOptionsDark": {
"responsive": true,
"maintainAspectRatio": false,
"scales": {
"x": {
"grid": {
"color": "rgba(255, 255, 255, 0.1)"
},
"ticks": {
"color": "#E0E0E0"
}
},
"y": {
"grid": {
"color": "rgba(255, 255, 255, 0.1)"
},
"ticks": {
"color": "#E0E0E0"
}
}
},
"plugins": {
"legend": {
"display": false
}
}
},
"chartOptionsLight": {
"responsive": true,
"maintainAspectRatio": false,
"scales": {
"x": {
"grid": {
"color": "rgba(0, 0, 0, 0.1)"
},
"ticks": {
"color": "#333"
}
},
"y": {
"grid": {
"color": "rgba(0, 0, 0, 0.1)"
},
"ticks": {
"color": "#333"
}
}
},
"plugins": {
"legend": {
"display": false
}
}
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

View File

@@ -1,10 +1,27 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Vert.X test</title>
</head>
<body>
<h1>Vert.X test</h1>
<p>Hola</p>
</body>
</html>
<!doctype html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta property="og:title" content="ContaminUS" />
<meta property="og:description" content="Midiendo la calidad del aire y las calles en Sevilla 🌿🚛" />
<meta property="og:image" content="https://contaminus.miarma.net/logo.png" />
<meta property="og:url" content="https://contaminus.miarma.net/" />
<meta property="og:type" content="website" />
<meta property="og:locale" content="es_ES" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="ContaminUS" />
<meta name="twitter:description" content="Midiendo la calidad del aire y las calles en Sevilla 🌿🚛" />
<meta name="twitter:image" content="https://contaminus.miarma.net/logo.png" />
<link rel="shortcut icon" href="/images/favicon.ico" type="image/x-icon">
<title>ContaminUS</title>
<script type="module" crossorigin src="/assets/index-Ch0_cdBw.js"></script>
<link rel="modulepreload" crossorigin href="/assets/react-vendors-DbHEDQBy.js">
<link rel="modulepreload" crossorigin href="/assets/leaflet-DYDK0jU3.js">
<link rel="modulepreload" crossorigin href="/assets/chartjs-C6LAl0aW.js">
<link rel="stylesheet" crossorigin href="/assets/index-DhzIL-fx.css">
</head>
<body>
<div id="root"></div>
</body>