Skip to content

Commit

Permalink
feat: store explorer (#166)
Browse files Browse the repository at this point in the history
* feat: export logs button

* fix: lint issues

* feat(ui): store explorer

* feat: writefile and download file

* feat: zip download using archiver

* feat: delete multiple files

* fix: lint issues

* fix: use save icon

* fix: useless language

* fix: icons color
  • Loading branch information
robertsLando authored Jan 19, 2021
1 parent c00f50a commit d98fd7e
Show file tree
Hide file tree
Showing 10 changed files with 6,662 additions and 10,338 deletions.
144 changes: 144 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ const history = require('connect-history-api-fallback')
const SocketManager = reqlib('/lib/SocketManager')
const { inboundEvents, socketEvents } = reqlib('/lib/SocketManager.js')
const utils = reqlib('/lib/utils.js')
const fs = require('fs-extra')
const path = require('path')
const appConfig = reqlib('config/app.js')
const renderIndex = reqlib('/lib/renderIndex')
const archiver = require('archiver')
const storeDir = utils.joinPath(true, appConfig.storeDir)

const socketManager = new SocketManager()

Expand Down Expand Up @@ -262,6 +267,145 @@ app.post('/api/importConfig', async function (req, res) {
}
})

// get config
app.get('/api/store', async function (req, res) {
try {
async function parseDir (dir) {
const toReturn = []
const files = await fs.readdir(dir)
for (const file of files) {
const entry = {
name: path.basename(file),
path: utils.joinPath(dir, file)
}
const stats = await fs.lstat(entry.path)
if (stats.isDirectory()) {
entry.children = await parseDir(entry.path)
} else {
entry.ext = file.split('.').pop()
}

entry.size = utils.humanSize(stats.size)
toReturn.push(entry)
}
return toReturn
}

const data = await parseDir(storeDir)

res.json({ success: true, data: data })
} catch (error) {
logger.error(error.message)
return res.json({ success: false, message: error.message })
}
})

app.get('/api/store/:path', async function (req, res) {
try {
const reqPath = req.params.path

if (!reqPath.startsWith(storeDir)) {
throw Error('Path not allowed')
}

const stat = await fs.lstat(reqPath)

if (!stat.isFile()) {
throw Error('Path is not a file')
}

const data = await fs.readFile(reqPath, 'utf8')

res.json({ success: true, data: data })
} catch (error) {
logger.error(error.message)
return res.json({ success: false, message: error.message })
}
})

app.put('/api/store/:path', async function (req, res) {
try {
const reqPath = req.params.path

if (!reqPath.startsWith(storeDir)) {
throw Error('Path not allowed')
}

const stat = await fs.lstat(reqPath)

if (!stat.isFile()) {
throw Error('Path is not a file')
}

await fs.writeFile(reqPath, req.body.content, 'utf8')

res.json({ success: true })
} catch (error) {
logger.error(error.message)
return res.json({ success: false, message: error.message })
}
})

app.delete('/api/store/:path', async function (req, res) {
try {
const reqPath = req.params.path

if (!reqPath.startsWith(storeDir)) {
throw Error('Path not allowed')
}

await fs.remove(reqPath)

res.json({ success: true })
} catch (error) {
logger.error(error.message)
return res.json({ success: false, message: error.message })
}
})

app.put('/api/store-multi', async function (req, res) {
try {
const files = req.body.files || []
for (const f of files) {
await fs.remove(f)
}
res.json({ success: true })
} catch (error) {
logger.error(error.message)
return res.json({ success: false, message: error.message })
}
})

app.post('/api/store-multi', function (req, res) {
const files = req.body.files || []

const archive = archiver('zip')

archive.on('error', function (err) {
res.status(500).send({
error: err.message
})
})

// on stream closed we can end the request
archive.on('end', function () {
logger.debug('zip archive ready')
})

// set the archive name
res.attachment('zwavejs2mqtt-store.zip')
res.setHeader('Content-Type', 'application/zip')

// use res as stream so I don't need to create a temp file
archive.pipe(res)

for (const f of files) {
archive.file(f, { name: f.replace(storeDir, '') })
}

archive.finalize()
})

// update settings
app.post('/api/settings', async function (req, res) {
try {
Expand Down
15 changes: 15 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,20 @@ module.exports = {
},
hasProperty (obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop)
},
humanSize (bytes) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']

if (bytes === 0) {
return 'n/a'
}

const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)))

if (i === 0) {
return bytes + ' ' + sizes[i]
}

return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i]
}
}
Loading

0 comments on commit d98fd7e

Please sign in to comment.