Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions GenerativeChainSim-G464-PS25/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

36 changes: 36 additions & 0 deletions GenerativeChainSim-G464-PS25/backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Node modules
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Build output
build/
dist/
.cache/

# Logs
logs/
*.log
debug.log
*.pid

# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# Editor files
.vscode/
.idea/
.DS_Store

# Coverage reports
coverage/

# Misc
*.tgz
*.zip
*.tar.gz
13 changes: 13 additions & 0 deletions GenerativeChainSim-G464-PS25/backend/config/db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const mongoose = require("mongoose");

const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGO_URI);
console.log("✅ MongoDB connected");
} catch (error) {
console.error("❌ MongoDB connection failed:", error.message);
process.exit(1);
}
};

module.exports = connectDB;
47 changes: 47 additions & 0 deletions GenerativeChainSim-G464-PS25/backend/controllers/authController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const User = require("../models/User");
const jwt = require("jsonwebtoken");

const generateToken = (id) => {
return jwt.sign({ id }, process.env.JWT_SECRET, { expiresIn: "30d" });
};

// @desc Register user
exports.registerUser = async (req, res) => {
const { name, email, password, role } = req.body;
try {
const userExists = await User.findOne({ email });
if (userExists) return res.status(400).json({ message: "User already exists" });

const user = await User.create({ name, email, password, role });
res.status(201).json({
_id: user._id,
name: user.name,
email: user.email,
role: user.role,
token: generateToken(user._id),
});
} catch (error) {
res.status(500).json({ message: error.message });
}
};

// @desc Login user
exports.loginUser = async (req, res) => {
const { email, password } = req.body;
try {
const user = await User.findOne({ email });
if (user && (await user.matchPassword(password))) {
res.json({
_id: user._id,
name: user.name,
email: user.email,
role: user.role,
token: generateToken(user._id),
});
} else {
res.status(401).json({ message: "Invalid email or password" });
}
} catch (error) {
res.status(500).json({ message: error.message });
}
};
27 changes: 27 additions & 0 deletions GenerativeChainSim-G464-PS25/backend/controllers/kpiController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const Order = require("../models/Order");

