const express = require("express") const expressStaticGzip = require("express-static-gzip") 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 sensorInterval = -1 //const serverpath = path.normalize(__dirname + "/../server/public/"); const subtarget = process.env.SUBTARGET_ENV ? process.env.SUBTARGET_ENV : "Marlin" const target = process.env.TARGET_ENV ? process.env.TARGET_ENV : "Printer3D" const serverpath = path.normalize(__dirname + "/../server/" + target + "/" + subtarget) + "/" if (!fs.existsSync(serverpath + "Flash")) { fs.mkdirSync(serverpath + "Flash", { recursive: true }) } if (!fs.existsSync(serverpath + "SD")) { fs.mkdirSync(serverpath + "SD", { recursive: true }) } const { commandsQuery, configURI, getLastconnection, hasEnabledAuthentication, } = require( path.normalize( __dirname + "/targets/" + target + "/" + subtarget + "/index.js" ) ) const WebSocketServer = require("ws").Server, wss = new WebSocketServer({ port: 8089 }) app.use("/", express.static(serverpath + "Flash")) app.use("/sd", express.static(serverpath + "sd")) app.use("/", expressStaticGzip(serverpath + "Flash")) app.use(fileUpload({ preserveExtension: true, debug: false })) app.listen(port, () => console.log("Env:", subtarget, ":", target)) app.timeout = 2000 //app.use(express.urlencoded({ extended: false })); function SendWS(text, isbinary = true, isNotification = true) { if (typeof isbinary === "undefined") isbinary = true if (isbinary) { 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) } }) } else { const notif = (isNotification ? "NOTIFICATION:" : "") + text console.log(wscolor("[ws] send:", notif)) wss.clients.forEach(function each(client) { if (client.readyState === WebSocket.OPEN) { client.send(notif) } }) } } app.post("/login", function (req, res) { loginURI(req, res) }) app.get("/config", function (req, res) { configURI(req, res) }) app.get("/command", function (req, res) { commandsQuery(req, res, SendWS) }) /*app.get("/sdfiles", function (req, res) { res.status(200); res.send( '{"files":[{"name":"LOST.DIR","shortname":"LOST.DIR","size":"-1"},{"name":".android_secure","shortname":"ANDROI~1","size":"-1"},{"name":"app","shortname":"APP","size":"-1"},{"name":"framework","shortname":"FRAMEW~1","size":"-1"},{"name":"lib","shortname":"LIB","size":"-1"},{"name":"permissions","shortname":"PERMIS~1","size":"-1"},{"name":".Trash-1000","shortname":"TRASH-~1","size":"-1"},{"name":".FileExpert","shortname":"FILEEX~1","size":"-1"},{"name":"download","shortname":"DOWNLOAD","size":"-1"},{"name":"Android","shortname":"ANDROID","size":"-1"},{"name":".mmsyscache","shortname":"MMSYSC~1","size":"-1"},{"name":"clockworkmod","shortname":"CLOCKW~1","size":"-1"},{"name":"Evernote","shortname":"EVERNOTE","size":"-1"},{"name":"data","shortname":"DATA","size":"-1"},{"name":".estrongs","shortname":"ESTRON~1","size":"-1"},{"name":"DCIM","shortname":"DCIM","size":"-1"},{"name":"backups","shortname":"BACKUPS","size":"-1"},{"name":".zdworks","shortname":"ZDWORK~1","size":"-1"},{"name":"toolbox-stericson","shortname":"TOOLBO~1","size":"75.54 KB"},{"name":"SystemROMToolbox","shortname":"SYSTEM~1","size":"-1"},{"name":"busybox-stericson","shortname":"BUSYBO~1","size":"843.20 KB"},{"name":"recovery.img","shortname":"RECOVERY.IMG","size":"6.00 MB"},{"name":"preloader.img","shortname":"PRELOA~1.IMG","size":"128.00 KB"},{"name":"nvram.img","shortname":"NVRAM.IMG","size":"3.00 MB"},{"name":"seccnfg.img","shortname":"SECCNFG.IMG","size":"128.00 KB"},{"name":"uboot.img","shortname":"UBOOT.IMG","size":"384.00 KB"},{"name":"boot.img","shortname":"BOOT.IMG","size":"6.00 MB"},{"name":"secstatic.img","shortname":"SECSTA~1.IMG","size":"1.12 MB"},{"name":"system.img","shortname":"SYSTEM.IMG","size":"170.00 MB"},{"name":"misc.img","shortname":"MISC.IMG","size":"384.00 KB"},{"name":"cache.img","shortname":"CACHE.IMG","size":"60.00 MB"},{"name":"logo.img","shortname":"LOGO.IMG","size":"3.00 MB"},{"name":"expdb.img","shortname":"EXPDB.IMG","size":"640.00 KB"},{"name":"userdata.img","shortname":"USERDATA.IMG","size":"261.25 MB"},{"name":"recovery2.img","shortname":"RECOVE~1.IMG","size":"6.00 MB"},{"name":"touchrec.img","shortname":"TOUCHREC.IMG","size":"4.00 MB"},{"name":"restart.gcode","shortname":"RESTAR~1.GCO","size":"1 B"},{"name":"M3.G","shortname":"M3.G","size":"75 B"},{"name":"WeChat Image_20210108102318.jpg","shortname":"WECHAT~1.JPG","size":"876.47 KB"},{"name":"test.txt","shortname":"test.txt","size":"0 B"},{"name":"foo.txt","shortname":"foo.txt","size":"13 B"},{"name":"myfile.txt","shortname":"myfile.txt","size":"0 B"},{"name":"update.zip","shortname":"update.zip","size":"1.29 MB"},{"name":"luc","shortname":"luc","size":"-1"}],"path":"/","occupation":"22","status":"ok","total":"3.67 GB","used":"833.38 MB"}' ); console.log(commandcolor(`[server]/sdfiles)`)); return; });*/ function fileSizeString(size) { if (size === -1) return "" const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] let i = 0 while (size >= 1024) { size /= 1024 ++i } return `${size.toFixed(2)} ${units[i]}` } function filesList(mypath, destination) { const currentPath = path.normalize(serverpath + destination + mypath) console.log("[path]" + currentPath) const totalUsed = getTotalSize(serverpath + destination) const total = (destination == "SD" ? 4096 : 1.31) * 1024 * 1024 const occupation = ((100 * totalUsed) / total).toFixed(0) const files = fs.readdirSync(currentPath).map((file) => { const fullpath = path.normalize(currentPath + "/" + file) const fst = fs.statSync(fullpath) const fsize = fst.isFile() ? fileSizeString(fst.size) : "-1" return { name: file, size: fsize } }) const response = { files, path: mypath, occupation, status: "ok", total: fileSizeString(total), used: fileSizeString(totalUsed), } return JSON.stringify(response) } const getAllFiles = function (dirPath, arrayOfFiles = []) { let files = fs.readdirSync(dirPath) || [] const newFiles = files.reduce((acc, file) => { const fullpath = dirPath + "/" + file return fs.statSync(fullpath).isDirectory() ? getAllFiles(fullpath, acc) : [...acc, fullpath] }, []) return [...arrayOfFiles, ...newFiles] } const getTotalSize = function (directoryPath) { const allFiles = getAllFiles(directoryPath) console.log("allFiles", allFiles) return allFiles.reduce( (acc, currFile) => acc + fs.statSync(currFile).size, 0 ) } 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("/files", function (req, res) { let mypath = req.query.path let url = req.originalUrl let filepath = path.normalize( serverpath + "Flash" + 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) { console.log("[server]delete file " + filepath) fs.unlinkSync(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, "Flash")) } let myFile = req.files.myfiles if (typeof myFile.length == "undefined") { let fullpath = path.normalize( serverpath + "Flash" + 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, "Flash")) }) return } else { console.log(myFile.length + " files") for (let i = 0; i < myFile.length; i++) { let fullpath = path.normalize( serverpath + "Flash" + mypath + myFile[i].name ) console.log(fullpath) myFile[i].mv(fullpath).then(() => { if (i == myFile.length - 1) res.send(filesList(mypath, "Flash")) }) } } }) app.all("/sdfiles", function (req, res) { let mypath = req.query.path let url = req.originalUrl let filepath = path.normalize( serverpath + "SD" + 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, "SD")) } let myFile = req.files.myfiles if (typeof myFile.length == "undefined") { let fullpath = path.normalize(serverpath + "SD" + 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, "SD")) }) return } else { console.log(myFile.length + " files") for (let i = 0; i < myFile.length; i++) { let fullpath = path.normalize( serverpath + "SD" + mypath + myFile[i].name ) console.log(fullpath) myFile[i].mv(fullpath).then(() => { if (i == myFile.length - 1) res.send(filesList(mypath, "SD")) }) } } }) 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}`) } }) if (sensorInterval != -1) { clearInterval(sensorInterval) sensorInterval = setInterval(() => { const sensorTxt = "SENSOR:10[C] 15[%]" SendWS(sensorTxt, false, false) }, 3000) } currentID++ socket.on("message", (message) => { console.log(wscolor("[ws] received:", message)) if (hasEnabledAuthentication() && message.startsWith("PING:")) { wss.clients.forEach(function each(client) { if (client.readyState === WebSocket.OPEN) { let t = hasEnableAuthentication() ? sessiontTime - (Date.now() - getLastconnection()) : 60000 let remainingtime = t < 0 ? 0 : t console.log("remain:", remainingtime, "millisec") client.send(`PING:${remainingtime}:60000`) } }) } }) }) wss.on("error", (error) => { console.log(wscolor("[ws] Error:", error)) })