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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
#!/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 redirect(path)
print("Status: 307")
print(string.format("Location: %s", path))
print()
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
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
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
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))
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
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")
|