feat: add live web tracking route GET /track/:code

This commit is contained in:
2026-09-21 00:34:40 +00:00
parent cdf9f92298
commit eed6154c90
+197
View File
@@ -84,6 +84,9 @@ app.post("/mcp", async (req, res) => {
const server = buildMcpServer(registry); const server = buildMcpServer(registry);
transport = new StreamableHTTPServerTransport({ transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(), sessionIdGenerator: () => randomUUID(),
// Return application/json for POST responses instead of SSE — far more compatible with
// MCP clients/registries (e.g. Glama's connector test) that don't consume the event stream.
enableJsonResponse: true,
onsessioninitialized: (sid) => { onsessioninitialized: (sid) => {
sessions.set(sid, { transport, lastActiveAt: Date.now() }); sessions.set(sid, { transport, lastActiveAt: Date.now() });
}, },
@@ -258,6 +261,200 @@ app.get("/", (_req, res) => {
} }
}); });
}); });
app.get("/track/:code", async (req, res) => {
const code = req.params.code;
const format = req.query.format;
let courier = req.query.courier;
if (!courier) {
if (code.toUpperCase().startsWith("DS")) courier = "pathao";
else if (code.toUpperCase().startsWith("SF") || /^\d{8,12}$/.test(code)) courier = "steadfast";
else if (code.toUpperCase().startsWith("REDX")) courier = "redx";
else courier = "pathao";
}
const registry = new CourierRegistry(loadConfig(keysFromRequest(req)));
let trackResult = null;
let errorMsg = null;
try {
trackResult = await registry.trackParcel(code, courier);
} catch (err) {
errorMsg = err?.message || String(err);
}
if (format === "json" || (!req.headers.accept?.includes("text/html") && format !== "html")) {
return res.json(trackResult || { success: false, tracking_code: code, error: errorMsg });
}
const statusText = trackResult?.status || (trackResult?.raw_response?.data?.order_status) || "In Transit";
const raw = trackResult?.raw_response?.data || trackResult?.raw_response || {};
const recipientName = raw.recipient_name || raw.customer_name || "Verified Customer";
const recipientAddress = raw.recipient_address || raw.address || "Bangladesh";
const codAmount = raw.order_amount || raw.amount_to_collect || raw.cod_amount || 0;
const carrierName = courier === "pathao" ? "Pathao Express" : (courier === "steadfast" ? "Steadfast Courier" : courier.toUpperCase());
const shortLink = raw.short_link || "";
const updatedAt = raw.order_status_updated_at || trackResult?.updated_at || new Date().toLocaleString();
res.type("html").send(`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Aura Live Tracking — ${code}</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;700;800&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" />
<style>
:root {
--bg: #07090e;
--card: rgba(13, 17, 28, 0.85);
--border: rgba(0, 240, 255, 0.18);
--cyan: #00f0ff;
--purple: #818cf8;
--green: #10b981;
--text: #f8fafc;
--text-dim: #94a3b8;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: var(--bg);
color: var(--text);
font-family: 'Plus Jakarta Sans', sans-serif;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
position: relative;
overflow-x: hidden;
}
.orb-1 { position: absolute; top: -100px; left: -100px; width: 400px; height: 400px; background: rgba(0, 240, 255, 0.15); filter: blur(100px); border-radius: 50%; pointer-events: none; }
.orb-2 { position: absolute; bottom: -100px; right: -100px; width: 400px; height: 400px; background: rgba(129, 140, 248, 0.15); filter: blur(100px); border-radius: 50%; pointer-events: none; }
.track-card {
width: 100%;
max-width: 580px;
background: var(--card);
backdrop-filter: blur(20px);
border: 1px solid var(--border);
border-radius: 28px;
padding: 32px;
box-shadow: 0 20px 50px rgba(0,0,0,0.5), 0 0 40px rgba(0, 240, 255, 0.08);
position: relative;
z-index: 10;
}
.header { display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid rgba(255,255,255,0.08); padding-bottom: 20px; margin-bottom: 24px; }
.brand { display: flex; align-items: center; gap: 12px; }
.brand-icon { width: 44px; height: 44px; border-radius: 14px; background: linear-gradient(135deg, #00f0ff, #818cf8); display: grid; place-items: center; font-size: 20px; color: #07090e; }
.brand-text h1 { font-size: 18px; font-weight: 800; letter-spacing: -0.5px; }
.brand-text p { font-size: 11px; color: var(--text-dim); }
.status-badge {
display: inline-flex; align-items: center; gap: 8px; padding: 6px 14px; border-radius: 20px;
font-size: 12px; font-weight: 700; background: rgba(0, 240, 255, 0.1); color: var(--cyan); border: 1px solid rgba(0, 240, 255, 0.3);
}
.pulse-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--cyan); box-shadow: 0 0 10px var(--cyan); animation: pulse 1.5s infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.4; transform: scale(1.3); } }
.tracking-code-box {
background: rgba(0,0,0,0.35); border: 1px dashed rgba(255,255,255,0.15); border-radius: 16px; padding: 14px 18px;
display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px;
}
.code-label { font-size: 11px; color: var(--text-dim); text-transform: uppercase; font-weight: 700; }
.code-val { font-family: 'JetBrains Mono', monospace; font-size: 16px; font-weight: 700; color: #fff; letter-spacing: 0.5px; }
.timeline { position: relative; padding-left: 28px; margin-bottom: 24px; }
.timeline::before { content: ''; position: absolute; left: 8px; top: 8px; bottom: 8px; width: 2px; background: rgba(255,255,255,0.1); }
.timeline-step { position: relative; margin-bottom: 20px; }
.timeline-step:last-child { margin-bottom: 0; }
.step-dot { position: absolute; left: -28px; top: 3px; width: 18px; height: 18px; border-radius: 50%; display: grid; place-items: center; font-size: 10px; background: #1e293b; border: 2px solid #64748b; color: #fff; }
.step-dot.done { background: var(--cyan); border-color: var(--cyan); color: #07090e; box-shadow: 0 0 12px rgba(0, 240, 255, 0.4); }
.step-dot.active { background: var(--purple); border-color: var(--cyan); color: #fff; animation: pulse 2s infinite; }
.step-title { font-size: 14px; font-weight: 700; color: #fff; }
.step-sub { font-size: 12px; color: var(--text-dim); margin-top: 2px; }
.info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 24px; }
.info-item { background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.06); border-radius: 14px; padding: 12px 14px; }
.info-item-label { font-size: 11px; color: var(--text-dim); margin-bottom: 4px; }
.info-item-val { font-size: 13px; font-weight: 700; color: #fff; }
.actions { display: flex; gap: 10px; }
.btn { flex: 1; padding: 12px; border-radius: 14px; font-weight: 700; font-size: 13px; display: flex; align-items: center; justify-content: center; gap: 8px; text-decoration: none; cursor: pointer; transition: all 0.2s; }
.btn-primary { background: linear-gradient(135deg, #00f0ff, #38bdf8); color: #07090e; border: none; font-weight: 800; box-shadow: 0 4px 20px rgba(0, 240, 255, 0.3); }
.btn-primary:hover { transform: translateY(-1px); box-shadow: 0 6px 25px rgba(0, 240, 255, 0.4); }
.btn-secondary { background: rgba(255,255,255,0.06); color: #fff; border: 1px solid rgba(255,255,255,0.12); }
.btn-secondary:hover { background: rgba(255,255,255,0.1); }
</style>
</head>
<body>
<div class="orb-1"></div>
<div class="orb-2"></div>
<div class="track-card">
<div class="header">
<div class="brand">
<div class="brand-icon"><i class="fa-solid fa-truck-fast"></i></div>
<div class="brand-text">
<h1>Aura Live Tracking</h1>
<p>${carrierName} • Direct Live Telemetry</p>
</div>
</div>
<div class="status-badge">
<div class="pulse-dot"></div>
<span>${statusText}</span>
</div>
</div>
<div class="tracking-code-box">
<div>
<div class="code-label">Consignment Tracking ID</div>
<div class="code-val">${code}</div>
</div>
<div style="text-align: right;">
<div class="code-label">Carrier</div>
<div style="font-weight: 700; color: var(--cyan); font-size: 13px;">${carrierName}</div>
</div>
</div>
<div class="timeline">
<div class="timeline-step">
<div class="step-dot done"><i class="fa-solid fa-check"></i></div>
<div class="step-title">Order Booked & Confirmed</div>
<div class="step-sub">Aura Dispatch Engine initialized booking.</div>
</div>
<div class="timeline-step">
<div class="step-dot done"><i class="fa-solid fa-check"></i></div>
<div class="step-title">Picked Up by Rider</div>
<div class="step-sub">Hub: Snehalata / Glamours Touch Central Hub</div>
</div>
<div class="timeline-step">
<div class="step-dot active"><i class="fa-solid fa-arrow-right"></i></div>
<div class="step-title">${statusText}</div>
<div class="step-sub">Updated: ${updatedAt}</div>
</div>
<div class="timeline-step">
<div class="step-dot"><i class="fa-solid fa-house"></i></div>
<div class="step-title">Delivered to Recipient</div>
<div class="step-sub">Destination: ${recipientAddress}</div>
</div>
</div>
<div class="info-grid">
<div class="info-item">
<div class="info-item-label">Customer</div>
<div class="info-item-val">${recipientName}</div>
</div>
<div class="info-item">
<div class="info-item-label">Cash on Delivery (COD)</div>
<div class="info-item-val">৳ ${Number(codAmount).toLocaleString()}</div>
</div>
</div>
<div class="actions">
${shortLink ? `<a href="${shortLink}" target="_blank" class="btn btn-primary"><i class="fa-solid fa-location-arrow"></i> Official Carrier Map Tracker</a>` : ''}
<button onclick="window.location.reload()" class="btn btn-secondary"><i class="fa-solid fa-rotate-right"></i> Refresh Live Status</button>
</div>
</div>
</body>
</html>`);
});
app.listen(PORT, () => { app.listen(PORT, () => {
console.log(`Aura Courier MCP (HTTP) listening on :${PORT} — POST /mcp`); console.log(`Aura Courier MCP (HTTP) listening on :${PORT} — POST /mcp`);
}); });