This commit is contained in:
metamuffin 2024-11-04 20:05:04 +01:00
parent 7dc2354e49
commit c78ca2592d
No known key found for this signature in database
GPG key ID: 718F9749DCDBD654
2 changed files with 90 additions and 63 deletions

View file

@ -15,7 +15,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
]]--
]] --
local function escape(s)
return s:gsub("<", "&lt;"):gsub("<", "&lt;")
@ -63,17 +63,50 @@ local function load_config()
return config
end
local function load_translations(langs)
local t = {}
for _, lcode in ipairs(langs) do
local file = io.open(string.format("locale/%s.ini", lcode), "r")
if file ~= nil then
for l in file:lines("l") do
if l ~= "" then
local key, value = string.match(l, "^([^=]+)=([^=]*)")
if key ~= nil and value ~= nil then
t[key] = value
end
end
end
end
end
return t
end
local config = load_config()
local path = os.getenv("PATH_INFO")
local method = os.getenv("REQUEST_METHOD")
local query = parse_query(os.getenv("QUERY_STRING"))
local translations = load_translations({ "en", config.language })
local stylesheet = io.open("style.css"):read("a")
local script = io.open("script.js"):read("a")
local function format(template, params)
return string.gsub(template, "{([%w\\.!]+)}", function(n)
local raw = n:sub(1,1) ~= "!"
if raw then n = n:sub(2) end
local s = format(params[n] or translations[n] or "NIL TEMPLATE", params)
return raw and s or escape(s)
end)
end
local function format_amount(amount, tag, classes)
local s = string.format("%.02f%s", amount / 100, config.unit or "")
if tag == nil then return s end
return string.format([[<%s class="amount-%s %s">%s</%s>"]], tag, amount >= 0 and "pos" or "neg", s, tag, classes or "")
end
local function get_user_theme(username)
local c = ""
local js = ""
if username == "_jeb" then
c = "html { animation: 2s jeb infinite; }"
c = c .. "@keyframes jeb {\n"
@ -84,24 +117,29 @@ local function get_user_theme(username)
elseif username == "Dinnerbone" then
c = "html { transform: scale(-1); } "
end
return c, js
return c
end
local function format_duration(t)
if t > 86400 then return string.format("%d day%s", t / 86400, math.floor(t / 86400) ~= 1 and "s" or "") end
if t > 3600 then return string.format("%d hour%s", t / 3600, math.floor(t / 3600) ~= 1 and "s" or "") end
if t > 60 then return string.format("%d minute%s", t / 60, math.floor(t / 60) ~= 1 and "s" or "") end
return string.format("%d seconds", t)
end
local function respond(status, title, body)
local themecss, themejs = get_user_theme(path and path:sub(2))
print(string.format("Status: %d", status))
print("Content-Type: text/html")
print("")
print(string.format([[
print(format([[
<!DOCTYPE html>
<html><head>
<title>%s</title>
<title>{title}</title>
<meta charset="utf-8" />
<style>%s</style>
<style>%s</style>
<script>%s</script>
%s
<style>{style}</style>
<style>{user_style}</style>
<script>{script}</script>
{head_extra}
</head>
<body>
<nav>
@ -110,23 +148,21 @@ local function respond(status, title, body)
<path d="M3 4.5a.5.5 0 0 1 .5-.5h6a.5.5 0 1 1 0 1h-6a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h6a.5.5 0 1 1 0 1h-6a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h6a.5.5 0 1 1 0 1h-6a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 0 1h-6a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 0 1h-6a.5.5 0 0 1-.5-.5M11.5 4a.5.5 0 0 0 0 1h1a.5.5 0 0 0 0-1zm0 2a.5.5 0 0 0 0 1h1a.5.5 0 0 0 0-1zm0 2a.5.5 0 0 0 0 1h1a.5.5 0 0 0 0-1zm0 2a.5.5 0 0 0 0 1h1a.5.5 0 0 0 0-1zm0 2a.5.5 0 0 0 0 1h1a.5.5 0 0 0 0-1z"/>
<path d="M2.354.646a.5.5 0 0 0-.801.13l-.5 1A.5.5 0 0 0 1 2v13H.5a.5.5 0 0 0 0 1h15a.5.5 0 0 0 0-1H15V2a.5.5 0 0 0-.053-.224l-.5-1a.5.5 0 0 0-.8-.13L13 1.293l-.646-.647a.5.5 0 0 0-.708 0L11 1.293l-.646-.647a.5.5 0 0 0-.708 0L9 1.293 8.354.646a.5.5 0 0 0-.708 0L7 1.293 6.354.646a.5.5 0 0 0-.708 0L5 1.293 4.354.646a.5.5 0 0 0-.708 0L3 1.293zm-.217 1.198.51.51a.5.5 0 0 0 .707 0L4 1.707l.646.647a.5.5 0 0 0 .708 0L6 1.707l.646.647a.5.5 0 0 0 .708 0L8 1.707l.646.647a.5.5 0 0 0 .708 0L10 1.707l.646.647a.5.5 0 0 0 .708 0L12 1.707l.646.647a.5.5 0 0 0 .708 0l.509-.51.137.274V15H2V2.118z"/>
</svg>
Abrechenbarkeit</a>
<a href="/?log">Log</a>
<a href="/?products">Products</a>
<a href="/?about">About</a>
{appname}
</a>
<a href="/?log">{log}</a>
<a href="/?products">{products}</a>
<a href="/?about">{about}</a>
</nav>
]],
escape(title),
stylesheet, -- style.css
themecss, -- theme for user
script, -- script.js
config.head_extra or ""
))
]], {
title = escape(title),
style = stylesheet,
user_style = get_user_theme(path and path:sub(2)),
script = script,
head_extra = config.head_extra or ""
}))
body()
print(string.format(
"<script>%s</script></body></html>",
themejs
))
print("</body></html>")
end
local function error_box(message)
@ -149,13 +185,6 @@ local function form_data()
return parse_query(io.read())
end
local function format_duration(t)
if t > 86400 then return string.format("%d day%s", t / 86400, math.floor(t / 86400) ~= 1 and "s" or "") end
if t > 3600 then return string.format("%d hour%s", t / 3600, math.floor(t / 3600) ~= 1 and "s" or "") end
if t > 60 then return string.format("%d minute%s", t / 60, math.floor(t / 60) ~= 1 and "s" or "") end
return string.format("%d seconds", t)
end
local function read_log()
local log = io.open("log", "r")
if log == nil then
@ -285,14 +314,15 @@ local function r_user_post(username)
end
log:flush()
log:close()
return string.format([[
<div class="notif"><p>Transaction successful: <strong class="amount-%s">%.02f</strong> (%s)</p></div>
<audio src="%s" autoplay></audio>
]],
amount >= 0 and "pos" or "neg", amount / 100,
escape(comment),
config.transaction_sound or ""
)
return format([[
<div class="notif"><p>Transaction successful: {amount} ({!comment})</p></div>
<audio src="{sound}" autoplay></audio>
]], {
sign = amount >= 0 and "pos" or "neg",
amount = format_amount(amount, "strong"),
comment = escape(comment),
sound = config.transaction_sound or ""
})
end
local function r_user(username)
@ -301,24 +331,20 @@ local function r_user(username)
notif = r_user_post(username)
end
return respond(200, string.format("Abrechenbarheit: %s", username), function()
print(string.format("<h1>%s</h1>", username))
print(format("<h1>{username}</h1>", { username = username }))
local balance = balances()[username]
local last_txn = last_txns()[username]
local new_user = balance == nil
balance = balance or 0
if notif then print(notif) end
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>
]])
print([[<div class="notif"><p><i>{user.lazy_creation}</i></p></div>]])
else
print([[<div class="backgroundbox userinfo">]])
print(string.format([[
Current balance:<br><span class="amount-%s balance-value">%.02f</span><br>
]], balance >= 0 and "pos" or "neg", balance / 100))
print(string.format([[
Last transaction added %s ago. <a href="/%s?log">View user log</a>
]], format_duration(os.time() - last_txn), username))
print(format([[{user.balance}: <br>{amount}<br>]],
{ sign = balance >= 0 and "pos" or "neg", amount = format_amount(balance, "span", "balance-value") }))
print(format([[{user.last_txn} <a href="/{username}?log">{user.view_log}</a>]],
{ time = format_duration(os.time() - last_txn), username = urlencode(username) }))
print([[</div>]])
end
print([[<div class="transactions container firstchildlarge">]])
@ -336,35 +362,35 @@ local function r_user(username)
end
end
print("</div>")
print([[
print(format([[
<form class="transaction box backgroundbox" action="" method="POST">
<h3>Create Transaction</h3>
<h3>{user.form.transaction}</h3>
<label for="amount">Amount (ct): </label>
<input type="number" name="amount" id="amount" />
<label for="comment">Comment: </label>
<input type="text" name="comment" id="comment" />
<input type="submit" value="Update" class="amount-ntr button" />
<input type="submit" value="{user.form.transaction.submit}" class="amount-ntr button" />
</form>
<form class="transaction box backgroundbox" action="" method="POST" id="buy_product">
<h3>Buy Product</h3>
<h3>{user.form.buy}</h3>
<input type="text" name="negate_pcount" value="1" hidden />
<label for="pcount">Count: </label>
<input type="number" name="pcount" id="pcount" value="1" />
<label for="pcode">Product Barcode: </label>
<label for="pcode">{field.barcode}: </label>
<input type="text" name="pcode" id="pcode" />
<input class="amount-neg button" type="submit" value="Buy" />
<input class="amount-neg button" type="submit" value="{user.form.buy.submit}" />
</form>
<form class="transaction box backgroundbox" action="" method="POST" id="buy_product">
<h3>Restock Product</h3>
<label for="pcount">Count: </label>
<h3>{user.form.restock}</h3>
<label for="pcount">{field.count}: </label>
<input type="number" name="pcount" id="pcount" value="1" />
<label for="amount">Upstream price: </label>
<label for="amount">{field.upstream_price}: </label>
<input type="number" name="amount" id="amount" />
<label for="pcode">Product Barcode: </label>
<label for="pcode">{field.barcode}: </label>
<input type="text" name="pcode" id="pcode" />
<input type="submit" value="Restock" class="button amount-pos" />
<input type="submit" value="{user.form.restock.submit}" class="button amount-pos" />
</form>
]])
]]))
print("</div>")
end)
end

View file

@ -35,3 +35,4 @@ user.form.transaction.submit=Update
user.form.transaction=Create Transaction
user.last_txn=Last transaction added %s.
user.view_log=View user log
user.lazy_creation=This user account does not exist yet. It will only be created after the first transaction.