From 5bbda2ab816a4394b12cc622df8d151bb5750a9d Mon Sep 17 00:00:00 2001 From: adjoly Date: Thu, 23 Oct 2025 13:20:13 +0200 Subject: [PATCH 1/6] =?UTF-8?q?=E3=80=8C=E2=9C=A8=E3=80=8D=20feat:=20added?= =?UTF-8?q?=202fa=20to=20login=20page=20:D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/front/static/ts/views/LoginPage.ts | 97 +++++++++++++++++++++++++- src/front/static/ts/views/Profile.ts | 1 + 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/src/front/static/ts/views/LoginPage.ts b/src/front/static/ts/views/LoginPage.ts index 0ed05c1..a7159cb 100644 --- a/src/front/static/ts/views/LoginPage.ts +++ b/src/front/static/ts/views/LoginPage.ts @@ -53,7 +53,40 @@ export default class extends Aview { } async run() { - dragElement(document.getElementById("window")); + dragElement(document.getElementById("window")); + + const totpVerify = async () => { + const username = (document.getElementById("username") as HTMLInputElement).value; + const password = (document.getElementById("password") as HTMLInputElement).value; + const totpPin = (document.getElementById('totpPin') as HTMLInputElement).value; + try { + const data_req = await fetch("http://localhost:3001/login", { + method: "POST", + headers: { "Content-Type": "application/json", }, + credentials: "include", + body: JSON.stringify({ user: username, password: password, token: totpPin }), + }); + if (data_req.status === 200) { + isLogged(); + navigationManager("/"); + } else if (data_req.status === 401) { + const data = await data_req.json(); + + const error = document.createElement("p"); + error.innerHTML = data.error; + error.classList.add("text-red-700", "dark:text-red-500"); + + idWindow.appendChild(error); + } else { + console.log(data_req.status) + console.log(await data_req.json()) + // throw new Error("invalid response"); + } + } catch (error) { + console.error(error); + } + } + const login = async () => { const username = (document.getElementById("username") as HTMLInputElement).value; const password = (document.getElementById("password") as HTMLInputElement).value; @@ -71,11 +104,69 @@ export default class extends Aview { isLogged(); navigationManager("/"); } + else if (data_req.status === 402) { + const popup: HTMLDivElement = document.createElement("div"); + popup.id = "2fa-popup"; + popup.classList.add("z-10", "absolute", "default-border"); + const header = popup.appendChild(document.createElement("div"));; + header.classList.add("bg-linear-to-r", "from-orange-200", "to-orange-300", "flex", "flex-row", "min-w-35", "justify-between", "px-2"); + header.id = "2fa-header"; + header.appendChild(document.createElement("span")).innerText = "2fa.ts"; + const btn = header.appendChild(document.createElement("button")); + btn.innerText = " × "; + btn.onclick = () => { document.getElementById("2fa-popup").remove(); }; + + const popup_content: HTMLSpanElement = popup.appendChild(document.createElement("div")); + popup_content.id = "2fa-popup-content"; + popup_content.classList.add("flex", "flex-col", "bg-neutral-200", "dark:bg-neutral-800", "p-6", "pt-4", "text-neutral-900", "dark:text-white", "space-y-4"); + + const tokenInput = document.createElement("input"); + tokenInput.type = "tel"; + tokenInput.id = "totpPin"; + tokenInput.name = "totpPin"; + tokenInput.placeholder = "TOTP code"; + tokenInput.required = true; + tokenInput.autocomplete = "off"; + tokenInput.pattern = "[0-9]*"; + tokenInput.setAttribute("inputmode", "numeric"); + tokenInput.classList.add("bg-white", "text-neutral-900","w-full", "px-4", "py-2", "input-border"); + + const tokenSubmit = document.createElement("button"); + tokenSubmit.type = "submit"; + tokenSubmit.classList.add("default-button", "w-full"); + tokenSubmit.id = "totp-submit"; + tokenSubmit.innerHTML = "submit"; + + const tokenTitle = document.createElement("h1"); + tokenTitle.innerHTML = `hey ${username}, please submit your 2fa code below :`; + tokenTitle.classList.add("text-gray-900", "dark_text-white", "text-lg", "pt-0", "pb-4", "justify-center"); + + const form = document.createElement("form"); + form.method = "dialog"; + form.classList.add("space-y-4"); + form.appendChild(tokenTitle); + form.appendChild(tokenInput); + form.appendChild(tokenSubmit); + + popup_content.appendChild(form); + + const uu = document.getElementById("username") as HTMLInputElement; + const pass = document.getElementById("password") as HTMLInputElement; + + uu.disabled = true; + pass.disabled = true; + + document.getElementById("app")?.appendChild(popup); + tokenInput.focus(); + dragElement(document.getElementById("2fa-popup")); + + document.getElementById("totp-submit")?.addEventListener("click", totpVerify); + } else if (data_req.status === 400) { const data = await data_req.json(); - document.getElementById("login-error-message").innerHTML = "error: " + data.error; - document.getElementById("login-error-message").classList.remove("hidden"); + document.getElementById("login-error-message").innerHTML = "error: " + data.error; + document.getElementById("login-error-message").classList.remove("hidden"); } else { diff --git a/src/front/static/ts/views/Profile.ts b/src/front/static/ts/views/Profile.ts index b80bd0b..fda349f 100644 --- a/src/front/static/ts/views/Profile.ts +++ b/src/front/static/ts/views/Profile.ts @@ -127,6 +127,7 @@ export default class extends Aview { if (!main) return console.error("what"); + // don't read this shit for you mental health if (matches.matchHistory) { for (let match of matches.matchHistory) { const newEntry = document.createElement("li"); From 8da2193d9ef786974bf78ecb0f3c575f37752777 Mon Sep 17 00:00:00 2001 From: adjoly Date: Thu, 23 Oct 2025 14:15:54 +0200 Subject: [PATCH 2/6] =?UTF-8?q?=E3=80=8C=F0=9F=94=A8=E3=80=8D=20fix:=20wor?= =?UTF-8?q?king=20error=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/front/static/ts/views/LoginPage.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/front/static/ts/views/LoginPage.ts b/src/front/static/ts/views/LoginPage.ts index a7159cb..da0deec 100644 --- a/src/front/static/ts/views/LoginPage.ts +++ b/src/front/static/ts/views/LoginPage.ts @@ -59,6 +59,7 @@ export default class extends Aview { const username = (document.getElementById("username") as HTMLInputElement).value; const password = (document.getElementById("password") as HTMLInputElement).value; const totpPin = (document.getElementById('totpPin') as HTMLInputElement).value; + const idWindow = (document.getElementById('2fa-popup-content') as HTMLInputElement); try { const data_req = await fetch("http://localhost:3001/login", { method: "POST", From 182452e931c915418057fdf1092fc94037f087c9 Mon Sep 17 00:00:00 2001 From: adjoly Date: Thu, 23 Oct 2025 14:16:31 +0200 Subject: [PATCH 3/6] =?UTF-8?q?=E3=80=8C=F0=9F=94=A8=E3=80=8D=20fix:=20now?= =?UTF-8?q?=20a=20good=20wallpaper=20for=20whilte=20theme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/front/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/front/index.html b/src/front/index.html index 8532da2..ee61724 100644 --- a/src/front/index.html +++ b/src/front/index.html @@ -12,7 +12,7 @@ - + From 494612a160007adb525e232c0d769c2c924f5b1e Mon Sep 17 00:00:00 2001 From: adjoly Date: Thu, 23 Oct 2025 14:54:53 +0200 Subject: [PATCH 4/6] =?UTF-8?q?=E3=80=8C=F0=9F=8F=97=EF=B8=8F=E3=80=8D=20w?= =?UTF-8?q?ip:=20started=20adding=20enable=202fa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/front/static/ts/views/Settings.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/front/static/ts/views/Settings.ts b/src/front/static/ts/views/Settings.ts index 5c70167..94e6500 100644 --- a/src/front/static/ts/views/Settings.ts +++ b/src/front/static/ts/views/Settings.ts @@ -28,6 +28,8 @@ export default class extends Aview { +
+ `; @@ -80,5 +82,9 @@ export default class extends Aview { else console.error("xd"); // xd????????????? }); + + document.getElementById("2fa-button")?.addEventListener("click", async () => { + + }); } } From b290915249ca5257ab6103bd28baab43e70d39c6 Mon Sep 17 00:00:00 2001 From: adjoly Date: Thu, 23 Oct 2025 19:31:55 +0200 Subject: [PATCH 5/6] =?UTF-8?q?=E3=80=8C=F0=9F=94=A8=E3=80=8D=20fix:=20fix?= =?UTF-8?q?ed=20some=20things.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/front/static/ts/views/LoginPage.ts | 16 ++- src/front/static/ts/views/Settings.ts | 145 ++++++++++++++++-------- src/front/static/ts/views/TotpEnable.ts | 110 ++++++++++++++++++ 3 files changed, 217 insertions(+), 54 deletions(-) create mode 100644 src/front/static/ts/views/TotpEnable.ts diff --git a/src/front/static/ts/views/LoginPage.ts b/src/front/static/ts/views/LoginPage.ts index da0deec..97e6ed0 100644 --- a/src/front/static/ts/views/LoginPage.ts +++ b/src/front/static/ts/views/LoginPage.ts @@ -73,11 +73,17 @@ export default class extends Aview { } else if (data_req.status === 401) { const data = await data_req.json(); - const error = document.createElement("p"); - error.innerHTML = data.error; - error.classList.add("text-red-700", "dark:text-red-500"); + if (!document.getElementById("error-totp")) { + const error = document.createElement("p"); + error.innerHTML = data.error; + error.classList.add("text-red-700", "dark:text-red-500"); + + idWindow.appendChild(error); + } else { + const error = document.getElementById("error-totp") as HTMLParagraphElement; + error.innerHTML = data.error; + } - idWindow.appendChild(error); } else { console.log(data_req.status) console.log(await data_req.json()) @@ -140,7 +146,7 @@ export default class extends Aview { const tokenTitle = document.createElement("h1"); tokenTitle.innerHTML = `hey ${username}, please submit your 2fa code below :`; - tokenTitle.classList.add("text-gray-900", "dark_text-white", "text-lg", "pt-0", "pb-4", "justify-center"); + tokenTitle.classList.add("text-gray-900", "dark:text-white", "text-lg", "pt-0", "pb-4", "justify-center"); const form = document.createElement("form"); form.method = "dialog"; diff --git a/src/front/static/ts/views/Settings.ts b/src/front/static/ts/views/Settings.ts index 94e6500..021b215 100644 --- a/src/front/static/ts/views/Settings.ts +++ b/src/front/static/ts/views/Settings.ts @@ -2,12 +2,12 @@ import Aview from "./Aview.ts" import { dragElement } from "./drag.ts"; import { setOnekoState } from "../oneko.ts" import { isLogged, navigationManager } from "../main.ts" - +import { totpEnablePopup } from "./TotpEnable.ts"; +import { totpVerify } from "../../../../api/auth/totpVerify.js"; export default class extends Aview { - constructor() - { + constructor() { super(); this.setTitle("profile"); setOnekoState("default"); @@ -29,62 +29,109 @@ export default class extends Aview {
- + `; } - async run() { - if (!await isLogged()) - navigationManager("/"); + async run() { + if (!await isLogged()) + navigationManager("/"); - dragElement(document.getElementById("window")); + dragElement(document.getElementById("window")); - let uuid: String; - uuid = document.cookie.match(new RegExp('(^| )' + "uuid" + '=([^;]+)'))[2]; - const userdata_req = await fetch(`http://localhost:3002/users/${uuid}`, { - method: "GET", - credentials: "include", - }); - if (userdata_req.status == 404) { - console.error("invalid user"); - return; - } - let userdata = await userdata_req.json(); + const isTOTPEnabled = async () => { + const totpVerify_req = await fetch('http://localhost:3001/2fa', { + method: "GET", + credentials: "include" + }) - (document.getElementById("displayName-input") as HTMLInputElement).placeholder = userdata.displayName; - (document.getElementById("displayName-input") as HTMLInputElement).value = userdata.displayName; + if (totpVerify_req.status === 200) { + const totpVerify_data = await totpVerify_req.json(); + if (totpVerify_data.totp == true) { + return true; + } + } + return false; + }; - document.getElementById("displayName-button")?.addEventListener("click", async () => { - const changeDisplayName_req = await fetch(`http://localhost:3002/users/${uuid}/displayName`, { - method: "PATCH", - headers: { "Content-Type": "application/json", }, - credentials: "include", - body: JSON.stringify({ displayName: (document.getElementById("displayName-input") as HTMLInputElement).value }) - }); - if (changeDisplayName_req.status == 200) { - // idk display success - } - else { - // display error ig, uuuh it's in await changeDisplayName.json().error - } - }); + let uuid: String; + uuid = document.cookie.match(new RegExp('(^| )' + "uuid" + '=([^;]+)'))[2]; + const userdata_req = await fetch(`http://localhost:3002/users/${uuid}`, { + method: "GET", + credentials: "include", + }); + if (userdata_req.status == 404) { + console.error("invalid user"); + return; + } + let userdata = await userdata_req.json(); - document.getElementById("deleteAccount-button")?.addEventListener("click", async () => { - const delete_req = await fetch(`http://localhost:3001/`, { - method: "DELETE", - credentials: "include", - }); + (document.getElementById("displayName-input") as HTMLInputElement).placeholder = userdata.displayName; + (document.getElementById("displayName-input") as HTMLInputElement).value = userdata.displayName; - if (delete_req.status == 200) - navigationManager("/"); - else - console.error("xd"); // xd????????????? - }); + document.getElementById("displayName-button")?.addEventListener("click", async () => { + const changeDisplayName_req = await fetch(`http://localhost:3002/users/${uuid}/displayName`, { + method: "PATCH", + headers: { "Content-Type": "application/json", }, + credentials: "include", + body: JSON.stringify({ displayName: (document.getElementById("displayName-input") as HTMLInputElement).value }) + }); + if (changeDisplayName_req.status == 200) { + // idk display success + } + else { + // display error ig, uuuh it's in await changeDisplayName.json().error + } + }); - document.getElementById("2fa-button")?.addEventListener("click", async () => { - - }); - } + document.getElementById("deleteAccount-button")?.addEventListener("click", async () => { + const delete_req = await fetch(`http://localhost:3001/`, { + method: "DELETE", + credentials: "include", + }); + + if (delete_req.status == 200) + navigationManager("/"); + else + console.error("xd"); // xd????????????? + }); + + + const totpButton = document.getElementById("2fa-button") as HTMLButtonElement; + + if ((await isTOTPEnabled()) === true) { + totpButton.innerHTML = "disable 2fa"; + + document.getElementById("2fa-button")?.addEventListener("click", async () => { + const totp_req = await fetch(`http://localhost:3001/2fa`, { + method: "DELETE", + credentials: "include" + }) + if (totp_req.status === 200) { + console.log("working") + navigationManager("/settings") + } else { + console.log("wut") + } + }); + } else { + totpButton.innerHTML = "enable 2fa"; + + document.getElementById("2fa-button")?.addEventListener("click", async () => { + const totp_req = await fetch(`http://localhost:3001/2fa`, { + method: "POST", + credentials: "include" + }) + if (totp_req.status === 200) { + console.log("working") + const totp_data = await totp_req.json(); + totpEnablePopup(uuid, totp_data.secret, totp_data.otpauthUrl); + } else { + console.log("wut") + } + }); + } + } } diff --git a/src/front/static/ts/views/TotpEnable.ts b/src/front/static/ts/views/TotpEnable.ts new file mode 100644 index 0000000..89488c1 --- /dev/null +++ b/src/front/static/ts/views/TotpEnable.ts @@ -0,0 +1,110 @@ +import { navigationManager } from "../main.ts"; +import { dragElement } from "./drag.ts"; + +async function totpVerify() { + const code = (document.getElementById("totpPin") as HTMLInputElement).value; + const data_req = await fetch('http://localhost:3001/2fa/verify', { + method: "POST", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + token: code + }) + }) + + if (data_req.status === 200) { + navigationManager("/settings"); + } else if (data_req.status === 401 || data_req.status === 400) { + const popup_content = document.getElementById("2fa-enable-content"); + + if (!document.getElementById("error-totp")) { + const error = document.createElement("p"); + error.id = "error-totp"; + error.classList.add("text-red-700", "dark:text-red-500"); + error.innerHTML = (await data_req.json()).error; + + popup_content?.appendChild(error) + } else { + const error = document.getElementById("error-totp") as HTMLParagraphElement; + error.innerHTML = (await data_req.json()).error; + } + } else { + console.log("Unexpected error") + } +} + +export async function totpEnablePopup(username: String, secret: String, url: String) { + const popup: HTMLDivElement = document.createElement("div"); + popup.id = "2fa-enable-popup"; + popup.classList.add("z-10", "absolute", "default-border"); + const header = popup.appendChild(document.createElement("div"));; + header.classList.add("bg-linear-to-r", "from-orange-200", "to-orange-300", "flex", "flex-row", "min-w-35", "justify-between", "px-2"); + header.id = "2fa-enable-popup-header"; + header.appendChild(document.createElement("span")).innerText = "2fa_enable.ts"; + const btn = header.appendChild(document.createElement("button")); + btn.innerText = " × "; + btn.onclick = () => { document.getElementById("2fa-enable-popup")?.remove(); }; + + const popup_content: HTMLSpanElement = popup.appendChild(document.createElement("div")); + popup_content.id = "2fa-enable-content"; + popup_content.classList.add("flex", "flex-col", "bg-neutral-200", "dark:bg-neutral-800", "p-6", "pt-4", "text-neutral-900", "dark:text-white", "space-y-4"); + + const qrDivTOTP = document.createElement("div"); + qrDivTOTP.classList.add("flex", "justify-center"); + + const qrCodeTOTP = document.createElement("img"); + qrCodeTOTP.id = "qrCodeTOTP"; + qrCodeTOTP.src = `https://api.qrserver.com/v1/create-qr-code/?margin=10&size=512x512&data=${url}`; + qrCodeTOTP.classList.add("w-60"); + qrDivTOTP.appendChild(qrCodeTOTP); + + const secretText = document.createElement("p"); + secretText.innerHTML = `key:
${secret}
`; + secretText.classList.add("text-center") + + const tokenInput = document.createElement("input"); + tokenInput.type = "tel"; + tokenInput.id = "totpPin"; + tokenInput.name = "totpPin"; + tokenInput.placeholder = "TOTP code"; + tokenInput.required = true; + tokenInput.autocomplete = "off"; + tokenInput.pattern = "[0-9]*"; + tokenInput.setAttribute("inputmode", "numeric"); + tokenInput.classList.add("bg-white", "text-neutral-900", "w-full", "px-4", "py-2", "input-border"); + + const tokenSubmit = document.createElement("button"); + tokenSubmit.type = "submit"; + tokenSubmit.classList.add("default-button", "w-full"); + tokenSubmit.id = "totp-submit"; + tokenSubmit.innerHTML = "submit"; + + const hr = document.createElement("hr"); + hr.classList.add("my-2", "w-full", "reverse-border"); + + const t = document.createElement("h2"); + t.innerHTML = "hey " + username + + ` you are trying to add 2fa
+ just add the following to your app and enter the code bellow ↓ + `; + t.classList.add("text-center") + + document.getElementById("app")?.appendChild(popup); + + const form = document.createElement("form"); + form.method = "dialog"; + form.classList.add("space-y-4"); + form.appendChild(tokenInput); + form.appendChild(tokenSubmit); + + popup_content.appendChild(t) + popup_content.appendChild(qrDivTOTP); + popup_content.appendChild(secretText); + popup_content.appendChild(hr) + popup_content.appendChild(form); + dragElement(document.getElementById("2fa-enable-popup")); + + document.getElementById("totp-submit")?.addEventListener("click", totpVerify) +} From bfaed233651ca4c393b8b4c65321dbe95d89a971 Mon Sep 17 00:00:00 2001 From: adjoly Date: Thu, 23 Oct 2025 19:41:35 +0200 Subject: [PATCH 6/6] =?UTF-8?q?=E3=80=8C=F0=9F=94=A8=E3=80=8D=20fix:=20cen?= =?UTF-8?q?tered=20the=20error=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/front/static/ts/views/TotpEnable.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/front/static/ts/views/TotpEnable.ts b/src/front/static/ts/views/TotpEnable.ts index 89488c1..baf4fd9 100644 --- a/src/front/static/ts/views/TotpEnable.ts +++ b/src/front/static/ts/views/TotpEnable.ts @@ -22,7 +22,7 @@ async function totpVerify() { if (!document.getElementById("error-totp")) { const error = document.createElement("p"); error.id = "error-totp"; - error.classList.add("text-red-700", "dark:text-red-500"); + error.classList.add("text-red-700", "dark:text-red-500", "text-center"); error.innerHTML = (await data_req.json()).error; popup_content?.appendChild(error)