middlewares, add admin templates

This commit is contained in:
Sky Johnson 2025-05-28 22:40:54 -05:00
parent bcf4c99167
commit 2a74f69f70
18 changed files with 293 additions and 51 deletions

4
.gitignore vendored
View File

@ -1,2 +1,2 @@
*.db-shm
*.db-wal
*.db-*
*.fasthttp.br

Binary file not shown.

View File

@ -0,0 +1,22 @@
<p>
Welcome to the Dragon Knight Administration section. Use the links on the left
bar to control and edit various elements of the game.
</p>
<p>
Please note that the control panel has been created mostly as a shortcut for certain
individual settings. It is meant for use primarily with editing one thing at a time.
If you need to completely replace an entire table (say, to replace all stock monsters
with your own new ones), it is suggested that you use a more in-depth database tool such
as <a href="http://www.phpmyadmin.net" target="_new">phpMyAdmin</a>. Also, you may want to
have a copy of the Dragon Knight development kit, available from the
<a href="http://dragon.se7enet.com/dev.php">Dragon Knight homepage</a>.
</p>
<p>
Also, you should be aware that certain portions of the DK code are dependent on the
formatting of certain database results (for example, the special attributes on item drops).
While I have attempted to point these out throughout the admin script, you should
definitely pay attention and be careful when editing some fields, because mistakes in the
database content may result in script errors or your game breaking completely.
</p>

View File

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }} - DK Admin</title>
<link rel="stylesheet" href="/css/admin.css">
<script src="/js/htmx.js"></script>
</head>
<body>
<div id="admin-container">
<header>
<h1>Dragon Knight</h1>
<h3>Admin</h3>
</header>
<main>
<nav>
<a href="/admin" hx-get="/admin" hx-target="#main">Admin Home</a><br>
<a href="/">Game Home</a><br><br>
<br>
<a href="/admin/main" hx-get="/admin/main" hx-target="#main">Main Settings</a><br>
<a href="/admin/news" hx-get="/admin/news" hx-target="#main">Add News Post</a><br>
<a href="/admin/users" hx-get="/admin/users" hx-target="#main">Edit Users</a><br><br>
<br>
<a href="/admin/items" hx-get="/admin/items" hx-target="#main">Edit Items</a><br>
<a href="/admin/drops" hx-get="/admin/drops" hx-target="#main">Edit Drops</a><br>
<a href="/admin/towns" hx-get="/admin/towns" hx-target="#main">Edit Towns</a><br>
<a href="/admin/monsters" hx-get="/admin/monsters" hx-target="#main">Edit Monsters</a><br>
<a href="/admin/levels" hx-get="/admin/levels" hx-target="#main">Edit Levels</a><br>
<a href="/admin/spells" hx-get="/admin/spells" hx-target="#main">Edit Spells</a><br>
</nav>
<section id="main">
{{{ content }}}
</section>
</main>
<footer>
<div>Powered by <a href="/" target="_new">Dragon Knight</a></div>
<div>&copy; 2024 Sharkk</div>
<div>Version {{ version }} {{ build }}</div>
</footer>
</div>
</body>
</html>

BIN
moonshark

Binary file not shown.

129
public/css/admin.css Normal file
View File

@ -0,0 +1,129 @@
:root {
--font-size: 16px;
--font-family: Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-size: var(--font-size);
font-family: var(--font-family);
}
body {
padding: 2rem;
color: rgb(108, 108, 108);
background-color: #121212;
}
h1, h2, h3, h4, h5 {
color: rgb(30, 30, 30);
}
div#admin-container {
max-width: 1280px;
margin: 0 auto;
padding: 1rem;
}
header {
margin-bottom: 2rem;
}
main {
display: flex;
gap: 2rem;
margin-bottom: 2rem;
}
main > nav {
flex: 0 0 auto;
}
main > section {
flex: 1;
}
table {
width: 100%;
border-collapse: collapse;
outline-width: none;
font-family: var(--font-family);
border: 1px solid rgba(0, 0, 0, 0.1);
& > caption {
margin: 1rem;
}
& :is(td,th) {
border: 1px solid rgba(0, 0, 0, 0.4);
padding: 0.5rem 1rem;
}
& thead tr {
background: rgba(0, 0, 0, 0.1);
}
& tbody tr:nth-of-type(even) {
background: rgba(0, 0, 0, 0.1);
&:hover { background: rgba(0, 0, 0, 0.2); }
}
td:hover {
color: white;
background: rgba(0, 0, 0, 0.1);
}
tr:hover {
background: rgba(0, 0, 0, 0.2);
}
}
.table-wrapper {
width: 100%; /* Ensure the wrapper takes 100% of the parent's width */
max-height: 300px; /* Set the desired height limit */
overflow-x: auto; /* Enable horizontal scrolling if the table overflows */
overflow-y: auto; /* Enable vertical scrolling if needed */
display: block; /* Ensure block-level behavior */
-webkit-overflow-scrolling: touch; /* Smooth scrolling on touch devices */
}
a {
color: #015df7;
text-decoration: none;
cursor: pointer;
&:hover {
color: hsl(218, 99%, 29%);
text-decoration: underline;
}
}
.small {
font: 10px verdana;
}
.highlight {
color: red;
}
.light {
color: #999999;
}
.title {
border: solid 1px black;
background-color: #eeeeee;
font-weight: bold;
padding: 5px;
margin: 3px;
}
footer {
display: flex;
justify-content: space-around;
font-size: 0.8rem;
padding: 0.5rem;
margin: 2rem 0;
}

