diff options
Diffstat (limited to 'web/labitrackd.lua')
-rwxr-xr-x | web/labitrackd.lua | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/web/labitrackd.lua b/web/labitrackd.lua new file mode 100755 index 0000000..c7afdfd --- /dev/null +++ b/web/labitrackd.lua @@ -0,0 +1,288 @@ +#!/usr/bin/env lem +-- +-- This file is part of blipserver. +-- Copyright 2011 Emil Renner Berthing +-- +-- blipserver is free software: you can redistribute it and/or +-- modify it under the terms of the GNU General Public License as +-- published by the Free Software Foundation, either version 3 of +-- the License, or (at your option) any later version. +-- +-- blipserver is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with blipserver. If not, see <http://www.gnu.org/licenses/>. +-- + +local function usage() + print('Labitrack daemon') + print('usage: '..arg[0]..' [bind [queue_dir]]') + print('defaults: bind=*:8080 queue_dir=./queue') + os.exit(1) +end + +-- +-- settings +-- +local pg_connect_str = 'host=localhost user=labitrack dbname=labitrack password=nerfyoawdAj3' +local bind = arg[1] or '*:8080' +local queue_dir = arg[2] or './queue' +-- +-- end of settings +-- + +local bind_colon = string.find(bind, ':', 1, true) +if bind_colon == nil then + usage() +end +local bind_addr = string.sub(bind, 1, bind_colon-1) +local bind_port = tonumber(string.sub(bind, bind_colon+1)) + +local utils = require 'lem.utils' +local streams = require 'lem.streams' +local postgres = require 'lem.postgres' +local qpostgres = require 'lem.postgres.queued' +local hathaway = require 'lem.hathaway' +local json = require 'dkjson' +local base64 = require 'base64' +local filequeue = require 'filequeue' + +local assert = assert +local format = string.format +local tonumber = tonumber + +local function sendfile(content, path) + return function(req, res) + res.headers['Content-Type'] = content + res.file = path + end +end + +local function sendfile_js(path) + return sendfile('text/javascript; charset=UTF-8', path) +end + +local function sendfile_css(path) + return sendfile('text/css; charset=UTF-8', path) +end + +local function deserialize_array_from_pg(pgarray) + local ret, rc = {}, 0 + if pgarray == nil then + return ret + end + local strlen = string.len(pgarray) + if strlen <= 2 then + return ret + end + local n = 0 + for i = 2, strlen-1 do + if i > n then + rc = rc + 1 + n = string.find(pgarray, ',', i) + if n == nil then + n = strlen + end + ret[rc] = string.sub(pgarray, i, n-1) + end + end + return ret +end + +local function add_json_row(res, values, i) + local n = #values + if n > 0 then + local clen = #values[0] + local point = values[i] + local d = {} + for j = 1, clen do + local k = values[0][j] + local v = point[j] + if k == 'id' then + d[k] = tonumber(v) + elseif k == 'created' then + d[k] = tonumber(v) + elseif k == 'updated' then + d[k] = tonumber(v) + elseif not (k == 'tags') then + d[k] = v + else + d[k] = deserialize_array_from_pg(v) + end + end + res:add('%s', json.encode(d)) + end +end + + +local function add_json(res, values) + local n = #values + res:add('[') + if n > 0 then + for i = 1, n do + add_json_row(res, values, i) + if not (i == n) then + res:add(',') + end + end + end + res:add(']') +end + +local function set_json_nocache_headers(res) + res.headers['Content-Type'] = 'application/json; charset=UTF-8' + res.headers['Cache-Control'] = 'max-age=0, must-revalidate' +end + +local function unescape(s) + s = string.gsub(s, "+", " ") + s = string.gsub(s, "%%(%x%x)", function (h) + return string.char(tonumber(h, 16)) + end) + return s +end + +-- Connect to database and prepare statements +local db = assert(qpostgres.connect(pg_connect_str)) +local cols = 'id, name, "desc", tags, extract(epoch from created)::int8 created, extract(epoch from updated)::int8 updated' +assert(db:prepare('get', 'SELECT '..cols..' FROM objects WHERE id = $1')) +assert(db:prepare('recent', 'SELECT '..cols..' FROM objects ORDER BY updated DESC LIMIT 10;')) +assert(db:prepare('since', 'SELECT '..cols..' FROM objects ORDER BY id LIMIT 10 OFFSET $1;')) +assert(db:prepare('insert', 'INSERT INTO objects (name, "desc", tags) VALUES ($1, $2, string_to_array($3, \',\')::text[]) RETURNING id;')) +assert(db:prepare('update', 'UPDATE objects SET name=$2, "desc"=$3, tags=string_to_array($4, \',\')::text[], updated=now() WHERE id = $1;')) +assert(db:prepare('count', 'SELECT COUNT(id) FROM objects;')) + +local function count() + return assert(db:run('count'))[1][1] +end + +-- initialize queue +local queue = filequeue.open(queue_dir) + +hathaway.import() + +local htmlpage = sendfile('text/html; charset=UTF-8', 'pub/index.html') + +GET('/', htmlpage) +GET('/browse', htmlpage) +MATCH('^/browse/(%d+)$', htmlpage) +GET('/recent', htmlpage) +GET('/about', htmlpage) +MATCH('^/view/(%d+)$', htmlpage) +MATCH('^/edit/(%d+)$', htmlpage) + +GET('/js/corelibs.min.js', sendfile_js('js/dist/corelibs.min.js')) +GET('/js/corelibs.src.js', sendfile_js('js/dist/corelibs.src.js')) +GET('/js/qrcode.min.js', sendfile_js('js/dist/qrcode.min.js')) +GET('/js/labitrack.min.js', sendfile_js('js/dist/labitrack.min.js')) +GET('/js/labitrack.src.js', sendfile_js('js/dist/labitrack.src.js')) +GET('/js/templates.js', sendfile_js('templates/dist/labitrack.min.js')) + +GET('/css/bootstrap.1.4.0.min.css', sendfile_css('pub/css/bootstrap.1.4.0.min.css')) +GET('/css/labitrack.css', sendfile_css('css/dist/labitrack.min.css')) + +GET('/favicon.ico', sendfile('image/x-icon', 'pub/favicon.ico')) + +MATCH('^/browse/(%d+).json$', function(req, res, since) + if req.method ~= 'HEAD' and req.method ~= 'GET' then + return hathaway.method_not_allowed(req, res) + end + + set_json_nocache_headers(res) + + res:add('{"count": %d, "objects":', count()); + add_json(res, assert(db:run('since', (since-1)*10))) + res:add('}'); +end) + +GET('/queue.json', function(req, res) + set_json_nocache_headers(res) + res:add('%s', json.encode(queue:stat())) +end) + +GET('/queue.json?empty', function(req, res) + set_json_nocache_headers(res) + res:add('%s', json.encode(queue:empty())) +end) + +GET('/recent.json', function(req, res) + set_json_nocache_headers(res) + add_json(res, assert(db:run('recent'))) +end) + +local function unescape(s) + s = string.gsub(s, "+", " ") + s = string.gsub(s, "%%(%x%x)", function (h) + return string.char(tonumber(h, 16)) + end) + return s +end + +local function save_or_update(req, res) + set_json_nocache_headers(res) + + local expected = "application/json" + assert(string.sub(req.headers['Content-Type'], 1, string.len(expected)) == expected) + + local body = req:body() + local label = json.decode(body) + + local id + if label['id'] == nil then + id = assert(db:run('insert', label['name'], label['desc'], table.concat(label['tags'], ',')))[1][1] + else + assert(db:run('update', label['id'], label['name'], label['desc'], table.concat(label['tags'], ','))) + id = label['id'] + end + + res:add('{"id": %d}', id) +end + +POST('/o', save_or_update) + +MATCH('^/o/(%d+).json$', function(req, res, id) + if req.method == 'PUT' then + return save_or_update(req, res) + elseif req.method ~= 'HEAD' and req.method ~= 'GET' then + return hathaway.method_not_allowed(req, res) + end + + set_json_nocache_headers(res) + + qr = db:run('get', id) + if #qr == 0 then + res.status = 404 + else + add_json_row(res, qr, 1) + end +end) + + +POST('/print.json', function(req, res) + set_json_nocache_headers(res) + + local expected = "application/x-www-form-urlencoded" + assert(string.sub(req.headers['Content-Type'], 1, string.len(expected)) == expected) + + local body = unescape(req:body()) + expected = "image=data:image/png;base64," + local expected_len = string.len(expected) + assert(string.sub(body, 1, expected_len) == expected) + image = base64.dec(string.sub(body, expected_len+1)) + + local qi = queue:new() + local outfile = qi:open("wb") + outfile:write(image) + outfile:close() + qi:queue(); + + res:add('["%s"]', 'OK') +end) + +hathaway.debug = print +assert(Hathaway(bind_addr, bind_port)) + +-- vim: syntax=lua ts=2 sw=2 noet: |