2019-09-20 15:46:05 -04:00
|
|
|
import * as fs from 'fs';
|
|
|
|
import * as url from 'url';
|
2019-09-20 17:48:40 -04:00
|
|
|
import * as path from 'path';
|
2019-09-20 15:46:05 -04:00
|
|
|
|
2019-09-20 17:48:40 -04:00
|
|
|
import * as express from 'express';
|
2019-09-20 15:46:05 -04:00
|
|
|
import fetch from 'node-fetch';
|
2019-09-20 17:48:40 -04:00
|
|
|
import * as httpProxy from 'http-proxy';
|
2019-09-20 15:46:05 -04:00
|
|
|
import * as WebSocket from 'ws';
|
|
|
|
import * as yaml from 'js-yaml';
|
2019-09-20 17:48:40 -04:00
|
|
|
import * as Bundler from 'parcel-bundler';
|
2019-09-24 10:53:51 -04:00
|
|
|
import * as expressWs from 'express-ws';
|
2019-09-20 17:48:40 -04:00
|
|
|
|
|
|
|
import * as messages from './messages';
|
|
|
|
import * as octoprint from './octoprint';
|
2019-09-20 15:46:05 -04:00
|
|
|
|
2020-02-19 20:43:27 -05:00
|
|
|
const PORT = process.env.PORT || 1234;
|
|
|
|
|
2019-09-20 15:46:05 -04:00
|
|
|
// Load config
|
|
|
|
const config: {
|
|
|
|
printers: { address: string; apikey: string }[];
|
|
|
|
} = yaml.safeLoad(fs.readFileSync('config.yaml', 'utf8'));
|
|
|
|
|
2019-09-20 17:48:40 -04:00
|
|
|
const proxy = httpProxy.createProxyServer({});
|
2020-02-19 18:37:32 -05:00
|
|
|
proxy.on('error', function(e) {
|
|
|
|
console.error('Proxy failed:');
|
|
|
|
console.error(e);
|
|
|
|
});
|
2019-09-20 15:46:05 -04:00
|
|
|
let printerStatuses: PrinterStatus[] = [];
|
|
|
|
|
|
|
|
function broadcast(data: WebSocket.Data) {
|
2019-09-24 10:53:51 -04:00
|
|
|
wsInstance.getWss().clients.forEach((client: WebSocket) => {
|
2019-09-20 15:46:05 -04:00
|
|
|
if (client.readyState === WebSocket.OPEN) {
|
|
|
|
client.send(data);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function broadcastPayload(payload: messages.ExtendedMessage) {
|
|
|
|
broadcast(JSON.stringify(payload));
|
|
|
|
}
|
|
|
|
|
2019-09-24 10:53:51 -04:00
|
|
|
const wsInstance = expressWs(express());
|
|
|
|
const app = wsInstance.app;
|
|
|
|
|
|
|
|
app.ws('/ws', function(ws, req) {
|
|
|
|
printerStatuses.forEach((ps: PrinterStatus) => ps.send_init(ws));
|
|
|
|
});
|
2019-09-20 17:48:40 -04:00
|
|
|
|
|
|
|
app.get('/webcam/:printer', (req, res) => {
|
|
|
|
let printer: PrinterStatus | undefined = printerStatuses.find(
|
|
|
|
p => p.name === req.params.printer
|
|
|
|
);
|
2020-02-19 18:38:30 -05:00
|
|
|
if (printer?.webcamURL) {
|
2019-10-22 12:59:12 -04:00
|
|
|
req.url = ''; // truncate the url for passing to the proxy
|
2019-09-20 17:48:40 -04:00
|
|
|
proxy.web(req, res, { target: printer.webcamURL });
|
2019-10-22 12:59:12 -04:00
|
|
|
} else res.status(404).send('Not Found: Printer not known or has no webcam.');
|
2019-09-20 17:48:40 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
let bundler = new Bundler(path.join(__dirname, 'index.html'));
|
|
|
|
app.use(bundler.middleware());
|
|
|
|
|
2020-02-19 20:43:27 -05:00
|
|
|
app.listen(PORT);
|
2019-09-20 17:48:40 -04:00
|
|
|
|
2019-09-20 15:46:05 -04:00
|
|
|
class PrinterStatus {
|
|
|
|
address: string;
|
|
|
|
apikey: string;
|
|
|
|
|
|
|
|
webcamURL?: string;
|
|
|
|
name?: string;
|
|
|
|
|
|
|
|
websocket?: WebSocket;
|
|
|
|
lastStatus?: messages.ExtendedMessage;
|
|
|
|
|
2019-09-24 10:53:51 -04:00
|
|
|
constructor(address: string, apikey: string) {
|
2019-09-20 15:46:05 -04:00
|
|
|
this.address = address;
|
|
|
|
this.apikey = apikey;
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.init(); // async init
|
|
|
|
} catch (e) {
|
|
|
|
throw 'Failed to Init' + e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async init() {
|
|
|
|
// TODO: error handling (try/catch)
|
|
|
|
const settings = await this.api_get('settings');
|
|
|
|
this.webcamURL = settings.webcam.streamUrl;
|
2020-02-19 18:38:30 -05:00
|
|
|
if (this.webcamURL?.startsWith('/')) {
|
|
|
|
this.webcamURL = this.address + this.webcamURL;
|
|
|
|
}
|
2019-09-20 15:46:05 -04:00
|
|
|
this.name = settings.appearance.name;
|
|
|
|
|
|
|
|
// do passive login to get a session key from the API key
|
|
|
|
const login: octoprint.LoginResponse = await this.api_post('login', {
|
|
|
|
passive: 'true',
|
|
|
|
});
|
|
|
|
|
|
|
|
this.websocket = new WebSocket(
|
|
|
|
url.resolve(this.address, '/sockjs/websocket')
|
|
|
|
);
|
|
|
|
this.websocket
|
|
|
|
.on('open', () => {
|
|
|
|
this.websocket!.send(
|
|
|
|
JSON.stringify({ auth: login.name + ':' + login.session })
|
|
|
|
);
|
|
|
|
})
|
|
|
|
.on('message', (data: WebSocket.Data) => {
|
|
|
|
const event: octoprint.Message = JSON.parse(data as string);
|
|
|
|
console.log(event);
|
|
|
|
|
|
|
|
let ext_event: messages.ExtendedMessage = {
|
|
|
|
...event,
|
|
|
|
printer: this.name!,
|
|
|
|
};
|
|
|
|
broadcastPayload(ext_event);
|
|
|
|
|
|
|
|
if ('current' in event || 'history' in event) {
|
|
|
|
this.lastStatus = ext_event;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async api_get(endpoint: string): Promise<any> {
|
|
|
|
const r = await fetch(url.resolve(this.address, '/api/' + endpoint), {
|
|
|
|
headers: { 'X-Api-Key': this.apikey },
|
|
|
|
});
|
|
|
|
return await r.json();
|
|
|
|
}
|
|
|
|
|
|
|
|
async api_post(endpoint: string, data: any): Promise<any> {
|
|
|
|
const r = await fetch(url.resolve(this.address, '/api/' + endpoint), {
|
|
|
|
headers: {
|
|
|
|
'X-Api-Key': this.apikey,
|
|
|
|
Accept: 'application/json',
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
},
|
|
|
|
method: 'POST',
|
|
|
|
body: JSON.stringify(data),
|
|
|
|
});
|
|
|
|
return await r.json();
|
|
|
|
}
|
|
|
|
|
|
|
|
send_init(ws: WebSocket) {
|
|
|
|
let payload: messages.ExtendedMessage;
|
|
|
|
console.log(this.lastStatus);
|
|
|
|
if (this.lastStatus) {
|
|
|
|
payload = this.lastStatus;
|
|
|
|
} else {
|
|
|
|
payload = { init: null, printer: this.name! };
|
|
|
|
}
|
|
|
|
ws.send(JSON.stringify(payload));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-24 10:53:51 -04:00
|
|
|
function initPrinters() {
|
2019-09-20 15:46:05 -04:00
|
|
|
printerStatuses = config.printers.map(
|
2019-09-24 10:53:51 -04:00
|
|
|
printer => new PrinterStatus(printer.address, printer.apikey)
|
2019-09-20 15:46:05 -04:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-09-24 10:53:51 -04:00
|
|
|
initPrinters();
|