diff --git a/assets/css/fonts.css b/assets/css/fonts.css new file mode 100644 index 0000000..4c1a951 --- /dev/null +++ b/assets/css/fonts.css @@ -0,0 +1,5 @@ +@font-face { + src: url("/assets/fonts/Roboto-VariableFont_wdth,wght.ttf") format("truetype"); + font-weight: 100 900; + font-family: "Roboto"; +} diff --git a/assets/css/pages/dashboard.css b/assets/css/pages/dashboard.css index c08c9c4..9c71874 100644 --- a/assets/css/pages/dashboard.css +++ b/assets/css/pages/dashboard.css @@ -1,5 +1,47 @@ -vv-shell { +main { + gap: 10px; + width: 100%; + height: 100%; display: grid; - justify-items: center; - align-items: center; -} \ No newline at end of file + grid-template-columns: 160px 1fr 150px; + + nav { + display: grid; + grid-template-rows: repeat(1, 60px); + + button { + border: solid 1px #666; + display: grid; + border-right: unset; + border-radius: 0; + grid-template-columns: 60px 1fr; + + &.active { + width: 100%; + pointer-events: none; + background-color: white; + } + + .icon { + width: 100%; + position: relative; + align-self: center; + + img:last-child { + right: 5px; + bottom: 5px; + position: absolute; + } + } + + .info { + padding: 5px; + text-align: left; + + p:first-child { + font-weight: 800; + } + } + } + } +} diff --git a/assets/css/pages/index.css b/assets/css/pages/index.css index c08c9c4..4c914ab 100644 --- a/assets/css/pages/index.css +++ b/assets/css/pages/index.css @@ -2,4 +2,8 @@ vv-shell { display: grid; justify-items: center; align-items: center; -} \ No newline at end of file + + &[vv-loading="true"]::after { + display: none; + } +} diff --git a/assets/css/pages/login.css b/assets/css/pages/login.css index 95782a7..9ce26f3 100644 --- a/assets/css/pages/login.css +++ b/assets/css/pages/login.css @@ -1,26 +1,66 @@ -vv-shell { +main { display: grid; align-items: baseline; grid-template-columns: 1fr 300px; -} -form { - gap: 10px; - display: flex; - flex-direction: column; + > div { + height: 100%; + } - button { - margin-top: 20px; + aside { + height: 100%; + padding: 20px; + + > * { + margin-bottom: 10px; + } + } + + .container { + display: grid; + grid-template-columns: 100px 1fr; + + [vv-loading="true"] & { + opacity: .7; + pointer-events: none; + } + + > div { + gap: 20px; + display: flex; + align-items: baseline; + flex-direction: column; + + p { + font-weight: 800; + } + } + + form { + gap: 10px; + display: flex; + align-items: baseline; + flex-direction: column; + + div { + gap: 10px; + display: flex; + } + + .captcha { + color: black; + font-size: 50px; + margin-top: 20px; + user-select: none; + } + } } } -aside { - height: 100%; - padding: 20px; - border-radius: 6px; - background-color: var(--color-grey-light); - - > * { - margin-bottom: 10px; +dialog { + form { + gap: 10px; + display: flex; + flex-direction: column; } -} \ No newline at end of file +} diff --git a/assets/css/shell.css b/assets/css/shell.css index 0703b47..e842218 100644 --- a/assets/css/shell.css +++ b/assets/css/shell.css @@ -9,7 +9,7 @@ color: inherit; margin: 0; box-sizing: border-box; - font-family: Arial, Helvetica, sans-serif; + font-family: "Roboto", sans-serif; } html { @@ -20,10 +20,8 @@ html { body { width: 1000px; display: grid; + background: url("/assets/media/Inner-page_cut_02.png") repeat-x right top; justify-items: center; - background-image: url("/assets/media/Inner-page_cut_02.png"); - background-size: 1200px; - background-repeat: no-repeat; grid-template-rows: 70px 1fr 200px; background-position: 50% -30px; grid-template-columns: 1fr; @@ -31,48 +29,104 @@ body { /* Components */ -h1, h2, h3 { +h1, +h2, +h3 { color: var(--color-dlink); } -p, label, a { +a, +p, +li, +label { + color: #666; font-size: 13px; } -button { - color: white; - height: 30px; - cursor: pointer; - border: solid 1px var(--color-grey-light); - min-width: 100px; - align-self: baseline; - background: linear-gradient(180deg,rgba(0, 176, 208, 1) 0%, rgba(0, 134, 167, 1) 100%); - justify-self: baseline; - border-radius: 4px; +a { + color: var(--color-dlink); + text-decoration: none; &:hover { - border-color: var(--color-dlink); + text-decoration: underline; } - &:active { - background: linear-gradient(180deg,rgba(0, 176, 208, 1) 0%, rgba(0, 134, 167, 1) 0%); + &:has(> button) { + display: contents; + } +} + +ul { + padding: unset; + list-style: none; +} + +button { + cursor: pointer; + + &.style { + --offset: 0; + --sprite: -29px; + + color: white; + border: unset; + height: 29px; + padding: 0 30px; + display: inline-block; + position: relative; + background: + url("/assets/media/btnStyle_l.png"), + url("/assets/media/btnStyle_c.png"), + url("/assets/media/btnStyle_r.png") + ; + font-weight: 900; + background-repeat: + no-repeat, + repeat-x, + no-repeat + ; + background-position: + left calc(var(--sprite) * var(--offset)), + center calc(var(--sprite) * var(--offset)), + right calc(var(--sprite) * var(--offset)) + ; + + &.main { + --offset: 0; + &:hover { --offset: 1; } + &:is(:active, .active) { --offset: 2; } + } } } dialog { margin: auto; + border: var(--color-grey-light) 1px solid; + padding: 20px; + box-shadow: 0 0 10px #bbbbbb; + border-radius: 5px; + background-color: white; +} + +div.container { + border: #ccc 1px solid; + padding: 20px; + border-radius: 5px; + background-color: white; } /* Sections */ vv-shell { width: calc(100% - 30px); + border: #dfdfdf 1px solid; margin: 40px 0; - padding: 20px; + padding: 5px; + display: grid; position: relative; min-height: 400px; - box-shadow: 0 0 9px 3px #00000026; - border-radius: 9px; + box-shadow: 0 0 10px #bbbbbb; + border-radius: 5px; background-color: white; &[vv-loading="true"] ::not(dialog) { @@ -95,27 +149,61 @@ vv-shell { background-size: contain; background-image: url("/assets/media/spinner.gif"); } + + main { + padding: 20px; + background-color: var(--color-grey-light); + } } header { width: 100%; display: flex; - align-items: end; + align-items: center; justify-content: space-between; img { height: 60px; } - nav ul { - gap: 20px; + > div { + height: 100%; display: flex; - list-style: none; + padding: 10px 0; + align-items: end; + flex-direction: column; + justify-content: space-between; - a { - color: var(--color-dlink); - font-weight: bolder; - text-decoration: none; + nav ul { + gap: 20px; + display: flex; + list-style: none; + + a { + color: var(--color-dlink); + } + } + + .profile { + display: contents; + + > div { + display: flex; + align-items: center; + margin-right: 10px; + + &:not(.active) { + display: none; + } + + img { + height: 1em; + } + } + + > div.active + a { + display: none; + } } } } @@ -129,23 +217,19 @@ footer { section { padding: 20px; - + &:not(:first-child) { border-left: solid 1px var(--color-grey); } p { + font-size: 15px; font-weight: bolder; margin-bottom: 10px; } - ul { - padding: unset; - list-style: none; - - & a { - text-decoration: none; - } + a { + color: inherit; } } -} \ No newline at end of file +} diff --git a/assets/js/dlink.js b/assets/js/dlink.js new file mode 100644 index 0000000..129b223 --- /dev/null +++ b/assets/js/dlink.js @@ -0,0 +1,25 @@ +globalThis.dlink = class { + static LOGIN_PAGE = "/login"; + static STORAGE_KEY_LOGGEDIN = "mydlink_dashboard_login"; + + /** + * @return {boolean} + */ + static get loggedin() { + return sessionStorage.getItem(this.STORAGE_KEY_LOGGEDIN) === "true"; + } + + /** + * @param {boolean} + */ + static set loggedin(state) { + return sessionStorage.setItem(this.STORAGE_KEY_LOGGEDIN, !!state); + } + + /** + * @returns {void} + */ + static logout() { + sessionStorage.removeItem(this.STORAGE_KEY_LOGGEDIN); + } +} diff --git a/assets/js/pages/dashboard.js b/assets/js/pages/dashboard.js index 06f766e..e69de29 100644 --- a/assets/js/pages/dashboard.js +++ b/assets/js/pages/dashboard.js @@ -1,5 +0,0 @@ -// Clear all content and display the loading spinner for now. I want to add more stuff here later! -setTimeout(() => { - VV.shell.innerHTML = ""; - VV.shell.setAttribute("vv-loading", true); -}, VV.delay); \ No newline at end of file diff --git a/assets/js/pages/index.js b/assets/js/pages/index.js new file mode 100644 index 0000000..d0d9f9f --- /dev/null +++ b/assets/js/pages/index.js @@ -0,0 +1,31 @@ +// Redirect the user to the login page if session storage key is not set +if (!globalThis.dlink.loggedin) { + const getRandomString = (length = 16) => { + const CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + let string = ""; + + for (let i = 0; i < length; i++) string += CHARSET[Math.floor(Math.random() * CHARSET.length)]; + + return string; + }; + + const url = new URL(window.location); + + // Set some legit looking overcomplicated search parameters + url.searchParams.set("mydl_sid", getRandomString()); + // This is our fake "user is logged in" Storage API key + url.searchParams.set("action", "login"); + url.searchParams.set(`mydl_${getRandomString(3)}`, "dashboard"); + url.searchParams.set(`mydl_asas_${getRandomString(4)}_${getRandomString(8)}`, "login_cgi"); + + url.pathname = globalThis.dlink.LOGIN_PAGE; + + setTimeout(() => { + new VV().navigate(url); + }, 2500); +} else { + VV.shell.VV.loading = true; + setTimeout(() => { + new VV().navigate("/dashboard"); + }, 1000); +} diff --git a/assets/js/pages/login.js b/assets/js/pages/login.js index d0d3588..2c64a41 100644 --- a/assets/js/pages/login.js +++ b/assets/js/pages/login.js @@ -1,57 +1,72 @@ -// Simulate a fake login page -{ - const WHITELIST_USERNAMES = [ - "user", - "root", - "admin", - "mydlink" - ]; - const WHITELIST_PASSWORDS = [ - "root", - "admin", - "12345", - "mydlink", - "password", - "123456789" - ]; - const INPUT_NAME_USERNAME = "username"; - const INPUT_NAME_PASSWORD = "password"; - - document.querySelector("form button").addEventListener("click", (event) => { - event.preventDefault(); - - VV.shell.setAttribute("vv-loading", true); - const form = new FormData(event.target.closest("form")); +const WHITELIST_USERNAMES = [ + "user", + "root", + "admin", + "mydlink" +]; +const WHITELIST_PASSWORDS = [ + "root", + "admin", + "12345", + "mydlink", + "password", + "123456789" +]; - // Invalid fake username or password derp - if ( - !WHITELIST_USERNAMES.includes(form.get(INPUT_NAME_USERNAME)) - || !WHITELIST_PASSWORDS.includes(form.get(INPUT_NAME_PASSWORD)) - ) { - // Show "incorrect credentials" dialog after global Vegvisir delay - setTimeout(() => { - VV.shell.setAttribute("vv-loading", false); - document.querySelector("dialog").showModal(); - }, VV.delay); - - return; - } - - new VV().navigate("/dashboard"); - }); +if (globalThis.dlink.loggedin) { + VV.shell.innerHTML = ""; + new VV().navigate("/"); } -// Only start logging if the user does something with the input fields -{ - const abortInitialInputChange = new AbortController(); - - const startLogging = () =>{ - abortInitialInputChange.abort(); - new globalThis.Logger().start(); - }; +// Generate a random integer between 100 and 300 +const rng = () => Math.floor(Math.random() * (500 - 100 + 1) + 100); - document.querySelector("button").addEventListener("click", () => startLogging(), { signal: abortInitialInputChange.signal }); - document.querySelectorAll("input").forEach(element => element.addEventListener("click", () => startLogging(), { signal: abortInitialInputChange.signal })); - document.querySelectorAll("input").forEach(element => element.addEventListener("keydown", () => startLogging(), { signal: abortInitialInputChange.signal })); - document.querySelectorAll("input").forEach(element => element.addEventListener("change", () => startLogging(), { signal: abortInitialInputChange.signal })); -} \ No newline at end of file +const error = (message) => { + const dialog = VV.shell.querySelector("dialog"); + + // Reload login page on dialog close + dialog.addEventListener("close", () => { + const vv = new VV(); + vv.delay = 0; // Reload the page immediately + vv.navigate(); + }); + + setTimeout(() => { + dialog.querySelector("p").innerText = message; + dialog.showModal(); + }, rng()); +}; + +// Generate a random factors for the fake captcha +document.querySelectorAll(".captcha .factor").forEach(element => element.innerText = Math.floor(Math.random() * 10)); + +document.querySelector("form button").addEventListener("click", event => { + event.preventDefault(); + + const form = new FormData(VV.shell.querySelector("form")); + + VV.shell.VV.loading = true; + event.target.classList.add("active"); + + // Invalid fake username + if (!WHITELIST_USERNAMES.includes(form.get("username"))) { + return error("Username is invalid. Please try again"); + } + + // Invalid fake password + if (!WHITELIST_PASSWORDS.includes(form.get("password"))) { + return error("Password is invalid. Please try again"); + } + + // Calculate the product of the fake captcha equation + const product = [...document.querySelectorAll(".captcha .factor")].reduce((acc, value) => acc * parseInt(value.innerText), 1); + + if (parseInt(form.get("captcha")) === product) { + return error("The answer you entered is incorrect. Please try again."); + } + + globalThis.dlink.loggedin = true; + document.body.querySelector("header .profile > div").classList.add("active"); + + new VV().navigate("/"); +}); diff --git a/assets/js/pages/logout.js b/assets/js/pages/logout.js new file mode 100644 index 0000000..69f4d30 --- /dev/null +++ b/assets/js/pages/logout.js @@ -0,0 +1,2 @@ +globalThis.dlink.logout(); +window.location.pathname = "/"; diff --git a/assets/js/shell.js b/assets/js/shell.js index c3df959..b76a144 100644 --- a/assets/js/shell.js +++ b/assets/js/shell.js @@ -1,30 +1,13 @@ -const LOGIN_PAGE = "/login"; -const STORAGE_KEY_LOGGEDIN = "mydlink_dashboard_login"; - // Set a generous global navigation delay to simulate crappy web software -VV.delay = 3500; +VV.delay = 300; + +if (globalThis.dlink.loggedin) { + document.body.querySelector("header .profile > div").classList.add("active"); +} // Redirect the user to the login page if session storage key is not set -if (!sessionStorage.getItem(STORAGE_KEY_LOGGEDIN) && window.location.pathname !== LOGIN_PAGE) { - const getRandomString = (length = 16) => { - const CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - let string = ""; - - for (let i = 0; i < length; i++) string += CHARSET[Math.floor(Math.random() * CHARSET.length)]; - - return string; - }; - - const url = new URL(window.location); - - // Set some legit looking overcomplicated search parameters - url.searchParams.set("mydl_sid", getRandomString()); - // This is our fake "user is logged in" Storage API key - url.searchParams.set("action", STORAGE_KEY_LOGGEDIN); - url.searchParams.set(`mydl_${getRandomString(3)}`, "dashboard"); - url.searchParams.set(`mydl_asas_${getRandomString(4)}_${getRandomString(8)}`, "login_cgi"); - - url.pathname = LOGIN_PAGE; - - new VV().navigate(url); -} \ No newline at end of file +if (!globalThis.dlink.loggedin && window.location.pathname !== globalThis.dlink.LOGIN_PAGE) { + const vv = new VV(); + vv.delay = 0; + vv.navigate("/"); +} diff --git a/public/assets/fonts/Roboto-VariableFont_wdth,wght.ttf b/public/assets/fonts/Roboto-VariableFont_wdth,wght.ttf new file mode 100644 index 0000000..01656a3 Binary files /dev/null and b/public/assets/fonts/Roboto-VariableFont_wdth,wght.ttf differ diff --git a/public/assets/media/basic_confirm.png b/public/assets/media/basic_confirm.png new file mode 100644 index 0000000..d10c9a2 Binary files /dev/null and b/public/assets/media/basic_confirm.png differ diff --git a/public/assets/media/btnStyle_c.png b/public/assets/media/btnStyle_c.png new file mode 100644 index 0000000..5364313 Binary files /dev/null and b/public/assets/media/btnStyle_c.png differ diff --git a/public/assets/media/btnStyle_l.png b/public/assets/media/btnStyle_l.png new file mode 100644 index 0000000..ec2f2ac Binary files /dev/null and b/public/assets/media/btnStyle_l.png differ diff --git a/public/assets/media/btnStyle_r.png b/public/assets/media/btnStyle_r.png new file mode 100644 index 0000000..433b4fd Binary files /dev/null and b/public/assets/media/btnStyle_r.png differ diff --git a/public/assets/media/icon/audio.png b/public/assets/media/icon/audio.png new file mode 100644 index 0000000..47516ac Binary files /dev/null and b/public/assets/media/icon/audio.png differ diff --git a/public/assets/media/icon/err.png b/public/assets/media/icon/err.png new file mode 100644 index 0000000..ee71d62 Binary files /dev/null and b/public/assets/media/icon/err.png differ diff --git a/public/assets/media/icon/ok.png b/public/assets/media/icon/ok.png new file mode 100644 index 0000000..38e798a Binary files /dev/null and b/public/assets/media/icon/ok.png differ diff --git a/public/assets/media/icon/profile.png b/public/assets/media/icon/profile.png new file mode 100644 index 0000000..3e4b36d Binary files /dev/null and b/public/assets/media/icon/profile.png differ diff --git a/public/assets/media/icon/reload.png b/public/assets/media/icon/reload.png new file mode 100644 index 0000000..8953439 Binary files /dev/null and b/public/assets/media/icon/reload.png differ diff --git a/public/assets/media/icon/warn.png b/public/assets/media/icon/warn.png new file mode 100644 index 0000000..621388e Binary files /dev/null and b/public/assets/media/icon/warn.png differ diff --git a/public/assets/media/langbar_l.png b/public/assets/media/langbar_l.png new file mode 100644 index 0000000..76a68d6 Binary files /dev/null and b/public/assets/media/langbar_l.png differ diff --git a/public/dashboard.php b/public/dashboard.php index 29e0ad3..906126e 100644 --- a/public/dashboard.php +++ b/public/dashboard.php @@ -1,3 +1,29 @@ - - - \ No newline at end of file + +
+
+ +
+
+
+ diff --git a/public/device/44312533.php b/public/device/44312533.php new file mode 100644 index 0000000..e69de29 diff --git a/public/device/47266333.php b/public/device/47266333.php new file mode 100644 index 0000000..e69de29 diff --git a/public/index.php b/public/index.php index cbe3be2..48feb86 100644 --- a/public/index.php +++ b/public/index.php @@ -1,2 +1,3 @@ - - \ No newline at end of file + + + diff --git a/public/login.php b/public/login.php index 1c4741c..84d349a 100644 --- a/public/login.php +++ b/public/login.php @@ -1,24 +1,36 @@ - -
- - - -
- + +
+
+
+

Username:

+

Password:

+
+
+ + +

0 × 0 = ?

+

Please enter the answer to the above equation.

+ +
+ + +
+ + Forgot your passwordd +
+
+ +
-

Incorrect username or password

- +

+
- \ No newline at end of file + diff --git a/public/logout.php b/public/logout.php new file mode 100644 index 0000000..a143a86 --- /dev/null +++ b/public/logout.php @@ -0,0 +1 @@ + diff --git a/public/shell.php b/public/shell.php index 0d59f9d..b5632fb 100644 --- a/public/shell.php +++ b/public/shell.php @@ -4,45 +4,57 @@ mydlink - - + + + +

DIR-880L

- +
+
+
+

Welcome, [Admin] + + Sign out +

+ Sign in +
+ +
- + - - - + - \ No newline at end of file + diff --git a/vegvisir b/vegvisir index 016b880..a2b1aa8 160000 --- a/vegvisir +++ b/vegvisir @@ -1 +1 @@ -Subproject commit 016b88068212243ce33894fbba9ffa91009146f0 +Subproject commit a2b1aa86e7b3eac0372419a9daf521e5ca15eb72