diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..ed60f1f4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +node_modules +npm-debug.log +Dockerfile +docker-compose.yml +.vscode +.git +.gitignore +.env \ No newline at end of file diff --git a/controllers/predictController.js b/controllers/predictController.js index 20bc61e3..3da42fbd 100644 --- a/controllers/predictController.js +++ b/controllers/predictController.js @@ -1,5 +1,7 @@ + // controllers/predictController.js const { getModelInfo, predict } = require("../services/tfModelService"); +const Prediction = require("../model/Prediction"); function health(req, res) { res.json({ @@ -60,20 +62,36 @@ async function doPredict(req, res) { }); } - const prediction = await predict(features); + // Ejecutar el modelo + const predictionValue = await predict(features); const latencyMs = Date.now() - start; - const timestamp = new Date().toISOString(); + const timestamp = new Date(); - // De momento sin MongoDB → predictionId null - res.status(201).json({ - predictionId: null, - prediction, + // Guardar en Mongo solo campos válidos + const responsePred = await Prediction.create({ + features, + prediction: predictionValue, timestamp, + latencyMs, + featureCount: meta.featureCount, + scalerVersion: meta.scalerVersion || "v1", + createdAt: timestamp, + predictGroup: "predict" + }); + + res.status(201).json({ + predictionId: responsePred._id, + prediction: predictionValue, + timestamp: timestamp.toISOString(), latencyMs }); + } catch (err) { console.error("Error en /predict:", err); - res.status(500).json({ error: "Internal error" }); + + res.status(500).json({ + error: "Internal error", + }); } } diff --git a/dockerfile b/dockerfile new file mode 100644 index 00000000..8990ac6c --- /dev/null +++ b/dockerfile @@ -0,0 +1,26 @@ +# Imagen base con Node 22 +FROM node:22-slim + + +# Directorio de trabajo dentro del contenedor +WORKDIR /usr/src/app + + +# Copiamos primero manifiestos para cachear dependencias +COPY package*.json ./ + + +# Instalamos dependencias de producción +RUN npm ci --omit=dev + + +# Copiamos el resto del código (incluye /model) +COPY . . + + +# El servicio escucha en 3002 +EXPOSE 3002 + + +# Comando de arranque +CMD ["node", "server.js"] \ No newline at end of file diff --git a/model/Prediction.js b/model/Prediction.js new file mode 100644 index 00000000..c7cbe82c --- /dev/null +++ b/model/Prediction.js @@ -0,0 +1,23 @@ +//model/prediction.js +'use strict' + +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; + +const PredictionSchema = new Schema({ + source: String, + timestamp: { type: Date, default: Date.now }, + latencyMs: Number, + features: [Number], + prediction: Number, + + featureCount: Number, + scalerVersion: String, + createdAt: { type: Date, default: Date.now }, + targetDate: Date, + dailyValues: [Number], + + +}); + +module.exports = mongoose.model("Prediction", PredictionSchema); \ No newline at end of file diff --git a/package.json b/package.json index 7e709810..42a9fc1b 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,14 @@ }, "author": "Iren Lorenzo Fonseca", "license": "ISC", - "bugs": { - }, + "bugs": {}, "dependencies": { "@tensorflow/tfjs": "^4.22.0", "@tensorflow/tfjs-backend-wasm": "^4.22.0", - "express": "^5.1.0" + "dotenv": "^17.2.3", + "express": "^5.1.0", + "mongodb": "^7.0.0", + "mongoose": "^9.0.1" } -} \ No newline at end of file +} + diff --git a/server.js b/server.js index 4d44675f..5b7adcac 100644 --- a/server.js +++ b/server.js @@ -1,32 +1,46 @@ // server.js // Entry point del servicio PREDICT +require("dotenv").config(); const express = require("express"); +const app = express(); const path = require("path"); +const mongoose = require("mongoose"); const predictRoutes = require("./routes/predictRoutes"); const { initModel } = require("./services/tfModelService"); const PORT = process.env.PORT || 3002; +const MONGO_URI = process.env.MONGO_URI; + +// conectar a Mongo +mongoose + .connect(MONGO_URI) + .then(() => {console.log("MongoDB conectado (PREDICT)")}) + .catch((err) => { + console.error("Error al conectar MongoDB:", err); + process.exit(1); + }); -const app = express(); app.use(express.json()); // Servir la carpeta del modelo TFJS (model/model.json + pesos) const modelDir = path.resolve(__dirname, "model"); app.use("/model", express.static(modelDir)); -// Rutas del servicio PREDICT + app.use("/", predictRoutes); -// Arranque del servidor + carga del modelo + app.listen(PORT, async () => { const serverUrl = `http://localhost:${PORT}`; - console.log(`[PREDICT] Servicio escuchando en ${serverUrl}`); + console.log(`PREDICT escuchando en ${serverUrl}`); try { + // Inicializa el modelo predictivo await initModel(serverUrl); + console.log(" Modelo predictivo cargado correctamente."); } catch (err) { - console.error("Error al inicializar modelo:", err); + console.error("Error al inicializar el modelo predictivo:", err); process.exit(1); } }); diff --git a/services/tfModelService.js b/services/tfModelService.js index aff8b787..71ec499b 100644 --- a/services/tfModelService.js +++ b/services/tfModelService.js @@ -122,4 +122,4 @@ module.exports = { initModel, getModelInfo, predict -}; +}; \ No newline at end of file