exports.getKPIs = async (req, res) => {
try {
const orders = await Order.find({ userId: req.user._id });

let totalProfit = 0, totalCO2 = 0, onTime = 0;
orders.forEach(o => {
const rec = o.predictions.find(p => p.mode === o.recommended_mode);
if (rec) {
totalProfit += rec.profit;
totalCO2 += rec.co2;
onTime += (1 - rec.delay_risk);
}
});

const kpis = {
profit: totalProfit.toFixed(2),
co2: totalCO2.toFixed(2),
onTime: ((onTime / orders.length) * 100).toFixed(1),
};

res.json(kpis);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
117 changes: 117 additions & 0 deletions GenerativeChainSim-G464-PS25/backend/controllers/opsController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
const Order = require('../models/Order');
const Alert = require('../models/Alert');
const { getDisruptions } = require('../utils/externalAPI');
const { notifyUser } = require('../utils/notify');
const axios = require('axios');

exports.getDisruptions = async (req, res) => {
try {
// Input: optional area / coords: req.query.area or req.body.coords
const area = req.query.area || null;
const disruptions = await getDisruptions(area);
res.json({ disruptions });
} catch (err) {
console.error(err);
res.status(500).json({ message: 'Failed to fetch disruptions' });
}
};

// Replan: generate alternative mode recommendations for an order
exports.replanOrder = async (req, res) => {
try {
const orderId = req.params.orderId;
const order = await Order.findOne({ _id: orderId });
if (!order) return res.status(404).json({ message: 'Order not found' });

// Compose simulate request body from stored input + optional weight overrides
const simulateBody = {
...order.input, // ensure input has fields matching ML simulate request
alpha: req.body.alpha ?? 1.0,
beta: req.body.beta ?? 0.01,
gamma: req.body.gamma ?? 0.001
};

// Call local simulate service (Node -> Python ML service)
const mlUrl = process.env.ML_SIMULATE_URL || 'http://localhost:8000/simulate';
const simRes = await axios.post(mlUrl, simulateBody, { timeout: 10000 });

// store replan suggestions into order (optional)
order.replans = order.replans || [];
order.replans.push({
createdAt: new Date(),
request: simulateBody,
response: simRes.data
});
await order.save();

// If top recommendation has delay_risk high, create an alert
const best = simRes.data.best;
if (best.delay_risk && best.delay_risk > 0.7) {
const alert = await Alert.create({
userId: order.userId,
orderId: order._id,
severity: best.delay_risk > 0.9 ? 'critical' : 'warning',
message: `High delay risk predicted (${(best.delay_risk*100).toFixed(0)}%) for order ${order.orderId} with recommended ${best.mode}`,
metadata: { best }
});
// notify user
const user = req.user;
await notifyUser(user, 'High delay risk for Order ' + order.orderId, alert.message, ['email']);
}

res.json(simRes.data);
} catch (err) {
console.error('replanOrder error', err.message || err);
res.status(500).json({ message: 'Replan failed' });
}
};

exports.listAlerts = async (req, res) => {
try {
const alerts = await Alert.find({ userId: req.user._id }).sort({ createdAt: -1 });
res.json(alerts);
} catch (err) {
console.error(err);
res.status(500).json({ message: 'Failed to load alerts' });
}
};

exports.ackAlert = async (req, res) => {
try {
const alertId = req.params.alertId;
const alert = await Alert.findOne({ _id: alertId, userId: req.user._id });
if (!alert) return res.status(404).json({ message: 'Alert not found' });
alert.read = true;
await alert.save();
res.json({ success: true });
} catch (err) {
console.error(err);
res.status(500).json({ message: 'Failed to acknowledge alert' });
}
};
exports.simulateOrder = async (req, res) => {
try {
const simulateBody = {
city_enc: req.body.city_enc,
state_enc: req.body.state_enc,
region_enc: req.body.region_enc,
product_category_id: req.body.product_category_id,
order_item_quantity: req.body.order_item_quantity,
order_item_discount: req.body.order_item_discount,
order_item_total: req.body.order_item_total,
weight: req.body.weight,
alpha: 1.0,
beta: 0.01,
gamma: 0.001
};

const mlUrl = process.env.ML_SIMULATE_URL || 'http://localhost:8000/simulate';
const simRes = await axios.post(mlUrl, simulateBody, { timeout: 10000 });

// Optionally store result
res.json(simRes.data);
} catch (err) {
console.error('Ops simulate error:', err.message || err);
res.status(500).json({ message: 'Simulation failed' });
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const Order = require("../models/Order");

exports.createOrder = async (req, res) => {
try {
const { input } = req.body;

// 🚀 Mock prediction for now
const predictions = [
{ mode: "Standard Class", delivery_time: 4.0, delay_risk: 0.4, profit: 50, co2: 3.2, score: 1.1 },
{ mode: "First Class", delivery_time: 2.0, delay_risk: 0.6, profit: 45, co2: 6.1, score: 0.8 },
];

const recommended = predictions[0];

const order = await Order.create({
userId: req.user._id,
input,
predictions,
recommended_mode: recommended.mode,
explanation: `Recommended ${recommended.mode} due to better balance.`
});

res.status(201).json(order);
} catch (error) {
res.status(500).json({ message: error.message });
}
};

exports.getOrderHistory = async (req, res) => {
try {
const orders = await Order.find({ userId: req.user._id }).sort({ created_at: -1 });
res.json(orders);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const Order = require("../models/Order");

// Compute sustainability KPIs (CO₂, efficiency)
// controllers/sustainabilityController.js

exports.getSustainabilityMetrics = async (req, res) => {
try {
// Mock data for now — to be replaced with DB or ML model output later
const modes = ["Standard Class", "Second Class", "First Class", "Same Day"];
const cities = ["New York", "Chicago", "Houston", "Los Angeles", "Seattle"];
const suppliers = ["FedEx", "UPS", "DHL", "Amazon Logistics"];

const summary = {
totalCO2: Math.round(Math.random() * 5000 + 2000),
avgCO2PerOrder: (Math.random() * 15 + 5).toFixed(1),
reductionRate: (Math.random() * 10 + 5).toFixed(1), // 5–15%
bestMode: modes[Math.floor(Math.random() * modes.length)],
};

const modeLeaderboard = modes.map((m) => ({
mode: m,
avgCO2: (Math.random() * 5 + 1).toFixed(2),
efficiencyScore: (Math.random() * 100).toFixed(1),
totalOrders: Math.floor(Math.random() * 200 + 50),
}));

const cityCO2 = cities.map((c) => ({
city: c,
avgCO2: (Math.random() * 5 + 1).toFixed(2),
avgProfit: Math.round(Math.random() * 100 + 20),
}));

const supplierImpact = suppliers.map((s) => ({
supplier: s,
avgCO2: (Math.random() * 4 + 0.5).toFixed(2),
reliability: (Math.random() * 10 + 85).toFixed(1), // %
efficiencyScore: (Math.random() * 100).toFixed(1),
}));

return res.status(200).json({
summary,
modeLeaderboard,
cityCO2,
supplierImpact,
});
} catch (error) {
console.error("Error fetching sustainability data:", error);
res.status(500).json({ message: "Error fetching sustainability data" });
}
};
36 changes: 36 additions & 0 deletions GenerativeChainSim-G464-PS25/backend/middleware/authMiddleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const jwt = require("jsonwebtoken");
const User = require("../models/User");

// Protect route — verify token
const protect = async (req, res, next) => {
let token;
if (req.headers.authorization && req.headers.authorization.startsWith("Bearer")) {
try {
token = req.headers.authorization.split(" ")[1];
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = await User.findById(decoded.id).select("-password");
next();
} catch (error) {
console.error("Auth error:", error.message);
res.status(401).json({ message: "Not authorized, invalid token" });
}
}

if (!token) {
res.status(401).json({ message: "Not authorized, no token" });
}
};

// Role-based access
const authorizeRoles = (...allowedRoles) => {
return (req, res, next) => {
if (!allowedRoles.includes(req.user.role)) {
return res.status(403).json({
message: `Access denied. Requires one of: ${allowedRoles.join(", ")}.`,
});
}
next();
};
};

module.exports = { protect, authorizeRoles };
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const errorHandler = (err, req, res, next) => {
console.error(`🔥 Error: ${err.message}`);
res.status(err.statusCode || 500).json({
success: false,
message: err.message || "Server Error",
});
};

module.exports = errorHandler;
13 changes: 13 additions & 0 deletions GenerativeChainSim-G464-PS25/backend/models/Alert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const mongoose = require("mongoose");

const alertSchema = new mongoose.Schema({
userId: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
orderId: { type: String },
severity: { type: String, enum: ["info","warning","critical"], default: "info" },
message: { type: String, required: true },
metadata: { type: Object, default: {} },
read: { type: Boolean, default: false },
createdAt: { type: Date, default: Date.now }
});

module.exports = mongoose.model("Alert", alertSchema);
Loading