Files
ESP3DLib/embedded/config/server.js
Luc db978d3f8b Hard code websocket sub protocol in embedded page
Allow to customize SDIO pins like in ESP3D
Use ISO 8601 date time format for ESP140 and ESP800
Fix websocket disconnection during long upload / download
Change download packet from 1024 to 2048
Bump version
2022-09-11 19:06:02 +08:00

674 lines
18 KiB
JavaScript

const express = require("express");
const chalk = require("chalk");
let path = require("path");
const fs = require("fs");
const port = 8080;
/*
* Web Server for development
* Web Socket server for development
*/
const wscolor = chalk.cyan;
const expresscolor = chalk.green;
const commandcolor = chalk.white;
const WebSocket = require("ws");
let currentID = 0;
const app = express();
const fileUpload = require("express-fileupload");
let serverpath = path.normalize(__dirname + "/../server/public/");
let sdpath = path.normalize(__dirname + "/../server/sd/");
let WebSocketServer = require("ws").Server,
wss = new WebSocketServer({ port: 81,handleProtocols:function(protocol) {console.log( "protocol received from client " + protocol );
return "webui-v3";
return null;}});
app.use(fileUpload({ preserveExtension: true, debug: false }));
app.listen(port, () =>
console.log(expresscolor(`[express] Listening on port ${port}!`))
);
//app.use(express.urlencoded({ extended: false }));
function SendBinary(text) {
const array = new Uint8Array(text.length);
for (let i = 0; i < array.length; ++i) {
array[i] = text.charCodeAt(i);
}
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(array);
}
});
}
app.post("/login", function (req, res) {
res.send("");
return;
});
app.get("/config", function (req, res) {
res.send(
"chip id: 56398\nCPU Freq: 240 Mhz<br/>" +
"CPU Temp: 58.3 C<br/>" +
"free mem: 212.36 KB<br/>" +
"SDK: v3.2.3-14-gd3e562907<br/>" +
"flash size: 4.00 MB<br/>" +
"size for update: 1.87 MB<br/>" +
"FS type: LittleFS<br/>" +
"FS usage: 104.00 KB/192.00 KB<br/>" +
"baud: 115200<br/>" +
"sleep mode: none<br/>" +
"wifi: ON<br/>" +
"hostname: esp3d<br/>" +
"HTTP port: 80<br/>" +
"Telnet port: 23<br/>" +
"WebDav port: 8383<br/>" +
"sta: ON<br/>" +
"mac: 80:7D:3A:C4:4E:DC<br/>" +
"SSID: WIFI_OFFICE_A2G<br/>" +
"signal: 100 %<br/>" +
"phy mode: 11n<br/>" +
"channel: 11<br/>" +
"ip mode: dhcp<br/>" +
"ip: 192.168.1.61<br/>" +
"gw: 192.168.1.1<br/>" +
"msk: 255.255.255.0<br/>" +
"DNS: 192.168.1.1<br/>" +
"ap: OFF<br/>" +
"mac: 80:7D:3A:C4:4E:DD<br/>" +
"serial: ON<br/>" +
"notification: OFF<br/>" +
"Target Fw: repetier<br/>" +
"FW ver: 3.0.0.a91<br/>" +
"FW arch: ESP32 "
);
return;
});
app.get("/command", function (req, res) {
console.log(commandcolor(`[server]/command params: ${req.query.cmd}`));
let url = req.query.cmd;
if (url.startsWith("[ESP800]json")) {
res.json(
{
cmd: "800",
status: "ok",
data:{
FWVersion: "2.0.9.3+-3.0.0.a111",
FWTarget: 30,
SDConnection: "none",
Authentication: "Disabled",
WebCommunication: "Synchronous",
WebSocketIP: "localhost",
WebSocketPort: "81",
Hostname: "esp3d",
WiFiMode: "STA",
WebUpdate: "Enabled",
FlashFileSystem: "LittleFs",
HostPath: "/",
Time: "none",
}
}
);
return;
}
if (url.indexOf("ESP111") != -1) {
res.send("192.168.1.111");
return;
}
if (url.indexOf("ESP420") != -1) {
res.json({
Status: [
{ id: "chip id", value: "38078" },
{ id: "CPU Freq", value: "240 Mhz" },
{ id: "CPU Temp", value: "50.6 C" },
{ id: "free mem", value: "217.50 KB" },
{ id: "SDK", value: "v3.3.1-61-g367c3c09c" },
{ id: "flash size", value: "4.00 MB" },
{ id: "size for update", value: "1.87 MB" },
{ id: "FS type", value: "SPIFFS" },
{ id: "FS usage", value: "39.95 KB/169.38 KB" },
{ id: "baud", value: "115200" },
{ id: "sleep mode", value: "none" },
{ id: "wifi", value: "ON" },
{ id: "hostname", value: "esp3d" },
{ id: "HTTP port", value: "80" },
{ id: "Telnet port", value: "23" },
{ id: "Ftp ports", value: "21, 20, 55600" },
{ id: "sta", value: "ON" },
{ id: "mac", value: "30:AE:A4:21:BE:94" },
{ id: "SSID", value: "WIFI_OFFICE_B2G" },
{ id: "signal", value: "100 %" },
{ id: "phy mode", value: "11n" },
{ id: "channel", value: "2" },
{ id: "ip mode", value: "dhcp" },
{ id: "ip", value: "192.168.1.43" },
{ id: "gw", value: "192.168.1.1" },
{ id: "msk", value: "255.255.255.0" },
{ id: "DNS", value: "192.168.1.1" },
{ id: "ap", value: "OFF" },
{ id: "mac", value: "30:AE:A4:21:BE:95" },
{ id: "serial", value: "ON" },
{ id: "notification", value: "OFF" },
{ id: "FW ver", value: "3.0.0.a28" },
{ id: "FW arch", value: "ESP32" },
],
});
return;
}
if (url.indexOf("ESP410") != -1) {
res.json({
AP_LIST: [
{
SSID: "HP-Setup>71-M277 LaserJet",
SIGNAL: "92",
IS_PROTECTED: "0",
},
{ SSID: "WIFI_OFFICE_B2G", SIGNAL: "88", IS_PROTECTED: "1" },
{ SSID: "NETGEAR70", SIGNAL: "66", IS_PROTECTED: "1" },
{ SSID: "ZenFone6&#39;luc", SIGNAL: "48", IS_PROTECTED: "1" },
{ SSID: "Livebox-EF01", SIGNAL: "20", IS_PROTECTED: "1" },
{ SSID: "orange", SIGNAL: "20", IS_PROTECTED: "0" },
],
});
return;
}
if (url.indexOf("ESP400") != -1) {
res.json({
Settings: [
{
F: "network/network",
P: "130",
T: "S",
V: "esp3d",
H: "hostname",
S: "32",
M: "1",
},
{
F: "network/network",
P: "0",
T: "B",
V: "1",
H: "radio mode",
O: [{ none: "0" }, { sta: "1" }, { ap: "2" }],
},
{
F: "network/sta",
P: "1",
T: "S",
V: "WIFI_OFFICE_B2G",
S: "32",
H: "SSID",
M: "1",
},
{
F: "network/sta",
P: "34",
T: "S",
N: "1",
V: "********",
S: "64",
H: "pwd",
M: "8",
},
{
F: "network/sta",
P: "99",
T: "B",
V: "1",
H: "ip mode",
O: [{ dhcp: "1" }, { static: "0" }],
},
{
F: "network/sta",
P: "100",
T: "A",
V: "192.168.0.1",
H: "ip",
},
{
F: "network/sta",
P: "108",
T: "A",
V: "192.168.0.1",
H: "gw",
},
{
F: "network/sta",
P: "104",
T: "A",
V: "255.255.255.0",
H: "msk",
},
{
F: "network/ap",
P: "218",
T: "S",
V: "ESP3D",
S: "32",
H: "SSID",
M: "1",
},
{
F: "network/ap",
P: "251",
T: "S",
N: "1",
V: "********",
S: "64",
H: "pwd",
M: "8",
},
{
F: "network/ap",
P: "316",
T: "A",
V: "192.168.0.1",
H: "ip",
},
{
F: "network/ap",
P: "118",
T: "B",
V: "11",
H: "channel",
O: [
{ 1: "1" },
{ 2: "2" },
{ 3: "3" },
{ 4: "4" },
{ 5: "5" },
{ 6: "6" },
{ 7: "7" },
{ 8: "8" },
{ 9: "9" },
{ 10: "10" },
{ 11: "11" },
{ 12: "12" },
{ 13: "13" },
{ 14: "14" },
],
},
{
F: "service/http",
P: "328",
T: "B",
V: "1",
H: "enable",
O: [{ no: "0" }, { yes: "1" }],
},
{
F: "service/http",
P: "121",
T: "I",
V: "80",
H: "port",
S: "65001",
M: "1",
},
{
F: "service/telnetp",
P: "329",
T: "B",
V: "1",
H: "enable",
O: [{ no: "0" }, { yes: "1" }],
},
{
F: "service/telnetp",
P: "125",
T: "I",
V: "23",
H: "port",
S: "65001",
M: "1",
},
{
F: "service/ftp",
P: "1021",
T: "B",
V: "1",
H: "enable",
O: [{ no: "0" }, { yes: "1" }],
},
{
F: "service/ftp",
P: "1009",
T: "I",
V: "21",
H: "control port",
S: "65001",
M: "1",
},
{
F: "service/ftp",
P: "1013",
T: "I",
V: "20",
H: "active port",
S: "65001",
M: "1",
},
{
F: "service/ftp",
P: "1017",
T: "I",
V: "55600",
H: "passive port",
S: "65001",
M: "1",
},
{
F: "service/notification",
P: "1004",
T: "B",
V: "1",
H: "auto notif",
O: [{ no: "0" }, { yes: "1" }],
},
{
F: "service/notification",
P: "116",
T: "B",
V: "0",
H: "notification",
O: [{ none: "0" }, { pushover: "1" }, { email: "2" }, { line: "3" }],
},
{
F: "service/notification",
P: "332",
T: "S",
V: "********",
S: "63",
H: "t1",
M: "0",
},
{
F: "service/notification",
P: "396",
T: "S",
V: "********",
S: "63",
H: "t2",
M: "0",
},
{
F: "service/notification",
P: "855",
T: "S",
V: " ",
S: "127",
H: "ts",
M: "0",
},
{
F: "system/system",
P: "461",
T: "B",
V: "40",
H: "targetfw",
O: [
{ repetier: "50" },
{ marlin: "20" },
{ marlinkimbra: "35" },
{ smoothieware: "40" },
{ grbl: "10" },
{ unknown: "0" },
],
},
{
F: "system/system",
P: "112",
T: "I",
V: "115200",
H: "baud",
O: [
{ 9600: "9600" },
{ 19200: "19200" },
{ 38400: "38400" },
{ 57600: "57600" },
{ 74880: "74880" },
{ 115200: "115200" },
{ 230400: "230400" },
{ 250000: "250000" },
{ 500000: "500000" },
{ 921600: "921600" },
],
},
{
F: "system/system",
P: "320",
T: "I",
V: "10000",
H: "bootdelay",
S: "40000",
M: "0",
},
{
F: "system/system",
P: "129",
T: "F",
V: "255",
H: "outputmsg",
O: [{ M117: "16" }, { serial: "1" }, { telnet: "2" }],
},
],
});
return;
}
SendBinary("ok\n");
res.send("");
});
function fileSizeString(size) {
let s;
if (size < 1024) return size + " B";
if (size < 1024 * 1024) return (size / 1024).toFixed(2) + " KB";
if (size < 1024 * 1024 * 1024)
return (size / (1024 * 1024)).toFixed(2) + " MB";
if (size < 1024 * 1024 * 1024 * 1024)
return (size / (1024 * 1024 * 1024)).toFixed(2) + " GB";
return "X B";
}
function filesList(mypath,mainpath) {
let res = '{"files":[';
let nb = 0;
let total = sdpath==mainpath? (4096 * 1024 * 1024):(1.2 * 1024 * 1024);
let totalused = getTotalSize(mainpath);
let currentpath = path.normalize(mainpath + mypath);
console.log("[path]" + currentpath);
fs.readdirSync(currentpath).forEach((fileelement) => {
let fullpath = path.normalize(currentpath + "/" + fileelement);
let fst = fs.statSync(fullpath);
let fsize = -1;
if (fst.isFile()) {
fsize = fileSizeString(fst.size);
}
if (nb > 0) res += ",";
res += '{"name":"' + fileelement + '","size":"' + fsize + '"}';
nb++;
});
res +=
'],"path":"' +
mypath +
'","occupation":"' +
((100 * totalused) / total).toFixed(0) +
'","status":"ok","total":"' +
fileSizeString(total) +
'","used":"' +
fileSizeString(totalused) +
'"}';
return res;
}
const getAllFiles = function (dirPath, arrayOfFiles) {
let files = fs.readdirSync(dirPath);
arrayOfFiles = arrayOfFiles || [];
files.forEach(function (file) {
if (fs.statSync(dirPath + "/" + file).isDirectory()) {
arrayOfFiles = getAllFiles(dirPath + "/" + file, arrayOfFiles);
} else {
arrayOfFiles.push(dirPath + "/" + file);
}
});
return arrayOfFiles;
};
const getTotalSize = function (directoryPath) {
const arrayOfFiles = getAllFiles(directoryPath);
let totalSize = 0;
arrayOfFiles.forEach(function (filePath) {
totalSize += fs.statSync(filePath).size;
});
return totalSize;
};
function deleteFolderRecursive(path) {
if (fs.existsSync(path) && fs.lstatSync(path).isDirectory()) {
fs.readdirSync(path).forEach(function (file, index) {
let curPath = path + "/" + file;
if (fs.lstatSync(curPath).isDirectory()) {
// recurse
deleteFolderRecursive(curPath);
} else {
// delete file
fs.unlinkSync(curPath);
}
});
console.log(`[server]Deleting directory "${path}"...`);
if (fs.existsSync(path)) fs.rmdirSync(path);
} else console.log(`[server]No directory "${path}"...`);
}
app.all("/updatefw", function (req, res) {
res.send("ok");
});
app.all("/sdfiles", function (req, res) {
let mypath = req.query.path;
let url = req.originalUrl;
let filepath = path.normalize(sdpath + mypath + "/" + req.query.filename);
if (url.indexOf("action=deletedir") != -1) {
console.log("[server]delete directory " + filepath);
deleteFolderRecursive(filepath);
fs.readdirSync(mypath);
} else if (url.indexOf("action=delete") != -1) {
fs.unlinkSync(filepath);
console.log("[server]delete file " + filepath);
}
if (url.indexOf("action=createdir") != -1) {
fs.mkdirSync(filepath);
console.log("[server]new directory " + filepath);
}
if (typeof mypath == "undefined") {
if (typeof req.body.path == "undefined") {
console.log("[server]path is not defined");
mypath = "/";
} else {
mypath = (req.body.path == "/" ? "" : req.body.path) + "/";
}
}
console.log("[server]path is " + mypath);
if (!req.files || Object.keys(req.files).length === 0) {
return res.send(filesList(mypath,sdpath));
}
let myFile = req.files.myfiles;
if (typeof myFile.length == "undefined") {
let fullpath = path.normalize(sdpath + mypath + myFile.name);
console.log("[server]one file:" + fullpath);
myFile.mv(fullpath, function (err) {
if (err) return res.status(500).send(err);
res.send(filesList(mypath,sdpath));
});
return;
} else {
console.log(myFile.length + " files");
for (let i = 0; i < myFile.length; i++) {
let fullpath = path.normalize(sdpath + mypath + myFile[i].name);
console.log(fullpath);
myFile[i].mv(fullpath).then(() => {
if (i == myFile.length - 1) res.send(filesList(mypath,sdpath));
});
}
}
});
app.all("/files", function (req, res) {
let mypath = req.query.path;
let url = req.originalUrl;
let filepath = path.normalize(serverpath + mypath + "/" + req.query.filename);
if (url.indexOf("action=deletedir") != -1) {
console.log("[server]delete directory " + filepath);
deleteFolderRecursive(filepath);
fs.readdirSync(mypath);
} else if (url.indexOf("action=delete") != -1) {
fs.unlinkSync(filepath);
console.log("[server]delete file " + filepath);
}
if (url.indexOf("action=createdir") != -1) {
fs.mkdirSync(filepath);
console.log("[server]new directory " + filepath);
}
if (typeof mypath == "undefined") {
if (typeof req.body.path == "undefined") {
console.log("[server]path is not defined");
mypath = "/";
} else {
mypath = (req.body.path == "/" ? "" : req.body.path) + "/";
}
}
console.log("[server]path is " + mypath);
if (!req.files || Object.keys(req.files).length === 0) {
return res.send(filesList(mypath,serverpath));
}
let myFile = req.files.myfiles;
if (typeof myFile.length == "undefined") {
let fullpath = path.normalize(serverpath + mypath + myFile.name);
console.log("[server]one file:" + fullpath);
myFile.mv(fullpath, function (err) {
if (err) return res.status(500).send(err);
res.send(filesList(mypath, serverpath));
});
return;
} else {
console.log(myFile.length + " files");
for (let i = 0; i < myFile.length; i++) {
let fullpath = path.normalize(serverpath + mypath + myFile[i].name);
console.log(fullpath);
myFile[i].mv(fullpath).then(() => {
if (i == myFile.length - 1) res.send(filesList(mypath, serverpath));
});
}
}
});
wss.on("connection", (socket, request) => {
console.log(wscolor("[ws] New connection"));
console.log(wscolor(`[ws] currentID:${currentID}`));
socket.send(`currentID:${currentID}`);
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(`activeID:${currentID}`);
}
});
currentID++;
socket.on("message", (message) => {
console.log(wscolor("[ws] received: %s", message));
});
});
wss.on("error", (error) => {
console.log(wscolor("[ws] Error: %s", error));
});