better log, barcode scanner support

This commit is contained in:
metamuffin 2024-10-30 22:00:15 +01:00
parent a7868afbcf
commit c3dadcc5a3
No known key found for this signature in database
GPG key ID: 718F9749DCDBD654

View file

@ -41,13 +41,22 @@ local stylesheet = [[
.amount-neg { color: red; }
nav h2 { display: inline-block }
.notif { padding: 0.5em; margin: 0.5em; background-color: #ddd; }
.notif.error { background-color: #faa; }
.notif p { margin: 5px; }
form.box { border: 2px solid grey; padding: 0.5em; margin: 0.5em; display: inline-block; }
form h3 { margin: 5px; }
]]
-- local script = io.open("main.js"):read("a")
local script = [[
document.addEventListener("keypress", ev => {
if (!(document.activeElement instanceof HTMLInputElement)) {
if (ev.code.startsWith("Digit"))
document.forms.buy_product.product.value += ev.code.substring(5)
if (ev.code == "Enter")
document.forms.buy_product.submit()
}
})
]]
local function respond(status, title, body)
@ -55,6 +64,7 @@ local function respond(status, title, body)
print("Content-Type: text/html")
print("")
print(string.format([[
<!DOCTYPE html>
<html><head>
<title>%s</title>
<meta charset="utf-8" />
@ -88,6 +98,13 @@ local function form_data()
return parse_query(io.read())
end
local function format_duration(t)
if t > 86400 then return string.format("%d days", t / 86400) end
if t > 3600 then return string.format("%d hours", t / 3600) end
if t > 60 then return string.format("%d minutes", t / 60) end
return string.format("%d seconds", t)
end
local function read_log()
local log = io.open("log", "r")
if log == nil then
@ -126,54 +143,65 @@ local function balances()
end
return users
end
local function last_txns()
local users = {}
for time, username, _, _ in read_log() do
users[username] = time
end
return users
end
local function r_user()
if path == nil then
return respond_error("no path")
end
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 function error_box(message)
return string.format([[<div class="notif error"><p>Error: %s</p></div>]], message)
end
local function r_user_post(username)
local data = form_data()
local amount = nil
local comment = ""
if data.barcode then
if data.product then
for p_barcode, p_amount, p_name in read_products() do
if p_barcode == data.barcode then
if p_barcode == data.product then
amount = p_amount
comment = p_name
end
end
if amount == nil then
return error_box("unknown product")
end
else
amount = tonumber(data.amount)
comment = data.comment or ""
end
if amount == nil then
return respond_error("amount invalid")
return error_box("amount invalid")
end
if comment:match("^[%w_ -]*$") == nil then
return respond_error("comment invalid")
return error_box("comment invalid")
end
local log = io.open("log", "a+")
if log == nil then
return respond_error("failed to open log")
return error_box("failed to open log")
end
local time = os.time()
log:write(string.format("%d,%s,%d,%s\n", time, username, amount, comment))
log:flush()
log:close()
notif = string.format(
return 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
end
local function r_user(username)
local notif = nil
if method == "POST" then
notif = r_user_post(username)
end
return respond(200, username, function()
print(string.format("<h1>%s</h1>", username))
local balance = balances()[username]
local last_txn = last_txns()[username]
local new_user = balance == nil
balance = balance or 0
if new_user then
@ -181,11 +209,13 @@ local function r_user()
<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))
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(string.format([[
<p>Last transaction added %s ago. <a href="/%s?log">View user log</a>
]], format_duration(os.time() - last_txn), username))
print([[
<form class="transaction box" action="" method="POST">
<h3>Create Transaction</h3>
@ -195,6 +225,12 @@ local function r_user()
<input type="text" name="comment" id="comment" /><br/>
<input type="submit" value="Update" />
</form>
<form class="transaction box" action="" method="POST" id="buy_product">
<h3>Buy Product</h3>
<label for="product">Product: </label>
<input type="text" name="product" id="product" /><br/>
<input type="submit" value="Buy" />
</form>
]])
print("<div class=\"amount-presets\">")
for _, type in ipairs({ 1, -1 }) do
@ -214,14 +250,20 @@ local function r_user()
end)
end
local function r_log()
local function r_log(filter)
return respond(200, "Log", function()
print("<table>")
print("<tr><th>Time</th><th>Username</th><th>Amount</th><th>Comment</th></tr>")
for time, username, amount, comment in read_log() do
print(string.format("<tr><td>%d</td><td>%s</td><td class=\"amount-%s\">%.02f€</td><td>%s</td></tr>", time,
if filter == nil or filter == username then
print(string.format(
"<tr><td>%d (%s ago)</td><td>%s</td><td class=\"amount-%s\">%.02f€</td><td>%s</td></tr>",
time, format_duration(os.time() - time),
escape(username),
amount >= 0 and "pos" or "neg", amount / 100, escape(comment)))
amount >= 0 and "pos" or "neg", amount / 100,
escape(comment)
))
end
end
print("</table>")
end)
@ -254,6 +296,17 @@ local function r_create_user()
return redirect(string.format("/%s", urlencode(username)))
end
local function extract_username()
if path == nil then
return respond_error("no path")
end
local username = urldecode(path:sub(2))
if username == nil or username:match("^([%w_ -]+)$") == nil then
return nil
end
return username
end
if path == "/" then
if query.log then
return r_log()
@ -263,5 +316,12 @@ if path == "/" then
return r_index()
end
else
return r_user()
local username = extract_username()
if username == nil then
return respond_error("username invalid")
elseif query.log then
return r_log(username)
else
return r_user(username)
end
end