Refactor and cleanup LoginCallback
All checks were successful
Build and Push to Registry / build-and-push (push) Successful in 3m58s

This commit is contained in:
Sosthene 2025-09-12 07:30:14 +02:00
parent 5d79b3df16
commit 7961165e1f

View File

@ -7,6 +7,7 @@ import HelpBox from "@Front/Components/Elements/HelpBox";
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage"; import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
import Module from "@Front/Config/Module"; import Module from "@Front/Config/Module";
import UserStore from "@Front/Stores/UserStore"; import UserStore from "@Front/Stores/UserStore";
import CookieService from "@Front/Services/CookieService/CookieService";
import Image from "next/image"; import Image from "next/image";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
@ -21,16 +22,10 @@ import MessageBus from "src/sdk/MessageBus";
import Iframe from "src/sdk/Iframe"; import Iframe from "src/sdk/Iframe";
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
import ImportData, { ProgressInfo } from "src/common/Api/LeCoffreApi/sdk/ImportData"; import { ProgressInfo } from "src/common/Api/LeCoffreApi/sdk/ImportData";
import RoleService from "src/common/Api/LeCoffreApi/sdk/RoleService";
import OfficeService from "src/common/Api/LeCoffreApi/sdk/OfficeService";
import OfficeRoleService from "src/common/Api/LeCoffreApi/sdk/OfficeRoleService";
import CollaboratorService from "src/common/Api/LeCoffreApi/sdk/CollaboratorService";
import { DEFAULT_STORAGE_URLS, DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants";
export default function LoginCallBack() { export default function LoginCallBack() {
const router = useRouter(); const router = useRouter();
const [idNotUser, setIdNotUser] = useState<any>(null);
const [isAuthModalOpen, setIsAuthModalOpen] = useState(false); const [isAuthModalOpen, setIsAuthModalOpen] = useState(false);
const [isConnected, setIsConnected] = useState(false); const [isConnected, setIsConnected] = useState(false);
@ -43,169 +38,26 @@ export default function LoginCallBack() {
description: '' description: ''
}); });
const getOffice = async (idNotUser: any) => { const waitForUserInfo = (): Promise<any> => {
return await new Promise<any>((resolve: (office: any) => void) => { return new Promise((resolve, reject) => {
OfficeService.getOffices().then((processes: any[]) => { if (UserStore.instance.getUser()) {
const officeFound: any = processes.length > 0 ? processes.find((office: any) => office.processData.idNot === idNotUser.office.idNot) : null; resolve(UserStore.instance.getUser());
if (officeFound) {
resolve(officeFound);
} else {
// Some info must be here or have some value, just to be sure
if (!idNotUser.office.office_status || idNotUser.office.office_status !== 'ACTIVATED') {
console.error(`[LoginCallback] office_status is not ACTIVATED for idNot ${idNotUser.office.idNot}`);
return; return;
} }
// I guess if we don't have crpcen that's also a big problem // Poll for userInfo every 100ms
if (!idNotUser.office.crpcen) { const checkInterval = setInterval(() => {
console.error(`[LoginCallback] crpcen is not set for idNot ${idNotUser.office.idNot}`); if (UserStore.instance.getUser()) {
return; clearInterval(checkInterval);
resolve(UserStore.instance.getUser());
} }
}, 100);
// We create office // Timeout after 60 seconds
const officeData: any = { setTimeout(() => {
idNot: idNotUser.office.idNot, clearInterval(checkInterval);
name: idNotUser.office.name, reject(new Error('Timeout waiting for user info'));
crpcen: idNotUser.office.crpcen, }, 60000);
address: {
create: {
address: idNotUser.office.address.address,
zip_code: idNotUser.office.address.zip_code,
city: idNotUser.office.address.city
}
},
office_status: idNotUser.office.office_status // must be ACTIVATED though
};
Auth.getInstance().getIdNotUserForOffice(idNotUser.office.idNot).then((users: any) => {
console.log('users : ', users);
const activeUsers = users.result.filter((user: any) => user.activite === 'En exercice');
let officeCollaborators: any[] = [];
for (const user of activeUsers) {
CollaboratorService.getCollaboratorByUid(user.uid).then((collaborator: any) => {
console.log('collaborator : ', collaborator);
officeCollaborators.push(collaborator);
});
}
OfficeService.createOffice(officeData, officeCollaborators, DEFAULT_VALIDATOR_ID, [...DEFAULT_STORAGE_URLS]).then((process: any) => {
if (process) {
const office: any = process.processData;
resolve(office);
}
});
});
}
return;
});
});
};
const getCollaborator = async (idNotUser: any) => {
return await new Promise<any>(async (resolve: (role: any) => void) => {
const processFound: any | null = await CollaboratorService.getCollaboratorBy({ idNot: idNotUser.idNot });
if (processFound) {
console.log('Found a collaborator for idNot', idNotUser.idNot);
// TODO: check if the collaborator is in the office process
const office: any = await getOffice(idNotUser);
// Take the role of the collaborator
MessageBus.getInstance().getRolesForProcess(processFound.processId).then((roles: any) => {
console.log('roles : ', roles);
// We should find one pairing id in the role 'owner'
const owners = roles['owner'].members;
if (owners.length !== 1) {
console.error('[LoginCallback] owner should have 1 member');
return;
}
const ownerPairingId = owners[0];
// Now we can check if the owner pairing id is in the office roles
MessageBus.getInstance().getRolesForProcess(office.processId).then((officeRoles: any) => {
const officeOwners = officeRoles['owner'].members;
if (!officeOwners.includes(ownerPairingId)) {
// We add the newly created collaborator to the office roles
OfficeService.addCollaborators(office, officeRoles, [ownerPairingId]).then((process: any) => {
resolve(processFound);
});
} else {
// Nothing to do
resolve(processFound);
}
});
});
} else {
console.log('No collaborator found for idNot', idNotUser.idNot);
const office: any = await getOffice(idNotUser);
const role: any = (await RoleService.getRoles())
.map((process: any) => process.processData)
.find((role: any) => role.name === idNotUser.role.name);
const officeRole: any = (await OfficeRoleService.getOfficeRoles())
.map((process: any) => process.processData)
.filter((officeRole: any) => officeRole.office.uid === office.processData.uid)
.find((officeRole: any) => officeRole.name === idNotUser.office_role.name);
if (!office || !role || !officeRole) {
LoaderService.getInstance().hide();
setShowProgress(true);
await ImportData.import(office, DEFAULT_VALIDATOR_ID, (info: ProgressInfo) => {
setProgressInfo(info);
});
setShowProgress(false);
LoaderService.getInstance().show();
}
const collaboratorData: any = {
idNot: idNotUser.idNot,
contact: idNotUser.contact,
office: {
uid: office.uid
},
role: {
uid: role.uid
},
office_role: {
uid: officeRole.uid
}
};
CollaboratorService.createCollaborator(collaboratorData, DEFAULT_VALIDATOR_ID).then((newCollaborator: any) => {
if (newCollaborator) {
// Now that we created the collaborator, we must check that it's in the office roles (probably not)
MessageBus.getInstance().getRolesForProcess(newCollaborator.processId).then((roles: any) => {
console.log('roles : ', roles);
// We should have our own pairing id in roles['owner']
const owner = roles['owner'].members;
if (owner.length !== 1) {
console.error('[LoginCallback] owner should have 1 member');
return;
}
const ownerPairingId = owner[0];
if (ownerPairingId !== newCollaborator.processData.uid) {
console.error('[LoginCallback] owner pairing id is not the same as the collaborator uid');
return;
}
// is ownerPairingId in roles for the office process?
MessageBus.getInstance().getRolesForProcess(office.processId).then((officeRoles: any) => {
const officeOwners = officeRoles['owner'].members;
if (!officeOwners.includes(ownerPairingId)) {
// We add the newly created collaborator to the office roles
OfficeService.addCollaborators(office, officeRoles, [ownerPairingId]).then((process: any) => {
resolve(newCollaborator);
});
} else {
// Nothing to do
resolve(newCollaborator);
}
});
});
}
});
}
}); });
}; };
@ -216,8 +68,8 @@ export default function LoginCallBack() {
// TODO: review // TODO: review
// HACK: If start with http://local.lecoffreio.4nkweb:3000/authorized-client // HACK: If start with http://local.lecoffreio.4nkweb:3000/authorized-client
// Replace with http://localhost:3000/authorized-client // Replace with http://localhost:3000/authorized-client
if (window.location.href.startsWith('http://local.4nkweb.com')) { if (window.location.href.startsWith('http://local.lecoffreio.4nkweb')) {
window.location.href = window.location.href.replace('http://local.4nkweb.com:3000/authorized-client', 'http://localhost:3000/authorized-client'); window.location.href = window.location.href.replace('http://local.lecoffreio.4nkweb:3000/authorized-client', 'http://localhost:3000/authorized-client');
return; return;
} }
@ -230,22 +82,34 @@ export default function LoginCallBack() {
window.history.replaceState({}, document.title, rootUrl); window.history.replaceState({}, document.title, rootUrl);
} }
const user: any = await Auth.getInstance().getIdNotUser(code as string); const user: any = await Auth.getInstance().idNotAuth(code as string);
setIdNotUser(user.idNotUser);
setIsAuthModalOpen(true); // Extract both user data and auth token from the response
console.log('[LoginCallback] idNotUser', idNotUser); const { idNotUser, authToken } = user;
/*
const token: any = null; if (!authToken) {
if (!token) return router.push(Module.getInstance().get().modules.pages.Login.props.path); console.error('[LoginCallback] No authToken received from backend');
await UserStore.instance.connect(token.accessToken, token.refreshToken); return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
const jwt = JwtService.getInstance().decodeJwt();
if (!jwt) return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
if (jwt.rules && !jwt.rules.includes("GET folders")) {
return router.push(Module.getInstance().get().modules.pages.Subscription.pages.New.props.path);
} }
// Store the auth token for API authentication
// TODO The authToken is just a uuid for now, it's very broken
CookieService.getInstance().setCookie("leCoffreAccessToken", authToken);
// Test that we can get user info and the authToken works
// TODO test that what's returned is identical to what we got before
const userInfoResponse = await Auth.getInstance().getIdNotUser();
console.log('[LoginCallback] userInfoResponse:', userInfoResponse);
if (!userInfoResponse || !userInfoResponse.success || !userInfoResponse.data) {
console.error('[LoginCallback] No userInfo received from backend');
return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
}
// Store user info as a cookie
CookieService.getInstance().setCookie("leCoffreUserInfo", JSON.stringify(userInfoResponse.data));
setIsAuthModalOpen(true); setIsAuthModalOpen(true);
//return router.push(Module.getInstance().get().modules.pages.Folder.props.path); console.log('[LoginCallback] authToken stored successfully');
*/
return; return;
} catch (e: any) { } catch (e: any) {
if (e.http_status === 401 && e.message === "Email not found") { if (e.http_status === 401 && e.message === "Email not found") {
@ -257,21 +121,6 @@ export default function LoginCallBack() {
return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1"); return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
} }
} }
/*
const refreshToken = CookieService.getInstance().getCookie("leCoffreRefreshToken");
if (!refreshToken) return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
const isTokenRefreshed = await JwtService.getInstance().refreshToken(refreshToken);
const jwt = JwtService.getInstance().decodeJwt();
if (!jwt) return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
if (!jwt.rules.includes("GET folders")) {
return router.push(Module.getInstance().get().modules.pages.Subscription.pages.New.props.path);
}
if (isTokenRefreshed) {
//setIsAuthModalOpen(true);
//return router.push(Module.getInstance().get().modules.pages.Folder.props.path);
return;
}
*/
return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=2"); return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=2");
} }
getUser(); getUser();
@ -354,23 +203,110 @@ export default function LoginCallBack() {
LoaderService.getInstance().show(); LoaderService.getInstance().show();
MessageBus.getInstance().initMessageListener(); MessageBus.getInstance().initMessageListener();
MessageBus.getInstance().isReady().then(async () => { MessageBus.getInstance().isReady().then(async () => {
const collaborator: any = await getCollaborator(idNotUser); try {
if (!UserStore.instance.connect(collaborator)) { const userInfo = await waitForUserInfo();
if (!userInfo) {
console.error('[LoginCallback] No userInfo received from backend');
return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
}
// console.log('userInfo : ', userInfo);
// Here we are now authenticated with idNot, we have our idnot user info
// We also have a device and it should be paired
// What we may not have yet is a collaborator process
// Office may not have a process too
let collaboratorProcess: { processId: string, processData: { [key: string]: any } } | null = null;
let officeProcess: { processId: string, processData: { [key: string]: any } } | null = null;
// Initialize collaborator process
try {
// Wait for pairing ID to be available before proceeding
const pairingId = await MessageBus.getInstance().getPairingId();
console.log('[LoginCallback] Pairing ID obtained:', pairingId);
// Check if we are part of the right collaborator process
const myCollaboratorProcessesData = await MessageBus.getInstance().getProcessesDecoded((processId: string, values: { [key: string]: any }) => {
return values['utype'] === 'collaborator'
&& values['idNot'] === userInfo.idNot
&& values['isDeleted'] === 'false';
});
if (myCollaboratorProcessesData && Object.keys(myCollaboratorProcessesData).length !== 0) {
collaboratorProcess = { processId: Object.keys(myCollaboratorProcessesData)[0]!, processData: Object.values(myCollaboratorProcessesData)[0]! };
} else {
const res = await Auth.getInstance().getUserProcessByIdNot(pairingId);
if (res.success) {
collaboratorProcess = res.data;
} else {
console.error('[LoginCallback] Error getting collaborator process');
router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
return;
}
// await new Promise(resolve => setTimeout(resolve, 100));
}
// If we're on a new device, signer should notice and add us to the process
// TODO check that we're part of the collaborator process
} catch (error: any) {
console.error('[LoginCallback] Error getting collaborator process:', error);
if (error.message === 'Timeout waiting for pairing ID') {
console.error('[LoginCallback] Pairing ID not available after timeout');
return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
}
// If we can't get collaborator process, we can't proceed
return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
}
// Initialize office process
try {
// Wait for pairing ID to be available before proceeding
const pairingId = await MessageBus.getInstance().getPairingId();
console.log('[LoginCallback] Pairing ID obtained:', pairingId);
// Now we need to check for office process
const myOfficeProcessesData = await MessageBus.getInstance().getProcessesDecoded((processId: string, values: { [key: string]: any }) => {
return values['utype'] === 'office'
&& values['idNot'] === userInfo.office.idNot
&& values['isDeleted'] === 'false';
});
if (myOfficeProcessesData && Object.keys(myOfficeProcessesData).length !== 0) {
officeProcess = { processId: Object.keys(myOfficeProcessesData)[0]!, processData: Object.values(myOfficeProcessesData)[0]! };
} else {
const res = await Auth.getInstance().getUserProcessByIdNot(pairingId);
if (res.success) {
officeProcess = res.data;
} else {
console.error('[LoginCallback] Error getting collaborator process');
router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
return;
}
}
// TODO Check that we're part of the office process
// For now we rely on the signer to get office data, for the sake of simplicity
} catch (error: any) {
console.error('[LoginCallback] Error getting office process:', error);
return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
}
// Verify both processes are initialized before proceeding
if (!collaboratorProcess || !officeProcess) {
console.error('[LoginCallback] Failed to initialize required processes');
router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
return;
}
if (!UserStore.instance.connect(collaboratorProcess.processData, officeProcess.processData)) {
console.error('[LoginCallback] collaborator not connected'); console.error('[LoginCallback] collaborator not connected');
router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1"); router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
return; return;
} }
window.location.href = Module.getInstance().get().modules.pages.Folder.props.path;
} catch (error) {
console.error('[LoginCallback] Error waiting for user info or processing collaborator:', error);
router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
} finally {
MessageBus.getInstance().destroyMessageListener(); MessageBus.getInstance().destroyMessageListener();
LoaderService.getInstance().hide(); LoaderService.getInstance().hide();
/*
if (jwt.rules && !jwt.rules.includes("GET folders")) {
router.push(Module.getInstance().get().modules.pages.Subscription.pages.New.props.path);
} }
*/ return;
window.location.href = Module.getInstance().get().modules.pages.Folder.props.path;
}); });
}, 100); }, 100);
}} }}