diff --git a/.gitignore b/.gitignore
index b7eca6e..c490a1b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
-*.db-shm
-*.db-wal
+*.db-*
+*.fasthttp.br
\ No newline at end of file
diff --git a/data/dk.db b/data/dk.db
index 4a00f91..bb555e3 100644
Binary files a/data/dk.db and b/data/dk.db differ
diff --git a/fs/templates/admin/home.html b/fs/templates/admin/home.html
new file mode 100644
index 0000000..72621c8
--- /dev/null
+++ b/fs/templates/admin/home.html
@@ -0,0 +1,22 @@
+
+ Welcome to the Dragon Knight Administration section. Use the links on the left
+ bar to control and edit various elements of the game.
+
+
+
+ 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 phpMyAdmin. Also, you may want to
+ have a copy of the Dragon Knight development kit, available from the
+ Dragon Knight homepage.
+
+
+
+ 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.
+
\ No newline at end of file
diff --git a/fs/templates/admin/layout.html b/fs/templates/admin/layout.html
new file mode 100644
index 0000000..20616cd
--- /dev/null
+++ b/fs/templates/admin/layout.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+ {{ title }} - DK Admin
+
+
+
+
+
+
+
Dragon Knight
+
Admin
+
+
+
+
+
+ {{{ content }}}
+
+
+
+
+
+
+
diff --git a/moonshark b/moonshark
index 442c5f1..d949765 100755
Binary files a/moonshark and b/moonshark differ
diff --git a/public/css/admin.css b/public/css/admin.css
new file mode 100644
index 0000000..fd456bb
--- /dev/null
+++ b/public/css/admin.css
@@ -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;
+}
diff --git a/routes/(guest)/login/get.lua b/routes/(guest)/login/get.lua
new file mode 100644
index 0000000..40898c8
--- /dev/null
+++ b/routes/(guest)/login/get.lua
@@ -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
+}))
diff --git a/routes/(guest)/login/post.lua b/routes/(guest)/login/post.lua
new file mode 100644
index 0000000..87f7fa4
--- /dev/null
+++ b/routes/(guest)/login/post.lua
@@ -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!"
diff --git a/routes/(guest)/middleware.lua b/routes/(guest)/middleware.lua
new file mode 100644
index 0000000..953f8e5
--- /dev/null
+++ b/routes/(guest)/middleware.lua
@@ -0,0 +1,3 @@
+if session_get("logged_in") then
+ http_redirect("/")
+end
\ No newline at end of file
diff --git a/routes/(guest)/register/get.lua b/routes/(guest)/register/get.lua
new file mode 100644
index 0000000..ef4372b
--- /dev/null
+++ b/routes/(guest)/register/get.lua
@@ -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
+}))
diff --git a/routes/register/post.lua b/routes/(guest)/register/post.lua
similarity index 63%
rename from routes/register/post.lua
rename to routes/(guest)/register/post.lua
index 7a97e20..8201e0b 100644
--- a/routes/register/post.lua
+++ b/routes/(guest)/register/post.lua
@@ -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
})
diff --git a/routes/admin/get.lua b/routes/admin/get.lua
new file mode 100644
index 0000000..4bc0dfb
--- /dev/null
+++ b/routes/admin/get.lua
@@ -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"
+ }
+))
\ No newline at end of file
diff --git a/routes/admin/middleware.lua b/routes/admin/middleware.lua
new file mode 100644
index 0000000..b317080
--- /dev/null
+++ b/routes/admin/middleware.lua
@@ -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
diff --git a/routes/get.lua b/routes/get.lua
index 2b82071..28b4b8e 100644
--- a/routes/get.lua
+++ b/routes/get.lua
@@ -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
diff --git a/routes/login/get.lua b/routes/login/get.lua
deleted file mode 100644
index 352425d..0000000
--- a/routes/login/get.lua
+++ /dev/null
@@ -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
-}))
diff --git a/routes/login/post.lua b/routes/login/post.lua
deleted file mode 100644
index 0010a41..0000000
--- a/routes/login/post.lua
+++ /dev/null
@@ -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!"
diff --git a/routes/middleware.lua b/routes/middleware.lua
new file mode 100644
index 0000000..b53e202
--- /dev/null
+++ b/routes/middleware.lua
@@ -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
\ No newline at end of file
diff --git a/routes/register/get.lua b/routes/register/get.lua
deleted file mode 100644
index 12d543d..0000000
--- a/routes/register/get.lua
+++ /dev/null
@@ -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
-}))