doodle3d-firmware/src/rest/api/api_sketch.lua

227 lines
6.3 KiB
Lua

--
-- This file is part of the Doodle3D project (http://doodle3d.com).
--
-- @copyright 2013, Doodle3D
-- @license This software is licensed under the terms of the GNU GPL v2 or later.
-- See file LICENSE.txt or visit http://www.gnu.org/licenses/gpl.html for full license details.
--[[
This module uses the Lua filesystem library to iterate over all sketches.
A more flexible approach would be to use an index file (like the update module does).
That way, we could also store arbitrary meta-data together with the sketches.
]]--
local lfs = require('lfs')
local log = require('util.logger')
local utils = require('util.utils')
local MOD_ABBR = "ASKE"
local M = {
isApi = true,
SKETCH_DIR = '/root/sketches',
MIN_FREE_SPACE = 1024 * 80
}
local NUMBER_PAD_WIDTH = 5
local SKETCH_FILE_EXTENSION = 'svg'
-- creates sketch directory, and sets response to error state on failure
local function createSketchDirectory()
if os.execute('mkdir -p ' .. M.SKETCH_DIR) ~= 0 then
log:error(MOD_ABBR, "could not create sketch directory '" .. M.SKETCH_DIR .. "'")
response:setError("could not create sketch directory")
return false
end
return true
end
local function constructSketchFilename(idx)
return string.format('%0' .. NUMBER_PAD_WIDTH .. '.0f.%s', idx, SKETCH_FILE_EXTENSION)
end
--should this return 1k-blocks or bytes?
local function getFreeDiskSpace()
local kb = utils.captureCommandOutput('df / -k | awk \'$3 ~ /[0-9]+/ { print $4 }\'')
return kb * 1024
end
local function createSketchList()
local result = {}
for item in lfs.dir(M.SKETCH_DIR) do
if item ~= '.' and item ~= '..' then
local idx = item:match('^(%d+)\.'..SKETCH_FILE_EXTENSION..'$')
if idx and idx:len() == NUMBER_PAD_WIDTH then
table.insert(result, tonumber(idx))
end
end
end
table.sort(result)
return result
end
-- retrieve sketches, requires id(int)
function M._global_GET(request, response)
local argId = request:getBlankArgument() -- try this one first
if not argId then argId = request:get("id") end -- and fall back to this one
if not argId then
response:setError("missing id argument")
return
end
argId = tonumber(argId)
if not createSketchDirectory(request, response) then return end
local sketches = createSketchList()
local exists = false
for _,sketchId in ipairs(sketches) do
if sketchId == argId then
exists = true
break
end
end
response:addData('id', argId)
if exists then
local loadFile,msg = io.open(M.SKETCH_DIR .. '/' .. constructSketchFilename(argId))
if not loadFile then
response:setError("could not open sketch file for reading (" .. msg .. ")")
return
end
local data = loadFile:read('*a')
if not data then data = '' end
response:addData('data', data)
loadFile:close()
response:setSuccess()
else
response:setFail("sketch not found")
end
end
-- TODO: do we need locking?
-- save sketches, requires data(string)
function M._global_POST(request, response)
local argData = request:get("data")
if not argData then
response:setFail("missing data argument")
return
elseif argData:len() == 0 then
response:setFail("data argument must be non-empty")
return
end
if getFreeDiskSpace() - M.MIN_FREE_SPACE < argData:len() then
response:setFail("not enough free space")
response:addData('available', getFreeDiskSpace())
response:addData('reserved', M.MIN_FREE_SPACE)
response:addData('sketch_size', argData:len())
return
end
if not createSketchDirectory(request, response) then return end
local sketches = createSketchList()
local listSize = table.getn(sketches)
local sketchIdx = listSize > 0 and sketches[listSize] + 1 or 1
local sketchFile = M.SKETCH_DIR .. '/' .. constructSketchFilename(sketchIdx)
log:verbose(MOD_ABBR, "saving sketch #" .. sketchIdx .. " (" .. argData:len() .. " bytes)")
local saveFile,msg = io.open(sketchFile, 'w')
if not saveFile then
response:setError("error opening sketch file for writing (".. msg .. ")")
return
end
saveFile:write(argData)
saveFile:close()
response:addData('id', sketchIdx)
response:setSuccess()
end
-- TODO: return total space used by sketches? (createSketchList() could just as well collect size info too...see lfs.attributes)
function M.status(request, response)
if not createSketchDirectory(request, response) then return end
local sketches = createSketchList()
local listSize = table.getn(sketches)
response:addData('number_of_sketches', table.getn(sketches))
response:addData('available', getFreeDiskSpace())
response:addData('reserved', M.MIN_FREE_SPACE)
response:setSuccess()
end
-- remove all sketches
function M.clear_POST(request, response)
local rv = os.execute("rm -f " .. M.SKETCH_DIR .. '/*')
if rv == 0 then response:setSuccess()
else response:setFail("could not remove contents of sketch directory")
end
end
-- remove single sketch
-- function M.delete_POST(request, response)
-- local argId = tonumber(request:get("id")) --to number will raise exception in case of illegal input
-- local filename = M.SKETCH_DIR .. '/' .. constructSketchFilename(argId)
-- local rv = os.execute("rm -f " .. filename)
-- if rv == 0 then response:setSuccess()
-- else response:setFail("could not remove " .. filename)
-- end
-- end
-- recreate directory sequence by renaming files
function M.index_POST(request, response)
local list = {}
local index = 1
for item in lfs.dir(M.SKETCH_DIR) do
if item ~= '.' and item ~= '..' then
local idx = item:match('^(%d+)\.'..SKETCH_FILE_EXTENSION..'$')
if idx and idx:len() == NUMBER_PAD_WIDTH then
local src = M.SKETCH_DIR .. '/' .. item
local dst = M.SKETCH_DIR .. '/' .. constructSketchFilename(index)
if src ~= dst then
table.insert(list, src .. ' ' .. dst)
local rv = os.execute("mv " .. src .. ' ' .. dst)
end
index = index + 1
end
end
end
response:addData('list',list)
response:setSuccess()
end
-- list files by fileID (not by index)
function M.list_GET(request, response)
response:addData("list",createSketchList())
response:setSuccess()
end
-- remove single sketch by fileID (not by index)
function M.delete_POST(request, response)
local argId = tonumber(request:get("id")) --to number will raise exception in case of illegal input
local filename = M.SKETCH_DIR .. '/' .. constructSketchFilename(argId)
local rv = os.execute("rm -f " .. filename)
if rv == 0 then response:setSuccess()
else response:setFail("could not remove " .. filename)
end
end
return M