2024-10-30 01:00:27 +00:00
|
|
|
#!/usr/bin/env luajit
|
|
|
|
|
|
|
|
local path = os.getenv("PATH_INFO")
|
|
|
|
local method = os.getenv("REQUEST_METHOD")
|
2024-10-30 01:11:09 +00:00
|
|
|
local query = os.getenv("QUERY_STRING")
|
2024-10-30 01:00:27 +00:00
|
|
|
|
|
|
|
local function escape(s)
|
|
|
|
return s:gsub("<", "<"):gsub("<", "<")
|
|
|
|
end
|
|
|
|
|
|
|
|
local function respond(status, title, body)
|
|
|
|
print(string.format("Status: %d", status))
|
|
|
|
print("Content-Type: text/html")
|
|
|
|
print("")
|
|
|
|
print(string.format([[
|
|
|
|
<html><head>
|
|
|
|
<title>%s</title>
|
|
|
|
<meta charset="utf-8" />
|
|
|
|
</head><body>
|
|
|
|
]], escape(title)))
|
|
|
|
body()
|
|
|
|
print("</body></html>")
|
|
|
|
end
|
|
|
|
|
|
|
|
local function respond_error(message)
|
|
|
|
respond(400, "Error", function()
|
|
|
|
print(string.format("<p>Error: %s</p>", escape(message)))
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2024-10-30 11:20:03 +00:00
|
|
|
local function redirect(path)
|
|
|
|
print("Status: 307")
|
|
|
|
print(string.format("Location: %s", path))
|
|
|
|
print()
|
|
|
|
end
|
|
|
|
|
2024-10-30 01:00:27 +00:00
|
|
|
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
|
|
|
|
end
|
|
|
|
|
|
|
|
local function read_log()
|
|
|
|
local log = io.open("log", "r")
|
|
|
|
if log == nil then
|
|
|
|
return function() return nil end
|
|
|
|
end
|
|
|
|
local lines = log:lines("l")
|
|
|
|
return function()
|
|
|
|
local l = lines()
|
|
|
|
local time, username, amount, comment = string.match(l, "(%d+),([%w_-]+),(-?%d+),([%w_-]*)")
|
|
|
|
return tonumber(time), username, tonumber(amount), comment
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-10-30 01:11:09 +00:00
|
|
|
local function balances()
|
|
|
|
local users = {}
|
|
|
|
for _, username, amount, _ in read_log() do
|
|
|
|
users[username] = (users[username] or 0) + amount
|
|
|
|
end
|
|
|
|
return users
|
|
|
|
end
|
|
|
|
|
2024-10-30 11:20:03 +00:00
|
|
|
local function r_user()
|
|
|
|
if path == nil then
|
|
|
|
return respond_error("no path")
|
2024-10-30 01:11:09 +00:00
|
|
|
end
|
2024-10-30 01:00:27 +00:00
|
|
|
local username = path:sub(2)
|
|
|
|
if username:match("^[%w_-]+$") == nil then
|
|
|
|
return respond_error("username invalid")
|
|
|
|
end
|
|
|
|
|
|
|
|
if method == "POST" then
|
|
|
|
local data = form_data()
|
|
|
|
local amount = tonumber(data.amount)
|
|
|
|
if amount == nil then
|
|
|
|
return respond_error("amount invalid")
|
|
|
|
end
|
|
|
|
local comment = data.comment or ""
|
|
|
|
if comment:match("^[%w_-]*$") == nil then
|
|
|
|
return respond_error("comment invalid")
|
|
|
|
end
|
|
|
|
local log = io.open("log", "a+")
|
|
|
|
if log == nil then
|
|
|
|
return respond_error("failed to open log")
|
|
|
|
end
|
|
|
|
local time = os.time()
|
|
|
|
log:write(string.format("%d,%s,%s,%s\n", time, username, amount, comment))
|
|
|
|
log:flush()
|
|
|
|
log:close()
|
|
|
|
end
|
|
|
|
|
2024-10-30 11:20:03 +00:00
|
|
|
return respond(200, username, function()
|
2024-10-30 01:00:27 +00:00
|
|
|
print(string.format("<h1>%s</h1>", username))
|
2024-10-30 11:20:03 +00:00
|
|
|
local balance = balances()[username] or 0
|
|
|
|
print(string.format("Current balance: %.02f", balance / 100))
|
2024-10-30 01:00:27 +00:00
|
|
|
print([[
|
|
|
|
<form action="" method="POST">
|
|
|
|
<label for="amount">Amount: </label>
|
|
|
|
<input type="number" name="amount" id="amount" /><br/>
|
|
|
|
<label for="comment">Comment: </label>
|
|
|
|
<input type="text" name="comment" id="comment" /><br/>
|
|
|
|
<input type="submit" value="Update" />
|
|
|
|
</form>
|
|
|
|
]])
|
|
|
|
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€" />
|
|
|
|
</form>
|
|
|
|
]], amount * type, ({ [-1] = "-", [1] = "+" })[type], amount / 100))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
2024-10-30 11:20:03 +00:00
|
|
|
|
|
|
|
local function r_log()
|
|
|
|
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>%.02f€</td><td>%s</td></tr>", time, escape(username),
|
|
|
|
amount / 100, escape(comment)))
|
|
|
|
end
|
|
|
|
print("</table>")
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
local function r_index()
|
|
|
|
return respond(200, "Users", function()
|
|
|
|
print([[
|
|
|
|
<form action="/" method="GET">
|
|
|
|
<label for="username">Username: </label>
|
|
|
|
<input type="text" name="create_user" id="username" /><br/>
|
|
|
|
<input type="submit" value="Create" />
|
|
|
|
</form>
|
|
|
|
]])
|
|
|
|
print("<ul>")
|
|
|
|
for username, balance in pairs(balances()) do
|
|
|
|
print(string.format("<li><a href=\"/%s\">%s</a>: %.02f€</li>", escape(username), escape(username),
|
|
|
|
balance / 100))
|
|
|
|
end
|
|
|
|
print("</ul>")
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
local function r_create_user()
|
|
|
|
if query == nil then
|
|
|
|
return respond_error("no query")
|
|
|
|
end
|
|
|
|
local username = query:match("^\\?create_user=([%w_-]+)$")
|
|
|
|
if username == nil then
|
|
|
|
return respond_error("invalid username")
|
|
|
|
end
|
|
|
|
return redirect(string.format("/%s", username))
|
|
|
|
end
|
|
|
|
|
|
|
|
if path == "/" then
|
|
|
|
if query == "?log" then
|
|
|
|
return r_log()
|
|
|
|
elseif query ~= nil and query:match("^\\?create_user=") then
|
|
|
|
return r_create_user()
|
|
|
|
else
|
|
|
|
return r_index()
|
|
|
|
end
|
|
|
|
else
|
|
|
|
r_user()
|
|
|
|
end
|
|
|
|
|
|
|
|
return respond_error("unknown route")
|