mirror of
https://github.com/jambonz/jambonz-webapp.git
synced 2025-12-19 05:37:43 +00:00
268 lines
7.1 KiB
TypeScript
268 lines
7.1 KiB
TypeScript
import fs from "fs";
|
|
import path from "path";
|
|
import cors from "cors";
|
|
import express from "express";
|
|
import { nanoid } from "nanoid";
|
|
|
|
import type { Request, Response } from "express";
|
|
import type {
|
|
Alert,
|
|
RecentCall,
|
|
PageQuery,
|
|
CallQuery,
|
|
PagedResponse,
|
|
} from "../src/api/types";
|
|
|
|
const app = express();
|
|
const port = 3002;
|
|
|
|
app.use(cors());
|
|
|
|
/** RecentCalls mock API responses for local dev */
|
|
app.get(
|
|
"/api/Accounts/:account_sid/RecentCalls",
|
|
(req: Request, res: Response) => {
|
|
const data: RecentCall[] = [];
|
|
const points = 500;
|
|
const start = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
|
|
const now = new Date();
|
|
const increment = (now.getTime() - start.getTime()) / points;
|
|
|
|
for (let i = 0; i < 500; i++) {
|
|
const attempted_at = new Date(start.getTime() + i * increment);
|
|
const failed = 0 === i % 5;
|
|
const call_sid = nanoid();
|
|
const call: RecentCall = {
|
|
account_sid: req.params.account_sid,
|
|
call_sid,
|
|
from: "15083084809",
|
|
to: "18882349999",
|
|
answered: !failed,
|
|
sip_callid: `${nanoid()}@192.168.1.100`,
|
|
sip_status: 200,
|
|
duration: failed ? 0 : 45,
|
|
attempted_at: attempted_at.getTime(),
|
|
answered_at: attempted_at.getTime() + 3000,
|
|
terminated_at: attempted_at.getTime() + 45000,
|
|
termination_reason: "caller hungup",
|
|
host: "192.168.1.100",
|
|
remote_host: "3.55.24.34",
|
|
direction: 0 === i % 2 ? "inbound" : "outbound",
|
|
trunk: 0 === i % 2 ? "twilio" : "user",
|
|
trace_id: nanoid(),
|
|
recording_url: `http://127.0.0.1:3002/api/Accounts/${req.params.account_sid}/RecentCalls/${call_sid}/record`,
|
|
};
|
|
data.push(call);
|
|
}
|
|
|
|
const query: CallQuery = {
|
|
...req.query,
|
|
page: Number(req.query.page),
|
|
count: Number(req.query.count),
|
|
};
|
|
|
|
let filtered = data;
|
|
|
|
if (query.start) {
|
|
filtered = filtered.filter((call) => {
|
|
return call.attempted_at >= new Date(query.start!).getTime();
|
|
});
|
|
}
|
|
|
|
console.log("RecentCalls: filtered", filtered.length);
|
|
|
|
if (query.days) {
|
|
filtered = filtered.filter((call) => {
|
|
return (
|
|
call.attempted_at >=
|
|
new Date(Date.now() - query.days! * 24 * 60 * 60 * 1000).getTime()
|
|
);
|
|
});
|
|
}
|
|
|
|
console.log("RecentCalls: filtered", filtered.length);
|
|
|
|
if (query.direction) {
|
|
filtered = filtered.filter((call) => {
|
|
return call.direction === query.direction;
|
|
});
|
|
}
|
|
|
|
console.log("RecentCalls: filtered", filtered.length);
|
|
|
|
if (query.answered) {
|
|
filtered = filtered.filter((call) => {
|
|
return call.answered.toString() === query.answered;
|
|
});
|
|
}
|
|
|
|
console.log("RecentCalls: filtered", filtered.length);
|
|
|
|
const begin = (query.page - 1) * query.count;
|
|
const end = begin + query.count;
|
|
const paged = filtered.slice(begin, end);
|
|
|
|
console.log("RecentCalls: paged", paged.length);
|
|
console.log("---");
|
|
|
|
res.status(200).json(<PagedResponse<RecentCall>>{
|
|
page_size: query.count,
|
|
total: filtered.length,
|
|
page: query.page,
|
|
data: paged,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/api/Accounts/:account_sid/RecentCalls/:call_sid",
|
|
(req: Request, res: Response) => {
|
|
res.status(200).json({ total: Math.random() > 0.5 ? 1 : 0 });
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/api/Accounts/:account_sid/RecentCalls/:call_sid/pcap",
|
|
(req: Request, res: Response) => {
|
|
/** Sample pcap file from: https://wiki.wireshark.org/SampleCaptures#sip-and-rtp */
|
|
const pcap: Buffer = fs.readFileSync(
|
|
path.resolve(process.cwd(), "server", "sample-sip-rtp-traffic.pcap"),
|
|
);
|
|
|
|
res
|
|
.status(200)
|
|
.set({
|
|
"Content-Type": "application/octet-stream",
|
|
"Content-Disposition": "attachment",
|
|
})
|
|
.send(pcap); // server: Buffer => client: Blob
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/api/Accounts/:account_sid/RecentCalls/:call_sid/record",
|
|
(req: Request, res: Response) => {
|
|
/** Sample pcap file from: https://wiki.wireshark.org/SampleCaptures#sip-and-rtp */
|
|
const wav: Buffer = fs.readFileSync(
|
|
path.resolve(process.cwd(), "server", "example.mp3"),
|
|
);
|
|
|
|
res
|
|
.status(200)
|
|
.set({
|
|
"Content-Type": "audio/wav",
|
|
"Content-Disposition": "attachment",
|
|
})
|
|
.send(wav); // server: Buffer => client: Blob
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/api/Accounts/:account_sid/RecentCalls/trace/:trace_id",
|
|
(req: Request, res: Response) => {
|
|
const json = fs.readFileSync(
|
|
path.resolve(process.cwd(), "server", "sample-jaeger.json"),
|
|
{ encoding: "utf8" },
|
|
);
|
|
res.status(200).json(JSON.parse(json));
|
|
},
|
|
);
|
|
|
|
/** Alerts mock API responses for local dev */
|
|
app.get("/api/Accounts/:account_sid/Alerts", (req: Request, res: Response) => {
|
|
const data: Alert[] = [];
|
|
const points = 500;
|
|
const start = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
|
|
const now = new Date();
|
|
const increment = (now.getTime() - start.getTime()) / points;
|
|
const url = "http://foo.bar";
|
|
const vendor = "google";
|
|
const count = 500;
|
|
|
|
for (let i = 0; i < 500; i++) {
|
|
const time = new Date(start.getTime() + i * increment);
|
|
const scenario = i % 5;
|
|
let alert_type = "";
|
|
let message = "";
|
|
|
|
switch (scenario) {
|
|
case 0:
|
|
alert_type = "webhook-failure";
|
|
message = `${url} returned 404`;
|
|
break;
|
|
case 1:
|
|
alert_type = "webhook-connection-failure";
|
|
message = `failed to connect to ${url}`;
|
|
break;
|
|
case 2:
|
|
alert_type = "no-tts";
|
|
message = `text to speech credentials for ${vendor} have not been provisioned`;
|
|
break;
|
|
case 3:
|
|
alert_type = "no-carrier";
|
|
message = "outbound call failure: no carriers have been provisioned";
|
|
break;
|
|
case 4:
|
|
alert_type = "call-limit";
|
|
message = `you have exceeded your provisioned call limit of ${count}; please consider upgrading your plan`;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
const alert: Alert = {
|
|
account_sid: req.params.account_sid,
|
|
time: time.getTime(),
|
|
alert_type,
|
|
message,
|
|
detail: "",
|
|
};
|
|
data.push(alert);
|
|
}
|
|
|
|
const query: PageQuery = {
|
|
...req.query,
|
|
page: Number(req.query.page),
|
|
count: Number(req.query.count),
|
|
};
|
|
|
|
let filtered = data;
|
|
|
|
if (query.start) {
|
|
filtered = filtered.filter((call) => {
|
|
return call.time >= new Date(query.start!).getTime();
|
|
});
|
|
}
|
|
|
|
console.log("Alerts: filtered", filtered.length);
|
|
|
|
if (query.days) {
|
|
filtered = filtered.filter((call) => {
|
|
return (
|
|
call.time >=
|
|
new Date(Date.now() - query.days! * 24 * 60 * 60 * 1000).getTime()
|
|
);
|
|
});
|
|
}
|
|
|
|
console.log("Alerts: filtered", filtered.length);
|
|
|
|
const begin = (query.page - 1) * query.count;
|
|
const end = begin + query.count;
|
|
const paged = filtered.slice(begin, end);
|
|
|
|
console.log("Alerts: paged", paged.length);
|
|
console.log("---");
|
|
|
|
res.status(200).json(<PagedResponse<Alert>>{
|
|
page_size: query.count,
|
|
total: filtered.length,
|
|
page: query.page,
|
|
data: paged,
|
|
});
|
|
});
|
|
|
|
app.listen(port, () => {
|
|
console.log(`express server listening on port ${port}`);
|
|
});
|