Skip to content

Commit f234f75

Browse files
committed
migrated to fastify v5; updated 'usb' and 'color' package
1 parent 668e4ee commit f234f75

3 files changed

Lines changed: 137 additions & 119 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* Replaced deprecated 'onoff' package with 'rpi-io' for GPIO control
66
* Replaced out-dated package '@abandonware/noble' with '@stoprocent/noble'
77
* Replaced out-dated package 'node-beacon-scanner' with custom fork
8+
* Migrated to fastify v5 and updated several other packages
89

910
## v0.9.2
1011
* New gpio-itme 'rpi-spi-rgb-leds' to control WS281X and APA102 LEDs via GPIO SPI

package.json

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@
1616
"BLE",
1717
"Eddystone",
1818
"Beacon",
19-
"GPIO",
20-
"Raspberry",
21-
"Remote-Control",
19+
"GPIO",
20+
"Raspberry",
21+
"Remote-Control",
2222
"Browser",
23-
"Extension",
23+
"Extension",
2424
"Client-Server",
25-
"SPI",
26-
"LED",
27-
"APA102",
28-
"WS281X"
25+
"SPI",
26+
"LED",
27+
"APA102",
28+
"WS281X"
2929
],
3030
"author": "Bytemind.de",
3131
"license": "MIT",
@@ -37,13 +37,13 @@
3737
"node": ">=20.19.0"
3838
},
3939
"dependencies": {
40+
"fastify": "^5.7.4",
41+
"@fastify/static": "^8.3.0",
42+
"@fastify/websocket": "^11.2.0",
4043
"node-beacon-scanner": "git+https://github.com/bytemind-de/node-beacon-scanner.git",
41-
"fastify": "^2.15.3",
42-
"fastify-static": "^2.7.0",
43-
"fastify-ws": "^1.0.3",
44-
"rpi-io": "^2.0.10",
4544
"spi-device": "^3.1.2",
46-
"usb": "^2.0.3",
47-
"color": "^4.2.3"
45+
"usb": "^2.17.0",
46+
"color": "^5.0.3",
47+
"rpi-io": "^2.0.10"
4848
}
4949
}

server.js

Lines changed: 122 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
const path = require('path');
44
const fs = require('fs');
55
const fastify = require('fastify');
6-
const fastify_static = require('fastify-static');
7-
const fastify_ws = require('fastify-ws');
6+
const fastify_static = require('@fastify/static');
7+
const fastify_ws = require('@fastify/websocket');
88

