diff options
-rwxr-xr-x | strichliste.lua | 164 |
1 files changed, 123 insertions, 41 deletions
diff --git a/strichliste.lua b/strichliste.lua index f7bef9c..c3ce80e 100755 --- a/strichliste.lua +++ b/strichliste.lua @@ -1,12 +1,52 @@ #!/usr/bin/env luajit -local path = os.getenv("PATH_INFO") -local method = os.getenv("REQUEST_METHOD") -local query = os.getenv("QUERY_STRING") - local function escape(s) return s:gsub("<", "<"):gsub("<", "<") end +local function urldecode(s) + if s == nil then return nil end + return s:gsub("+", " "):gsub("%%20", " ") +end +local function urlencode(s) + if s == nil then return nil end + return s:gsub(" ", "%%20") +end + +local function parse_query(q) + if q == nil then return {} end + local data = {} + for pair in string.gmatch(q, "([^&]+)") do + local flag = string.match(pair, "^([^=]+)$") + if flag ~= nil then + data[flag] = "1" + else + local key, value = string.match(pair, "^([^=]+)=([^=]*)$") + if key ~= nil and value ~= nil then + data[key] = urldecode(value) + end + end + end + return data +end + +local path = os.getenv("PATH_INFO") +local method = os.getenv("REQUEST_METHOD") +local query = parse_query(os.getenv("QUERY_STRING")) + +local stylesheet = [[ + /* body { background-color: #161616; } + h1, h2, h3, h4, h5, h6, p, label, a { color: #e2e2e2; } */ + .amount-presets form { display: inline-block; width: 60px } + .amount-pos { color: green; } + .amount-neg { color: red; } + nav h2 { display: inline-block } + .notif { padding: 0.5em; margin: 0.5em; background-color: #ddd; } + .notif p { margin: 5px } +]] + +local script = [[ + +]] local function respond(status, title, body) print(string.format("Status: %d", status)) @@ -16,8 +56,16 @@ local function respond(status, title, body) <html><head> <title>%s</title> <meta charset="utf-8" /> - </head><body> - ]], escape(title))) + <style>%s</style> + <script>%s</script> + </head> + <body> + <nav> + <h2><a href="/">Strichliste v2</a></h2> + <span><a href="/?log">View Log</a></span> + <span><a href="https://codeberg.org/metamuffin/strichliste">Source</a></span> + </nav> + ]], escape(title), stylesheet, script)) body() print("</body></html>") end @@ -35,16 +83,7 @@ local function redirect(path) end local function form_data() - local data = {} - for pair in string.gmatch(io.read(), "([^&]+)") do - local key, value = string.match(pair, "([^=]+)=([^=]+)") - if key == nil or value == nil then - goto continue - end - data[key] = value - ::continue:: - end - return data + return parse_query(io.read()) end local function read_log() @@ -55,10 +94,28 @@ local function read_log() local lines = log:lines("l") return function() local l = lines() - local time, username, amount, comment = string.match(l, "(%d+),([%w_-]+),(-?%d+),([%w_-]*)") + if l == "" or l == nil then + return nil + end + local time, username, amount, comment = string.match(l, "(%d+),([%w_ -]+),(-?%d+),([%w_ -]*)") return tonumber(time), username, tonumber(amount), comment end end +local function read_products() + local log = io.open("products", "r") + if log == nil then + return function() return nil end + end + local lines = log:lines("l") + return function() + local l = lines() + if l == "" or l == nil then + return nil + end + local barcode, amount, name = string.match(l, "([%w_-]+),(-?%d+),([%w_ -]*)") + return barcode, tonumber(amount), name + end +end local function balances() local users = {} @@ -72,19 +129,30 @@ local function r_user() if path == nil then return respond_error("no path") end - local username = path:sub(2) - if username:match("^[%w_-]+$") == nil then + local username = urldecode(path:sub(2)) + if username == nil or username:match("^([%w_ -]+)$") == nil then return respond_error("username invalid") end - + local notif = nil if method == "POST" then local data = form_data() - local amount = tonumber(data.amount) + local amount = nil + local comment = "" + if data.barcode then + for p_barcode, p_amount, p_name in read_products() do + if p_barcode == data.barcode then + amount = p_amount + comment = p_name + end + end + else + amount = tonumber(data.amount) + comment = data.comment or "" + end if amount == nil then return respond_error("amount invalid") end - local comment = data.comment or "" - if comment:match("^[%w_-]*$") == nil then + if comment:match("^[%w_ -]*$") == nil then return respond_error("comment invalid") end local log = io.open("log", "a+") @@ -95,14 +163,29 @@ local function r_user() log:write(string.format("%d,%s,%s,%s\n", time, username, amount, comment)) log:flush() log:close() + notif = string.format( + "<div class=\"notif\"><p>Transaction successful: <strong class=\"amount-%s\">%.02f€</strong> (%s)</p></div>", + amount >= 0 and "pos" or "neg", amount / 100, escape(comment) + ) end return respond(200, username, function() print(string.format("<h1>%s</h1>", username)) - local balance = balances()[username] or 0 - print(string.format("Current balance: %.02f", balance / 100)) + local balance = balances()[username] + local new_user = balance == nil + balance = balance or 0 + if new_user then + print([[ + <div class="notif"><p><i>This user account does not exist yet. It will only be created after the first transaction.</i></p></div> + ]]) + end + if notif then + print(notif) + end + print(string.format("<p>Current balance: <span class=\"amount-%s\">%.02f€</p>", balance >= 0 and "pos" or "neg", + balance / 100)) print([[ - <form action="" method="POST"> + <form class="transaction" action="" method="POST"> <label for="amount">Amount: </label> <input type="number" name="amount" id="amount" /><br/> <label for="comment">Comment: </label> @@ -110,17 +193,21 @@ local function r_user() <input type="submit" value="Update" /> </form> ]]) + print("<div class=\"amount-presets\">") for _, type in ipairs({ 1, -1 }) do for _, amount in ipairs({ 50, 100, 150, 200, 500, 1000 }) do print(string.format([[ <form action="" method="POST"> <input type="number" name="amount" id="amount" value="%d" hidden /> <input type="text" name="comment" id="comment" value="" hidden /> - <input type="submit" value="%s%.02f€" /> + <input type="submit" value="%s%.02f€" class="amount-%s" /> </form> - ]], amount * type, ({ [-1] = "-", [1] = "+" })[type], amount / 100)) + ]], amount * type, ({ [-1] = "-", [1] = "+" })[type], amount / 100, + ({ [-1] = "neg", [1] = "pos" })[type])) end + print("<br/>") end + print("</div>") end) end @@ -142,7 +229,7 @@ local function r_index() <form action="/" method="GET"> <label for="username">Username: </label> <input type="text" name="create_user" id="username" /><br/> - <input type="submit" value="Create" /> + <input type="submit" value="Continue" /> </form> ]]) print("<ul>") @@ -155,26 +242,21 @@ local function r_index() end local function r_create_user() - if query == nil then - return respond_error("no query") + local username = query.create_user + if username:match("^([%w_ -]+)$") == nil then + return respond_error("invalid username " .. username) end - local username = query:match("^\\?create_user=([%w_-]+)$") - if username == nil then - return respond_error("invalid username") - end - return redirect(string.format("/%s", username)) + return redirect(string.format("/%s", urlencode(username))) end if path == "/" then - if query == "?log" then + if query.log then return r_log() - elseif query ~= nil and query:match("^\\?create_user=") then + elseif query.create_user then return r_create_user() else return r_index() end else - r_user() + return r_user() end - -return respond_error("unknown route") |