1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
#!/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 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
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
local function balances()
local users = {}
for _, username, amount, _ in read_log() do
users[username] = (users[username] or 0) + amount
end
return users
end
if path == "/" then
if query == "?log" then
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
return respond(200, "Users", function()
print("<ul>")
for username, balance in balances() do
print(string.format("<li><a href=\"/%s\">%s</a>: %.02f€</li>", escape(username), escape(username),
balance / 100))
end
print("</ul>")
end)
elseif path ~= nil then
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
respond(200, username, function()
print(string.format("<h1>%s</h1>", username))
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
|