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 -}))