diff --git a/booking-checker.html b/booking-checker.html
new file mode 100644
index 000000000..9d2b7ded4
--- /dev/null
+++ b/booking-checker.html
@@ -0,0 +1,530 @@
+
+
+
+
+
+ EDR Booking Checker
+
+
+
+
+EDR Booking Checker
+
+
+
+
+
No trailing slash. e.g. https://api.edrsc.com
+
+
+
+
+
+
Supports any format: one per line, comma-separated, or {REF1,REF2} groups.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Journey |
+ Duplicate Bookings |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/booking-extractor.html b/booking-extractor.html
new file mode 100644
index 000000000..844c98b2c
--- /dev/null
+++ b/booking-extractor.html
@@ -0,0 +1,256 @@
+
+
+
+
+
+ EDR Booking Extractor
+
+
+
+
+EDR Booking Extractor
+
+
+
+
+
+ Drop bookings.json here or click to browse
+
+
Accepts a JSON array of bookings or an object with a bookings key.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | # |
+ Booking Ref |
+ Status |
+ Booking Type |
+ Phone |
+ Email |
+ Departure |
+ Origin |
+ Destination |
+ Passenger(s) |
+ Coach - Seat |
+ Payment Method |
+ Payment Status |
+ Total (DJF) |
+ Created At |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/booking-proxy.mjs b/booking-proxy.mjs
new file mode 100644
index 000000000..27f9248ee
--- /dev/null
+++ b/booking-proxy.mjs
@@ -0,0 +1,53 @@
+import http from 'http';
+import https from 'https';
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const PORT = 8080;
+const __dir = path.dirname(fileURLToPath(import.meta.url));
+
+const server = http.createServer((req, res) => {
+ res.setHeader('Access-Control-Allow-Origin', '*');
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
+
+ if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; }
+
+ // Serve any .html file in the same directory
+ if (req.url === '/' || req.url.endsWith('.html')) {
+ const filename = req.url === '/' ? 'booking-checker.html' : req.url.slice(1);
+ const filepath = path.join(__dir, filename);
+ if (fs.existsSync(filepath)) {
+ res.writeHead(200, { 'Content-Type': 'text/html' });
+ fs.createReadStream(filepath).pipe(res);
+ } else {
+ res.writeHead(404); res.end('Not found');
+ }
+ return;
+ }
+
+ // Proxy /proxy?url=
+ if (req.url.startsWith('/proxy?url=')) {
+ const target = decodeURIComponent(req.url.slice('/proxy?url='.length));
+ const parsed = new URL(target);
+ const mod = parsed.protocol === 'https:' ? https : http;
+ const options = {
+ hostname: parsed.hostname,
+ port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
+ path: parsed.pathname + parsed.search,
+ method: req.method,
+ headers: { ...req.headers, host: parsed.hostname },
+ };
+ const proxy = mod.request(options, (apiRes) => {
+ res.writeHead(apiRes.statusCode, apiRes.headers);
+ apiRes.pipe(res);
+ });
+ proxy.on('error', (e) => { res.writeHead(502); res.end(e.message); });
+ req.pipe(proxy);
+ return;
+ }
+
+ res.writeHead(404); res.end();
+});
+
+server.listen(PORT, () => console.log(`Booking checker: http://localhost:${PORT}/booking-checker.html`));
diff --git a/ticket-extractor.html b/ticket-extractor.html
new file mode 100644
index 000000000..17be60576
--- /dev/null
+++ b/ticket-extractor.html
@@ -0,0 +1,239 @@
+
+
+
+
+
+ EDR Ticket Extractor
+
+
+
+
+EDR Ticket Extractor
+
+
+
+
+
+ Drop tickets.json here or click to browse
+
+
Accepts a JSON array of tickets or an object with a tickets key.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | # |
+ Ticket No. |
+ Booking Ref |
+ Passenger |
+ Phone |
+ Email |
+ Journey Type |
+ Origin |
+ Destination |
+ Seat Class |
+ Coach |
+ Seat |
+
+
+
+
+
+
+
+
+
+
+