feat(frontend): added returnTo to login and signin page
This commit is contained in:
parent
033d399fcb
commit
b1d4f68453
14 changed files with 352 additions and 1460 deletions
|
|
@ -15,7 +15,6 @@
|
||||||
"vite-tsconfig-paths": "^5.1.4"
|
"vite-tsconfig-paths": "^5.1.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@openapitools/openapi-generator-cli": "^2.25.0",
|
|
||||||
"@tailwindcss/vite": "^4.1.17",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"openapi-fetch": "^0.15.0",
|
"openapi-fetch": "^0.15.0",
|
||||||
|
|
|
||||||
1376
frontend/pnpm-lock.yaml
generated
1376
frontend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -2,6 +2,7 @@ import { setTitle, handleRoute } from '@app/routing';
|
||||||
import './root/root.ts'
|
import './root/root.ts'
|
||||||
import './chat/chat.ts'
|
import './chat/chat.ts'
|
||||||
import './login/login.ts'
|
import './login/login.ts'
|
||||||
|
import './signin/signin.ts'
|
||||||
|
|
||||||
// ---- Initial load ----
|
// ---- Initial load ----
|
||||||
setTitle("");
|
setTitle("");
|
||||||
|
|
|
||||||
24
frontend/src/pages/login/alreadyLoggedin.html
Normal file
24
frontend/src/pages/login/alreadyLoggedin.html
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<div class="grid h-full place-items-center">
|
||||||
|
<div class="bg-white shadow-lg rounded-2xl p-8 w-full max-w-md">
|
||||||
|
<h1 class="text-2xl font-semibold text-center mb-6 text-gray-800">You are already logged in</h1>
|
||||||
|
<div id="returnToDiv" hidden>
|
||||||
|
<p class="text-center text-sm text-gray-500 mt-4">
|
||||||
|
We were asked to redirect you to somewhere when you logged in,
|
||||||
|
but you already are !
|
||||||
|
<br />
|
||||||
|
You can click the button below to go there
|
||||||
|
</p>
|
||||||
|
<button id="bReturnTo"
|
||||||
|
class="w-full bg-green-600 text-white font-medium py-2 rounded-xl hover:bg-gray-700 transition">
|
||||||
|
Get redirected
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p class="text-center text-sm text-gray-500 mt-4">Want to logout ? Click the big button bellow !</p>
|
||||||
|
<button id="bLogout"
|
||||||
|
class="w-full bg-gray-600 text-white font-medium py-2 rounded-xl hover:bg-gray-700 transition">
|
||||||
|
Logout
|
||||||
|
</button>
|
||||||
|
<p class="text-center text-sm text-gray-500 mt-4">Otherwise, here is a cute cat picture</p>
|
||||||
|
<img class="" id="cuteCatImage" hidden />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
BIN
frontend/src/pages/login/cuteCat.png
Normal file
BIN
frontend/src/pages/login/cuteCat.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 840 KiB |
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
class="w-full bg-blue-600 text-white font-medium py-2 rounded-xl hover:bg-blue-700 transition">
|
class="w-full bg-blue-600 text-white font-medium py-2 rounded-xl hover:bg-blue-700 transition">
|
||||||
Sign In
|
Log In
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,86 +1,163 @@
|
||||||
import { addRoute, setTitle, type RouteHandlerParams, type RouteHandlerReturn } from "@app/routing";
|
import {
|
||||||
|
addRoute,
|
||||||
|
navigateTo,
|
||||||
|
setTitle,
|
||||||
|
type RouteHandlerParams,
|
||||||
|
type RouteHandlerReturn,
|
||||||
|
} from "@app/routing";
|
||||||
import { showError, showInfo, showSuccess } from "@app/toast";
|
import { showError, showInfo, showSuccess } from "@app/toast";
|
||||||
import authHtml from './login.html?raw';
|
import authHtml from "./login.html?raw";
|
||||||
import client from '@app/api'
|
import client from "@app/api";
|
||||||
import { updateUser } from "@app/auth";
|
import { updateUser } from "@app/auth";
|
||||||
import Cookie from 'js-cookie';
|
import Cookie from "js-cookie";
|
||||||
|
import loggedInHtml from "./alreadyLoggedin.html?raw";
|
||||||
|
import cuteCat from "./cuteCat.png";
|
||||||
|
import { isNullish } from "@app/utils";
|
||||||
|
|
||||||
|
async function handleLogin(
|
||||||
type Providers = {
|
_url: string,
|
||||||
name: string,
|
_args: RouteHandlerParams,
|
||||||
display_name: string,
|
): Promise<RouteHandlerReturn> {
|
||||||
icon_url?: string,
|
setTitle("Login");
|
||||||
color?: { default: string, hover: string },
|
let user = await updateUser();
|
||||||
};
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const returnTo = urlParams.get("returnTo");
|
||||||
function handleLogin(_url: string, _args: RouteHandlerParams): RouteHandlerReturn {
|
if (user !== null) {
|
||||||
setTitle('Login')
|
return {
|
||||||
|
html: loggedInHtml,
|
||||||
|
postInsert: async (app) => {
|
||||||
|
const bLogoutButton =
|
||||||
|
app?.querySelector<HTMLButtonElement>("button#bLogout");
|
||||||
|
if (isNullish(bLogoutButton))
|
||||||
|
return showError("Error while rending page");
|
||||||
|
const iCuteCat =
|
||||||
|
app?.querySelector<HTMLImageElement>("img#cuteCatImage");
|
||||||
|
if (isNullish(iCuteCat))
|
||||||
|
return showError("Error while rending page");
|
||||||
|
const bReturnTo =
|
||||||
|
app?.querySelector<HTMLButtonElement>("button#bReturnTo");
|
||||||
|
if (isNullish(bReturnTo))
|
||||||
|
return showError("Error while rending page");
|
||||||
|
iCuteCat.src = cuteCat;
|
||||||
|
iCuteCat.hidden = false;
|
||||||
|
bLogoutButton.addEventListener("click", async () => {
|
||||||
|
await client.logout();
|
||||||
|
navigateTo("/login");
|
||||||
|
});
|
||||||
|
if (returnTo !== null) {
|
||||||
|
bReturnTo.parentElement!.hidden = false;
|
||||||
|
bReturnTo.addEventListener("click", async () => {
|
||||||
|
if (returnTo !== null) navigateTo(returnTo);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
html: authHtml, postInsert: async (app) => {
|
html: authHtml,
|
||||||
const fLogin = document.querySelector<HTMLFormElement>('form#login-form');
|
postInsert: async (app) => {
|
||||||
|
const aHref =
|
||||||
|
app?.querySelector<HTMLAnchorElement>('a[href="/signin"]');
|
||||||
|
if (!isNullish(aHref) && returnTo !== null) {
|
||||||
|
aHref.href = `/signin?returnTo=${encodeURI(returnTo)}`;
|
||||||
|
}
|
||||||
|
const fLogin =
|
||||||
|
document.querySelector<HTMLFormElement>("form#login-form");
|
||||||
if (fLogin === null)
|
if (fLogin === null)
|
||||||
return showError('Error while rendering the page: no form found');
|
return showError(
|
||||||
fLogin.addEventListener('submit', async function(e: SubmitEvent) {
|
"Error while rendering the page: no form found",
|
||||||
|
);
|
||||||
|
fLogin.addEventListener("submit", async function (e: SubmitEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let form = e.target as (HTMLFormElement | null);
|
let form = e.target as HTMLFormElement | null;
|
||||||
if (form === null)
|
if (form === null) return showError("Failed to send form...");
|
||||||
return showError('Failed to send form...');
|
let formData = Object.fromEntries(new FormData(form).entries());
|
||||||
let formData = Object.fromEntries((new FormData(form)).entries());
|
if (
|
||||||
if (!('login' in formData) || typeof formData['login'] !== 'string' || (formData['login'] as string).length === 0)
|
!("login" in formData) ||
|
||||||
return showError('Please enter a Login');
|
typeof formData["login"] !== "string" ||
|
||||||
if (!('password' in formData) || typeof formData['password'] !== 'string' || (formData['password'] as string).length === 0)
|
(formData["login"] as string).length === 0
|
||||||
return showError('Please enter a Password');
|
)
|
||||||
|
return showError("Please enter a Login");
|
||||||
|
if (
|
||||||
|
!("password" in formData) ||
|
||||||
|
typeof formData["password"] !== "string" ||
|
||||||
|
(formData["password"] as string).length === 0
|
||||||
|
)
|
||||||
|
return showError("Please enter a Password");
|
||||||
try {
|
try {
|
||||||
const res = await client.login({ loginRequest: { name: formData.login, password: formData.password } });
|
const res = await client.login({
|
||||||
|
loginRequest: {
|
||||||
|
name: formData.login,
|
||||||
|
password: formData.password,
|
||||||
|
},
|
||||||
|
});
|
||||||
switch (res.kind) {
|
switch (res.kind) {
|
||||||
case 'success': {
|
case "success": {
|
||||||
Cookie.set('token', res.payload.token, { path: '/', sameSite: 'lax' });
|
Cookie.set("token", res.payload.token, {
|
||||||
|
path: "/",
|
||||||
|
sameSite: "lax",
|
||||||
|
});
|
||||||
let user = await updateUser();
|
let user = await updateUser();
|
||||||
if (user === null)
|
if (user === null)
|
||||||
return showError('Failed to get user: no user ?');
|
return showError(
|
||||||
setTitle(`Welcome ${user.guest ? '[GUEST] ' : ''}${user.name}`);
|
"Failed to get user: no user ?",
|
||||||
|
);
|
||||||
|
setTitle(
|
||||||
|
`Welcome ${user.guest ? "[GUEST] " : ""}${user.name}`,
|
||||||
|
);
|
||||||
|
if (returnTo !== null) navigateTo(returnTo);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'otpRequired': {
|
case "otpRequired": {
|
||||||
showInfo('Got ask OTP, not yet implemented');
|
showInfo("Got ask OTP, not yet implemented");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'failed': {
|
case "failed": {
|
||||||
showError(`Failed to login: ${res.msg}`);
|
showError(`Failed to login: ${res.msg}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Login error:", e);
|
console.error("Login error:", e);
|
||||||
showError('Failed to login: Unknown error');
|
showError("Failed to login: Unknown error");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const bLoginAsGuest = document.querySelector<HTMLButtonElement>('#bGuestLogin');
|
const bLoginAsGuest =
|
||||||
bLoginAsGuest?.addEventListener('click', async () => {
|
document.querySelector<HTMLButtonElement>("#bGuestLogin");
|
||||||
|
bLoginAsGuest?.addEventListener("click", async () => {
|
||||||
try {
|
try {
|
||||||
const res = await client.guestLogin();
|
const res = await client.guestLogin();
|
||||||
switch (res.kind) {
|
switch (res.kind) {
|
||||||
case 'success': {
|
case "success": {
|
||||||
Cookie.set('token', res.payload.token, { path: '/', sameSite: 'lax' });
|
Cookie.set("token", res.payload.token, {
|
||||||
|
path: "/",
|
||||||
|
sameSite: "lax",
|
||||||
|
});
|
||||||
let user = await updateUser();
|
let user = await updateUser();
|
||||||
if (user === null)
|
if (user === null)
|
||||||
return showError('Failed to get user: no user ?');
|
return showError(
|
||||||
setTitle(`Welcome ${user.guest ? '[GUEST] ' : ''}${user.name}`);
|
"Failed to get user: no user ?",
|
||||||
|
);
|
||||||
|
setTitle(
|
||||||
|
`Welcome ${user.guest ? "[GUEST] " : ""}${user.name}`,
|
||||||
|
);
|
||||||
|
if (returnTo !== null) navigateTo(returnTo);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'failed': {
|
case "failed": {
|
||||||
showError(`Failed to login: ${res.msg}`);
|
showError(`Failed to login: ${res.msg}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Login error:", e);
|
console.error("Login error:", e);
|
||||||
showError('Failed to login: Unknown error');
|
showError("Failed to login: Unknown error");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const dOtherLoginArea = document.querySelector<HTMLDivElement>('#otherLogin');
|
const dOtherLoginArea =
|
||||||
|
document.querySelector<HTMLDivElement>("#otherLogin");
|
||||||
if (dOtherLoginArea) {
|
if (dOtherLoginArea) {
|
||||||
let styleSheetElement = document.createElement('style');
|
let styleSheetElement = document.createElement("style");
|
||||||
styleSheetElement.innerText = "";
|
styleSheetElement.innerText = "";
|
||||||
// TODO: fetch all the providers from an API ?
|
// TODO: fetch all the providers from an API ?
|
||||||
const providersReq = await client.providerList();
|
const providersReq = await client.providerList();
|
||||||
|
|
@ -92,37 +169,32 @@ function handleLogin(_url: string, _args: RouteHandlerParams): RouteHandlerRetur
|
||||||
]*/
|
]*/
|
||||||
let first = true;
|
let first = true;
|
||||||
for (const p of providers) {
|
for (const p of providers) {
|
||||||
let b = document.createElement('button');
|
let b = document.createElement("button");
|
||||||
if (first && providers.length % 2) b.classList.add('last:col-span-2');
|
if (first && providers.length % 2)
|
||||||
|
b.classList.add("last:col-span-2");
|
||||||
first = false;
|
first = false;
|
||||||
b.classList.add(...(
|
b.classList.add(
|
||||||
'w-full text-white font-medium py-2 rounded-xl transition'
|
..."w-full text-white font-medium py-2 rounded-xl transition".split(
|
||||||
.split(' ')
|
" ",
|
||||||
));
|
),
|
||||||
b.classList.add(`providerButton-${p.name}`)
|
);
|
||||||
|
b.classList.add(`providerButton-${p.name}`);
|
||||||
|
|
||||||
const col = p.colors;
|
const col = p.colors;
|
||||||
|
|
||||||
for (const k of Object.keys(col)) {
|
for (const k of Object.keys(col)) {
|
||||||
let c = (col as any)[k].trim();
|
let c = (col as any)[k].trim();
|
||||||
if (c.startsWith('bg-')) {
|
if (c.startsWith("bg-")) {
|
||||||
c = c.replace(/^bg-/, '');
|
c = c.replace(/^bg-/, "");
|
||||||
const customProp = c.match(/^\((.+)\)$/);
|
const customProp = c.match(/^\((.+)\)$/);
|
||||||
const customVal = c.match(/^\[(.+)\]$/);
|
const customVal = c.match(/^\[(.+)\]$/);
|
||||||
|
|
||||||
if (customProp)
|
if (customProp) c = `var(${customProp[1]})`;
|
||||||
c = `var(${customProp[1]})`
|
else if (customVal) c = customVal[1];
|
||||||
else if (customVal)
|
else if (c === "inherit") c = "inherit";
|
||||||
c = customVal[1];
|
else if (c === "current") c = "currentColor";
|
||||||
else if (c === 'inherit')
|
else if (c === "transparent") c = "transparent";
|
||||||
c = 'inherit';
|
else c = `var(--color-${c})`;
|
||||||
else if (c === 'current')
|
|
||||||
c = 'currentColor';
|
|
||||||
else if (c === 'transparent')
|
|
||||||
c = 'transparent';
|
|
||||||
else
|
|
||||||
c = `var(--color-${c})`
|
|
||||||
|
|
||||||
}
|
}
|
||||||
(col as any)[k] = c;
|
(col as any)[k] = c;
|
||||||
}
|
}
|
||||||
|
|
@ -134,19 +206,17 @@ function handleLogin(_url: string, _args: RouteHandlerParams): RouteHandlerRetur
|
||||||
b.dataset.name = p.name;
|
b.dataset.name = p.name;
|
||||||
//if (p.icon_url) b.dataset.icon = p.icon_url;
|
//if (p.icon_url) b.dataset.icon = p.icon_url;
|
||||||
|
|
||||||
b.innerHTML = `<span class="">${p.displayName}</span>`
|
b.innerHTML = `<span class="">${p.displayName}</span>`;
|
||||||
b.addEventListener('click', () => {
|
b.addEventListener("click", () => {
|
||||||
location.href = `/api/auth/oauth2/${p.name}/login`;
|
location.href = `/api/auth/oauth2/${p.name}/login`;
|
||||||
})
|
});
|
||||||
|
|
||||||
dOtherLoginArea.insertAdjacentElement('afterbegin', b);
|
dOtherLoginArea.insertAdjacentElement("afterbegin", b);
|
||||||
}
|
}
|
||||||
app?.appendChild(styleSheetElement);
|
app?.appendChild(styleSheetElement);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addRoute("/login", handleLogin, { bypass_auth: true });
|
||||||
addRoute('/login', handleLogin, { bypass_auth: true })
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,19 @@
|
||||||
import { addRoute, setTitle, type RouteHandlerParams } from "@app/routing";
|
import { addRoute, setTitle, type RouteHandlerParams } from "@app/routing";
|
||||||
import page from './root.html?raw'
|
import page from './root.html?raw'
|
||||||
|
import { updateUser } from "@app/auth";
|
||||||
|
|
||||||
addRoute('/', (_: string) => {
|
addRoute('/', async (_: string): Promise<string> => {
|
||||||
setTitle('ft boules')
|
let user = await updateUser();
|
||||||
|
if (user === null)
|
||||||
|
setTitle(`Welcome`)
|
||||||
|
else
|
||||||
|
setTitle(`Welcome ${user.guest ? '[GUEST] ' : ''}${user.name}`);
|
||||||
return page;
|
return page;
|
||||||
})
|
}, { bypass_auth: true })
|
||||||
|
|
||||||
|
|
||||||
addRoute('/with_title/:title', (_: string, args: RouteHandlerParams) => {
|
addRoute('/with_title/:title', (_: string, args: RouteHandlerParams) => {
|
||||||
setTitle(args.title)
|
setTitle(args.title)
|
||||||
console.log(`title should be '${args.title}'`);
|
console.log(`title should be '${args.title}'`);
|
||||||
return page;
|
return page;
|
||||||
})
|
}, { bypass_auth: false })
|
||||||
|
|
|
||||||
24
frontend/src/pages/signin/alreadyLoggedin.html
Normal file
24
frontend/src/pages/signin/alreadyLoggedin.html
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<div class="grid h-full place-items-center">
|
||||||
|
<div class="bg-white shadow-lg rounded-2xl p-8 w-full max-w-md">
|
||||||
|
<h1 class="text-2xl font-semibold text-center mb-6 text-gray-800">You are already logged in</h1>
|
||||||
|
<div id="returnToDiv" hidden>
|
||||||
|
<p class="text-center text-sm text-gray-500 mt-4">
|
||||||
|
We were asked to redirect you to somewhere when you logged in,
|
||||||
|
but you already are !
|
||||||
|
<br />
|
||||||
|
You can click the button below to go there
|
||||||
|
</p>
|
||||||
|
<button id="bReturnTo"
|
||||||
|
class="w-full bg-green-600 text-white font-medium py-2 rounded-xl hover:bg-gray-700 transition">
|
||||||
|
Get redirected
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p class="text-center text-sm text-gray-500 mt-4">Want to logout ? Click the big button bellow !</p>
|
||||||
|
<button id="bLogout"
|
||||||
|
class="w-full bg-gray-600 text-white font-medium py-2 rounded-xl hover:bg-gray-700 transition">
|
||||||
|
Logout
|
||||||
|
</button>
|
||||||
|
<p class="text-center text-sm text-gray-500 mt-4">Otherwise, here is a cute cat picture</p>
|
||||||
|
<img class="" id="cuteCatImage" hidden />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
BIN
frontend/src/pages/signin/cuteCat.png
Normal file
BIN
frontend/src/pages/signin/cuteCat.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 840 KiB |
24
frontend/src/pages/signin/signin.html
Normal file
24
frontend/src/pages/signin/signin.html
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<div class="grid h-full place-items-center">
|
||||||
|
<div class="bg-white shadow-lg rounded-2xl p-8 w-full max-w-md">
|
||||||
|
<h1 class="text-2xl font-semibold text-center mb-6 text-gray-800">Welcome to <span>ft boules</span></h1>
|
||||||
|
<form class="space-y-5 pt-3" id="signin-form">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-1">Username</label>
|
||||||
|
<input type="text" placeholder="Enter your username" name="login"
|
||||||
|
class="w-full px-4 py-2 border border-gray-300 text-gray-700 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-1">Password</label>
|
||||||
|
<input type="password" placeholder="Enter your password" name="password"
|
||||||
|
class="w-full px-4 py-2 border border-gray-300 text-gray-700 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit"
|
||||||
|
class="w-full bg-blue-600 text-white font-medium py-2 rounded-xl hover:bg-blue-700 transition">
|
||||||
|
Register
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
88
frontend/src/pages/signin/signin.ts
Normal file
88
frontend/src/pages/signin/signin.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
import { addRoute, setTitle, navigateTo, type RouteHandlerParams, type RouteHandlerReturn } from "@app/routing";
|
||||||
|
import { showError, showInfo, showSuccess } from "@app/toast";
|
||||||
|
import page from './signin.html?raw';
|
||||||
|
import client from '@app/api'
|
||||||
|
import { updateUser } from "@app/auth";
|
||||||
|
import Cookie from 'js-cookie';
|
||||||
|
import loggedInHtml from './alreadyLoggedin.html?raw';
|
||||||
|
import { isNullish } from "@app/utils";
|
||||||
|
import cuteCat from './cuteCat.png';
|
||||||
|
|
||||||
|
async function handleSignin(_url: string, _args: RouteHandlerParams): Promise<RouteHandlerReturn> {
|
||||||
|
setTitle('Signin')
|
||||||
|
let user = await updateUser();
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const returnTo = urlParams.get("returnTo");
|
||||||
|
if (user !== null) {
|
||||||
|
return {
|
||||||
|
html: loggedInHtml,
|
||||||
|
postInsert: async (app) => {
|
||||||
|
const bLogoutButton =
|
||||||
|
app?.querySelector<HTMLButtonElement>("button#bLogout");
|
||||||
|
if (isNullish(bLogoutButton))
|
||||||
|
return showError("Error while rending page");
|
||||||
|
const iCuteCat =
|
||||||
|
app?.querySelector<HTMLImageElement>("img#cuteCatImage");
|
||||||
|
if (isNullish(iCuteCat))
|
||||||
|
return showError("Error while rending page");
|
||||||
|
const bReturnTo =
|
||||||
|
app?.querySelector<HTMLButtonElement>("button#bReturnTo");
|
||||||
|
if (isNullish(bReturnTo))
|
||||||
|
return showError("Error while rending page");
|
||||||
|
iCuteCat.src = cuteCat;
|
||||||
|
iCuteCat.hidden = false;
|
||||||
|
bLogoutButton.addEventListener("click", async () => {
|
||||||
|
await client.logout();
|
||||||
|
navigateTo("/signin");
|
||||||
|
});
|
||||||
|
if (returnTo !== null) {
|
||||||
|
bReturnTo.parentElement!.hidden = false;
|
||||||
|
bReturnTo.addEventListener("click", async () => {
|
||||||
|
if (returnTo !== null) navigateTo(returnTo);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
html: page, postInsert: async (app) => {
|
||||||
|
const fSignin = document.querySelector<HTMLFormElement>('form#signin-form');
|
||||||
|
if (fSignin === null)
|
||||||
|
return showError('Error while rendering the page: no form found');
|
||||||
|
fSignin.addEventListener('submit', async function(e: SubmitEvent) {
|
||||||
|
e.preventDefault();
|
||||||
|
let form = e.target as (HTMLFormElement | null);
|
||||||
|
if (form === null)
|
||||||
|
return showError('Failed to send form...');
|
||||||
|
let formData = Object.fromEntries((new FormData(form)).entries());
|
||||||
|
if (!('login' in formData) || typeof formData['login'] !== 'string' || (formData['login'] as string).length === 0)
|
||||||
|
return showError('Please enter a Login');
|
||||||
|
if (!('password' in formData) || typeof formData['password'] !== 'string' || (formData['password'] as string).length === 0)
|
||||||
|
return showError('Please enter a Password');
|
||||||
|
try {
|
||||||
|
const res = await client.signin({ loginRequest: { name: formData.login, password: formData.password } });
|
||||||
|
switch (res.kind) {
|
||||||
|
case 'success': {
|
||||||
|
Cookie.set('token', res.payload.token, { path: '/', sameSite: 'lax' });
|
||||||
|
let user = await updateUser();
|
||||||
|
if (user === null)
|
||||||
|
return showError('Failed to get user: no user ?');
|
||||||
|
navigateTo(returnTo !== null ? returnTo : '/')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'failed': {
|
||||||
|
showError(`Failed to signin: ${res.msg}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Signin error:", e);
|
||||||
|
showError('Failed to signin: Unknown error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
addRoute('/signin', handleSignin, { bypass_auth: true })
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
|
import { updateUser } from '@app/auth';
|
||||||
import { route_404 } from './special_routes'
|
import { route_404 } from './special_routes'
|
||||||
|
|
||||||
// ---- Router logic ----
|
// ---- Router logic ----
|
||||||
function navigateTo(url: string) {
|
export function navigateTo(url: string) {
|
||||||
history.pushState(null, "", `${url.startsWith('/') ? '/app' : ""}${url}`);
|
if (url.startsWith('/') && !url.startsWith('/app'))
|
||||||
|
url = `/app${url}`;
|
||||||
|
history.pushState(null, "", url);
|
||||||
handleRoute();
|
handleRoute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,8 +43,9 @@ export class RouteHandlerData {
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(url: string, handler: RouteHandler, special_args: Partial<RouteHandlerSpecialArgs>) {
|
constructor(url: string, handler: RouteHandler, special_args: Partial<RouteHandlerSpecialArgs>) {
|
||||||
this.special_args = RouteHandlerData.SPECIAL_ARGS_DEFAULT;
|
this.special_args = Object.assign({}, RouteHandlerData.SPECIAL_ARGS_DEFAULT);
|
||||||
Object.assign(this.special_args, special_args);
|
Object.assign(this.special_args, special_args);
|
||||||
|
console.log(url, this.special_args);
|
||||||
|
|
||||||
let parsed = RouteHandlerData.parseUrl(url);
|
let parsed = RouteHandlerData.parseUrl(url);
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
|
|
@ -184,6 +188,11 @@ export async function handleRoute() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let user = await updateUser();
|
||||||
|
console.log(route_handler);
|
||||||
|
console.log(user, !route_handler.special_args.bypass_auth, user === null && !route_handler.special_args.bypass_auth);
|
||||||
|
if (user === null && !route_handler.special_args.bypass_auth)
|
||||||
|
return navigateTo(`/login?returnTo=${encodeURIComponent(window.location.pathname)}`)
|
||||||
const app = document.getElementById('app')!;
|
const app = document.getElementById('app')!;
|
||||||
let ret = await executeRouteHandler(route_handler, window.location.pathname, args)
|
let ret = await executeRouteHandler(route_handler, window.location.pathname, args)
|
||||||
app.innerHTML = ret.html;
|
app.innerHTML = ret.html;
|
||||||
|
|
|
||||||
24
frontend/src/signin/alreadyLoggedin.html
Normal file
24
frontend/src/signin/alreadyLoggedin.html
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<div class="grid h-full place-items-center">
|
||||||
|
<div class="bg-white shadow-lg rounded-2xl p-8 w-full max-w-md">
|
||||||
|
<h1 class="text-2xl font-semibold text-center mb-6 text-gray-800">You are already logged in</h1>
|
||||||
|
<div id="returnToDiv" hidden>
|
||||||
|
<p class="text-center text-sm text-gray-500 mt-4">
|
||||||
|
We were asked to redirect you to somewhere when you logged in,
|
||||||
|
but you already are !
|
||||||
|
<br />
|
||||||
|
You can click the button below to go there
|
||||||
|
</p>
|
||||||
|
<button id="bReturnTo"
|
||||||
|
class="w-full bg-green-600 text-white font-medium py-2 rounded-xl hover:bg-gray-700 transition">
|
||||||
|
Get redirected
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p class="text-center text-sm text-gray-500 mt-4">Want to logout ? Click the big button bellow !</p>
|
||||||
|
<button id="bLogout"
|
||||||
|
class="w-full bg-gray-600 text-white font-medium py-2 rounded-xl hover:bg-gray-700 transition">
|
||||||
|
Logout
|
||||||
|
</button>
|
||||||
|
<p class="text-center text-sm text-gray-500 mt-4">Otherwise, here is a cute cat picture</p>
|
||||||
|
<img class="" id="cuteCatImage" hidden />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue