aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-10-30 13:50:36 +0100
committermetamuffin <metamuffin@disroot.org>2024-10-30 13:50:36 +0100
commit498cbfe9c862e09d603b77d9d0e6555e2a290ad6 (patch)
treec1454e74c901ff253593f65b184cd19203f20220
parent435c2a78f93abd6e7f2692e7def8f9935dae27d6 (diff)
downloadabrechenbarkeit-498cbfe9c862e09d603b77d9d0e6555e2a290ad6.tar
abrechenbarkeit-498cbfe9c862e09d603b77d9d0e6555e2a290ad6.tar.bz2
abrechenbarkeit-498cbfe9c862e09d603b77d9d0e6555e2a290ad6.tar.zst
refactor things
-rwxr-xr-xstrichliste.lua164
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("<", "&lt;"):gsub("<", "&lt;")
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")