@robmcelhinney/qr-file-share
Version:
Node.js http server allowing for file transfers over local area network made easier with QR code output on start.
292 lines (257 loc) • 9.46 kB
JavaScript
const myConstClass = require("./constants.js")
const fs = require("fs")
const util = require("util")
const path = require("path")
const qrcode = require("qrcode-terminal")
const archiver = require("archiver")
const fileUpload = require("express-fileupload")
const express = require("express")
const app = express()
module.exports = ({ base_path, compression, port }) => {
app.use(fileUpload())
// If want to limit file size add this:
// app.use(fileUpload({
// createParentPath: true,
// limits: {
// fileSize: 2 * 1024 * 1024 * 1024 //2MB max file(s) size
// },
// }))
app.listen(port, () => {
// get local ip address from https://gist.github.com/sviatco/9054346#gistcomment-1810845
let addresses = new Set()
let ifaces = require("os").networkInterfaces()
for (let dev in ifaces) {
ifaces[dev].filter((details) => {
if (details.family === 'IPv4' && !details.internal) {
addresses.add(details.address)
}
})
}
addresses = Array.from(addresses)
if (addresses.length == 0) {
return
}
for (address of addresses) {
const full_address = "http://" + address + ":" + port
console.log("Server listening on port: ", port)
console.log("Scan QR code or go to " + full_address)
qrcode.generate(full_address)
console.log("\n")
}
})
const readdir = util.promisify(fs.readdir)
// console.log("base_path: " + base_path)
const base_dir = base_path || process.cwd()
app.get("/api/files", async (req, res) => {
let dir
// console.log("/api/files req.query: ", req.query)
if (req.query.path && req.query.path !== "undefined") {
dir = path.join(base_dir, req.query.path)
} else {
// console.log("base_dir: ", base_dir)
dir = base_dir
}
if (!checkPathOK(dir)) {
console.error("path attempting to go back. dir: ", dir)
res.status(403).send("Can't go back up path")
}
// console.log("dir: ", dir)
listDir(dir)
.then((results) => {
res.send(results)
// console.log("success")
})
.catch((err) => {
console.error("error browsing files", err)
res.sendStatus(501)
})
})
async function listDir(dir) {
let jsonResult = {}
// console.log("listDir")
let rel_dir = ""
let files
try {
files = await readdir(dir)
} catch (err) {
console.error(err)
}
files = files.filter((item) => !/(^|\/)\.[^\/\.]/g.test(item))
files.forEach((file) => {
jsonResult[file] = path.join(rel_dir, file)
let is_dir = false
// Is directory?
let file_path = path.join(dir, file)
// console.log("isDirectory file_path: ", file_path)
if (
fs.existsSync(file_path) &&
fs.lstatSync(file_path).isDirectory()
) {
is_dir = true
// console.log("isDirectory file: ", file)
}
jsonResult[file] = is_dir
})
// console.log("jsonResult: ", jsonResult)
return jsonResult
}
app.get("/api/download", async (req, res) => {
// console.log("/api/download")
const file = req.query.file
downloadFile(file, res)
.then(() => {
// res.send(results)
// console.log("success")
})
.catch((err) => {
console.error("error", err)
res.sendStatus(501)
})
})
async function downloadFile(file, res) {
let file_path = path.join(base_dir, file)
if (fs.existsSync(file_path) && fs.lstatSync(file_path).isDirectory()) {
is_dir = true
// console.log("isDirectory file: ", file)
}
res.download(file_path)
}
app.get("/api/downloadDir", async (req, res) => {
const dir = req.query.dir
if (!checkPathOK(dir)) {
console.error("path attempting to go back. dir: ", dir)
res.status(403).send("Can't go back up path")
}
// console.log("downloadDir. dir: ", dir)
await zipDir(base_dir, dir, res)
.then((output_path) => {
// res.send(results)
// console.log("success downloadDir")
})
.catch(err => {
console.error("error", err)
res.sendStatus(501)
})
})
async function zipDir(rel_directory, dir, res) {
// console.log("rel_directory: ", rel_directory)
// console.log("dir: ", dir)
let archive = archiver("zip", {
zlib: { level: compression }, // Sets the compression level.
})
archive.on("warning", function (err) {
if (err.code === "ENOENT") {
console.error("Zipping error. ENOENT.")
} else {
// throw error
throw err
}
})
archive.on("error", function (err) {
throw err
})
res.writeHead(200, {
"Content-Type": "application/zip",
"Content-Disposition": "attachment filename=" + dir + ".zip",
})
// pipe archive data to the result
archive.pipe(res)
archive.directory(path.join(rel_directory, dir), dir)
// finalize the archive (ie we are done appending files but streams have to finish yet)
// 'close', 'end' or 'finish' may be fired right after calling this method so register to them beforehand
archive.finalize()
// console.log("finalized archive")
}
app.post("/api/upload", async (req, res) => {
// console.log("/api/upload.")
uploadFile(req, res)
.then(() => {
// res.send(results)
// console.log("upload success")
})
.catch((err) => {
console.error("upload error", err)
res.sendStatus(501)
})
})
async function moveFile(file, rel_dir) {
//move photo to uploads directory
let new_name = file.name
let dir = path.join(base_dir, rel_dir)
// console.log("dir: ", dir)
try {
fs.accessSync(path.join(dir, new_name))
let name = new_name.substring(0, new_name.lastIndexOf("."))
let file_extension = new_name.substring(new_name.lastIndexOf("."))
new_name = await nonExistingName(name, file_extension, 0, dir)
} catch (err) {
// console.log("moveFile. fs.accessSync(new_name)")
}
let full_path = path.join(dir, new_name)
file.mv(full_path)
//push file details
return {
name: file.name,
mimetype: file.mimetype,
size: file.size,
}
}
function checkPathOK(path_check) {
return !path_check.includes("..")
}
async function nonExistingName(name, file_extension, count, dir) {
// console.log("nonExistingName: name, file_extension: ", name, file_extension)
try {
fs.accessSync(path.join(dir, name) + "_" + count + file_extension)
return await nonExistingName(name, file_extension, count + 1, dir)
} catch (err) {
// file does not exist.//
}
return name + "_" + count + file_extension
}
async function uploadFile(req, res) {
// console.log("uploadFile req.files: ", req.files)
try {
if (!req.files) {
// console.log("!req.files")
res.send({
status: false,
message: "No file uploaded",
})
} else {
let data = new Array()
let message
const rel_dir = req.body.rel_dir
// console.log("rel_dir: ", rel_dir)
// Check if single file or group of files
if (req.files.file && !Array.isArray(req.files.file)) {
let file = req.files.file
// console.log("single file")
data = moveFile(file, rel_dir)
message = "File is uploaded"
} else {
for (let i = 0; i < req.files.file.length; i++) {
let file = req.files.file[i]
// console.log("inside before for each. : ")
data.push(moveFile(file, rel_dir))
message = "Files are uploaded"
}
}
//send response
res.send({
status: true,
message: message,
data: data,
})
}
} catch (err) {
res.status(500).send(err)
}
}
app.get("/api/baseDir", async (req, res) => {
// console.log("/api/baseDir: ", base_dir)
res.send(base_dir)
})
app.use(express.static(path.join(__dirname, "client/build")))
}