View File

@ -0,0 +1,6 @@
local base_template = fs_read("templates/base.html")
local login_form = fs_read("templates/auth/login.html")
return send_html(render(base_template, {
title = "Login",
content = login_form
}))

View File

@ -0,0 +1,30 @@
local db = sqlite("dk")
local username = string.trim(ctx.form.username)
if username == "" or not db:exists("users", "username = :u COLLATE NOCASE", {u = username}) then
return "username required and must exist"
end
if ctx.form.password == "" then
return "password required"
end
local user_row = db:get_one("SELECT id, username, password FROM users WHERE username = :u COLLATE NOCASE", {u = username})
if not password_verify(ctx.form.password, user_row.password) then
return "wrong username or password"
end
local token = generate_token()
local expires = os.time() + (30 * 24 * 60 * 60) -- 30 days
db:insert("user_sessions", {
user_id = user_row.id,
token = token,
expires = expires
})
cookie_set("dkauth", token, { expires = expires })
session_set("logged_in", true)
session_set("user_id", user_row.id)
return "Logged in!"

View File

@ -0,0 +1,3 @@
if session_get("logged_in") then
http_redirect("/")
end

View File

@ -0,0 +1,11 @@
session_set("test", {
foo = "bar",
hen = "tai"
})
local base_template = fs_read("templates/base.html")
local register_form = fs_read("templates/auth/register.html")
return send_html(render(base_template, {
title = "Register",
content = register_form
}))

View File

@ -1,12 +1,12 @@
local db = sqlite("dk")
local username = string.trim(ctx.form.username)
if username == "" or db:exists("users", "username = :u", {u = username}) then
if username == "" or db:exists("users", "username = :u COLLATE NOCASE", {u = username}) then
return "username required and must be unique"
end
local email = string.trim(ctx.form.email)
if util.sanitize_email(email) == "" or db:exists("users", "email = :e", {e = email}) then
if sanitize_email(email) == "" or db:exists("users", "email = :e COLLATE NOCASE", {e = email}) then
return "email required, must be valid format, and must be unique"
end
@ -17,7 +17,7 @@ end
db:insert("users", {
username = username,
email = email,
password = password.hash(ctx.form.password),
password = password_hash(ctx.form.password),
attributes_id = 0
})

9
routes/admin/get.lua Normal file
View File

@ -0,0 +1,9 @@
return send_html(render(
fs_read("templates/admin/layout.html"),
{
title = "Home",
content = fs_read("templates/admin/home.html"),
version = "v1.0.0",
build = "Moonlight"
}
))

View File

@ -0,0 +1,8 @@
if not session_get("logged_in") then
http_redirect("/")
end
local d = sqlite("dk"):get_one("SELECT auth_level FROM users WHERE id = :i", {i = session_get("user_id")})
if d.auth_level < 2 then
http_redirect("/")
end

View File

@ -1,5 +1,5 @@
if session.get("logged_in") then
return "Hello, "..session.get("user_id").."!"
if session_get("logged_in") then
return "Welcome, "..session_get("user_id")
else
return "Hello, guest!"
return "Welcome, guest!"
end

View File

@ -1,6 +0,0 @@
local base_template = fs.read("templates/base.html")
local login_form = fs.read("templates/auth/login.html")
return send.html(render(base_template, {
title = "Login",
content = login_form
}))

View File

@ -1,31 +0,0 @@
local db = sqlite("dk")
local username = string.trim(ctx.form.username)
if username == "" or not db:exists("users", "username = :u", {u = username}) then
return "username required and must exist"
end
if ctx.form.password == "" then
return "password required"
end
local user_row = db:get_one("SELECT id, username, password, auth_level FROM users WHERE username = :u", {u = username})
if not password.verify(ctx.form.password, user_row.password) then
return "wrong username or password"
end
local token = util.generate_token()
local expires = os.time() + (30 * 24 * 60 * 60) -- 30 days
db:insert("user_sessions", {
user_id = user_row.id,
token = token,
expires = expires
})
cookie.set("dkauth", token, { expires = expires })
session.set("logged_in", true)
session.set("user_id", user_row.id)
session.set("auth_level", user_row.auth_level)
return "Logged in!"

20
routes/middleware.lua Normal file
View File

@ -0,0 +1,20 @@
if not session_get("logged_in") then
local cookie = cookie_get("dkauth")
if cookie then
local db = sqlite("dk")
local d = db:get_one("SELECT * FROM user_sessions WHERE token = :t", {t = cookie})
if d then
if d.expires < os.time() then
db:exec("DELETE FROM user_sessions WHERE token = :t", {t = cookie})
session_set("logged_in", false)
else
session_set("logged_in", true)
session_set("user_id", d.user_id)
end
else
session_set("logged_in", false)
end
end
end

View File

@ -1,6 +0,0 @@
local base_template = fs.read("templates/base.html")
local register_form = fs.read("templates/auth/register.html")
return send.html(render(base_template, {
title = "Register",
content = register_form
}))