99
//Server
1010
const version = "0.10.0";
@@ -21,7 +21,7 @@ var Clexi = function(customSettings){
2121
var idIsPassword = customSettings.idIsPassword || settings.idIsPassword || false;
2222

2323
var sslCertPath = customSettings.sslCertPath || path.join(__dirname, "ssl");
24-
var wwwPath = customSettings.wwwPath || path.join(__dirname, "www");
24+
var wwwPath = path.join(__dirname, customSettings.wwwPath || "www");
2525
var defaultXtensionsPath = path.join(__dirname, 'xtensions');
2626
var customXtensionsPath = customSettings.customXtensionsPath || defaultXtensionsPath;
2727
var customXtensions = customSettings.customXtensions || [];
@@ -49,11 +49,13 @@ var Clexi = function(customSettings){
4949
const server = fastify(server_options);
5050
ClexiServer.fastify = server;
5151

52-
//Plugins
52+
//Register static files plugin and path
5353
server.register(fastify_static, {
5454
root: wwwPath,
55-
redirect: false //for fastify 2 security - see: https://github.com/advisories/GHSA-p6vg-p826-qp3v
55+
redirect: false
5656
});
57+
58+
//Register Websocket plugin
5759
server.register(fastify_ws);
5860

5961
//Xtensions
@@ -118,32 +120,29 @@ var Clexi = function(customSettings){
118120
//single receiver?
119121
var client = data.receiver;
120122
delete data.receiver; //remove object before sending
121-
if (!idIsPassword || (idIsPassword && client.authState)){
122-
if (typeof data === "object"){
123-
client.send(JSON.stringify(data));
124-
}else{
125-
client.send(data);
126-
}
127-
}else{
128-
server.log.error("Tried to broadcast to unauthorized client. Client should be disconnected already!");
129-
client.terminate();
130-
}
123+
sendToClient(client, data);
131124
}else{
132125
//broadcast to all
133-
server.ws.clients.forEach(function each(client){
134-
if (client.readyState === 1) { //WebSocket.OPEN should be 1
135-
if (!idIsPassword || (idIsPassword && client.authState)){
136-
if (typeof data === "object"){
137-
client.send(JSON.stringify(data));
138-
}else{
139-
client.send(data);
140-
}
141-
}else{
142-
server.log.error("Tried to broadcast to unauthorized client. Client should be disconnected already!");
143-
client.terminate();
126+
if (server.websocketServer){
127+
server.websocketServer.clients.forEach(function each(client){
128+
if (client.readyState === 1) {
129+
sendToClient(client, data);
144130
}
145-
}
146-
});
131+
});
132+
}
133+
}
134+
}
135+
function sendToClient(client, data){
136+
if (!idIsPassword || (idIsPassword && client.authState)){
137+
if (typeof data === "object"){
138+
client.send(JSON.stringify(data));
139+
}else{
140+
client.send(data);
141+
}
142+
}else{
143+
//make sure all unauthorized clients are removed
144+
server.log.error("Tried to broadcast to unauthorized client. Client should be disconnected already!");
145+
client.terminate();
147146
}
148147
}
149148

@@ -198,10 +197,103 @@ var Clexi = function(customSettings){
198197
reply.send();
199198
}
200199
}
200+
201+
//Websocket interface
202+
server.register(async function(fastify){
203+
//default
204+
fastify.get('/', {websocket: true}, (socket, request) => {
205+
handleWebsocketReq(socket, request, '/');
206+
});
207+
//popular client path
208+
fastify.get('/clexi', {websocket: true}, (socket, request) => {
209+
handleWebsocketReq(socket, request, '/clexi');
210+
});
211+
//fallback for common proxy paths
212+
fastify.get('/ws', {websocket: true}, (socket, request) => {
213+
handleWebsocketReq(socket, request, '/ws');
214+
});
215+
});
216+
function handleWebsocketReq(socket, request, path){
217+
server.log.info("Client connected via WebSocket path '" + path + "'.");
218+
219+
socket.on('message', function(msg){
220+
try {
221+
//msg is a buffer
222+
let msgObj = JSON.parse(msg.toString());
223+
224+
//Handle extensions input
225+
if (msgObj.type && xtensions[msgObj.type]){
226+
server.log.info('Calling xtensions: ' + msgObj.type);
227+
let response = xtensions[msgObj.type].input(msgObj, socket);
228+
if (response){
229+
socket.send(JSON.stringify({
230+
response: response,
231+
type: msgObj.type,
232+
id: msgObj.id
233+
}));
234+
}
235+
236+
//Welcome
237+
}else if (msgObj.type == "welcome"){
238+
//check server ID
239+
if (msgObj.data && msgObj.data.server_id == serverId){
240+
socket.authState = true;
241+
}
242+
if (!idIsPassword){
243+
socket.send(JSON.stringify({
244+
type: "welcome",
245+
code: 200,
246+
info: {
247+
id: serverId,
248+
version: ("CLEXI Node.js server v" + version),
249+
xtensions: getXtensionsInfo()
250+
}
251+
}));
252+
}else if (idIsPassword && socket.authState){
253+
socket.send(JSON.stringify({
254+
type: "welcome",
255+
code: 200,
256+
info: {
257+
version: ("CLEXI Node.js server v" + version),
258+
xtensions: getXtensionsInfo()
259+
}
260+
}));
261+
}else{
262+
socket.send(JSON.stringify({
263+
type: "welcome",
264+
code: 401,
265+
info: {
266+
msg: "not authorized",
267+
version: ("CLEXI Node.js server v" + version)
268+
}
269+
}));
270+
//socket.terminate(); //the client should gracefully disconnect O_O
271+
}
272+
273+
//undefined
274+
}else{
275+
socket.send(JSON.stringify({
276+
response: ("Unknown message type: " + msgObj.type),
277+
type: "undefined"
278+
}));
279+
}
280+
}catch(e){
281+
server.log.error("Socket Message Error: " + e.message);
282+
}
283+
});
284+
285+
socket.on('close', function(){
286+
server.log.info('Client disconnected.');
287+
});
288+
289+
socket.on('error', function(e){
290+
server.log.error(`Client error: ${e.message}`);
291+
});
292+
}
201293

202294
//Run the server
203295
ClexiServer.start = function(){
204-
server.listen(port, hostname, function(err, address){
296+
server.listen({port: port, host: hostname}, function(err, address){
205297
if (err){
206298
server.log.error(err);
207299
process.exit(1);
@@ -211,87 +303,12 @@ var Clexi = function(customSettings){
211303
console.log(`Hostname: ${hostname} - SSL: ${useSsl}`);
212304
server.log.info(`Server running at ${address}`);
213305

214-
//Websocket interface
215-
server.ws.on('connection', function(socket, request){
216-
server.log.info('Client connected.');
217-
218-
//CLIENT INPUT
219-
socket.on('message', function(msg){
220-
let msgObj = JSON.parse(msg);
221-
222-
//Handle extensions input
223-
if (msgObj.type && xtensions[msgObj.type]){
224-
server.log.info('Calling xtensions: ' + msgObj.type);
225-
let response = xtensions[msgObj.type].input(msgObj, socket);
226-
if (response){
227-
socket.send(JSON.stringify({
228-
response: response,
229-
type: msgObj.type,
230-
id: msgObj.id
231-
}));
232-
}
233-
234-
//Welcome
235-
}else if (msgObj.type && msgObj.type == "welcome"){
236-
//check server ID
237-
if (msgObj.data && msgObj.data.server_id && msgObj.data.server_id == serverId){
238-
socket.authState = true;
239-
}
240-
if (!idIsPassword){
241-
socket.send(JSON.stringify({
242-
type: "welcome",
243-
code: 200,
244-
info: {
245-
id: serverId,
246-
version: ("CLEXI Node.js server v" + version),
247-
xtensions: getXtensionsInfo()
248-
}
249-
}));
250-
}else if (idIsPassword && socket.authState){
251-
socket.send(JSON.stringify({
252-
type: "welcome",
253-
code: 200,
254-
info: {
255-
version: ("CLEXI Node.js server v" + version),
256-
xtensions: getXtensionsInfo()
257-
}
258-
}));
259-
}else{
260-
socket.send(JSON.stringify({
261-
type: "welcome",
262-
code: 401,
263-
info: {
264-
msg: "not authorized",
265-
version: ("CLEXI Node.js server v" + version)
266-
}
267-
}));
268-
//socket.terminate(); //the client should gracefully disconnect O_O
269-
}
270-
271-
//undefined
272-
}else{
273-
socket.send(JSON.stringify({
274-
response: ("Unknown message type: " + msgObj.type),
275-
type: "undefined"
276-
}));
277-
}
278-
});
279-
280-
socket.on('close', function(){
281-
server.log.info('Client disconnected.');
282-
});
283-
284-
socket.on('error', function(e){
285-
server.log.error(`Client error: ${e.message}`);
286-
});
287-
});
288-
289306
//Load extensions
290307
loadXtensions();
291308
});
292309
}
293310

294-
ClexiServer.stop = server.close;
311+
ClexiServer.stop = () => server.close();
295312

296313
return ClexiServer;
297314
}

0 commit comments

Comments
 (0)