WIP import_id #9

Open
sosthene wants to merge 6 commits from import_id into dev
5 changed files with 84 additions and 92 deletions
Showing only changes of commit c4fd8da72b - Show all commits

View File

@ -77,6 +77,10 @@ const mimeTypesAccepted: { [key: string]: IMimeTypes } = {
extension: "txt", extension: "txt",
size: 104857600, // 100MB size: 104857600, // 100MB
}, },
"application/json": {
extension: "json",
size: 104857600, // 100MB
},
}; };
type IDocumentFileBase = { type IDocumentFileBase = {

View File

@ -13,7 +13,6 @@ type IProps = {
export default function StepImportProfile(props: IProps) { export default function StepImportProfile(props: IProps) {
const { onSubmit, onBack, validationErrors } = props; const { onSubmit, onBack, validationErrors } = props;
const [importedFile, setImportedFile] = useState<File | null>(null);
const [profileData, setProfileData] = useState<any>(null); const [profileData, setProfileData] = useState<any>(null);
const [error, setError] = useState<string>(""); const [error, setError] = useState<string>("");
@ -22,44 +21,32 @@ export default function StepImportProfile(props: IProps) {
return data && return data &&
typeof data === "object" && typeof data === "object" &&
data.version && data.version &&
data.userData && data.user_data &&
typeof data.userData === "object"; typeof data.user_data === "object";
}; };
const handleFileUpload = useCallback(async (files: File[]) => { const handleFileUpload = useCallback((files: File[]) => {
const file = files[0]; const file = files[0];
if (!file) return; if (!file) return;
// Validate file type
if (file.type !== "application/json") {
setError("Veuillez sélectionner un fichier JSON valide.");
return;
}
// Validate file size (max 1MB)
if (file.size > 1024 * 1024) {
setError("Le fichier est trop volumineux. Taille maximum : 1MB.");
return;
}
setImportedFile(file);
setError(""); setError("");
// Read and parse JSON // Read and parse JSON
try { file.text()
const text = await file.text(); .then((text) => {
const data = JSON.parse(text); const data = JSON.parse(text);
// Validate profile structure // Validate profile structure
if (!validateProfileStructure(data)) { if (!validateProfileStructure(data)) {
setError("Le fichier ne contient pas un profil valide."); setError('Le fichier ne contient pas un profil valide.');
return; return;
} }
setProfileData(data); setProfileData(data);
} catch (err) { })
setError("Erreur lors de la lecture du fichier JSON."); .catch((e) => {
} setError(`Erreur lors de la lecture du fichier JSON: ${e}`);
});
}, []); }, []);
const handleSubmit = useCallback(() => { const handleSubmit = useCallback(() => {
@ -100,15 +87,12 @@ export default function StepImportProfile(props: IProps) {
<Typography typo={ETypo.TEXT_LG_SEMIBOLD} className={classes["preview-title"]}> <Typography typo={ETypo.TEXT_LG_SEMIBOLD} className={classes["preview-title"]}>
Profil détecté : Profil détecté :
</Typography> </Typography>
<Typography typo={ETypo.TEXT_MD_REGULAR}>
{profileData.userData?.email || "Email non disponible"}
</Typography>
<Typography typo={ETypo.TEXT_SM_REGULAR} color={ETypoColor.COLOR_NEUTRAL_600}> <Typography typo={ETypo.TEXT_SM_REGULAR} color={ETypoColor.COLOR_NEUTRAL_600}>
Version : {profileData.version} Version : {profileData.version}
</Typography> </Typography>
{profileData.exportedAt && ( {profileData.exported_at && (
<Typography typo={ETypo.TEXT_SM_REGULAR} color={ETypoColor.COLOR_NEUTRAL_600}> <Typography typo={ETypo.TEXT_SM_REGULAR} color={ETypoColor.COLOR_NEUTRAL_600}>
Exporté le : {new Date(profileData.exportedAt).toLocaleDateString('fr-FR')} Exporté le : {new Date(profileData.exported_at).toLocaleDateString('fr-FR')}
</Typography> </Typography>
)} )}
</div> </div>

View File

@ -22,6 +22,8 @@ import UserStore from "@Front/Stores/UserStore";
import AuthModal from "src/sdk/AuthModal"; import AuthModal from "src/sdk/AuthModal";
import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService"; import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService";
import StepImportProfile from "./StepImportProfile"; import StepImportProfile from "./StepImportProfile";
import MessageBus from "src/sdk/MessageBus";
import Iframe from "src/sdk/Iframe";
export enum LoginStep { export enum LoginStep {
EMAIL, EMAIL,
@ -46,6 +48,7 @@ export default function Login() {
const [validationErrors, setValidationErrors] = useState<ValidationError[]>([]); const [validationErrors, setValidationErrors] = useState<ValidationError[]>([]);
const [isAuthModalOpen, setIsAuthModalOpen] = useState(false); const [isAuthModalOpen, setIsAuthModalOpen] = useState(false);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [showIframeForImport, setShowIframeForImport] = useState(false);
// const openErrorModal = useCallback(() => { // const openErrorModal = useCallback(() => {
// setIsErrorModalOpen(true); // setIsErrorModalOpen(true);
@ -248,26 +251,22 @@ export default function Login() {
try { try {
setIsLoading(true); setIsLoading(true);
setValidationErrors([]); setValidationErrors([]);
// Show iframe for import operation (but hidden from view)
setShowIframeForImport(true);
// Call API to validate and import profile // Initialize message listener for import operation
// Note: You'll need to implement this method in your Auth service MessageBus.getInstance().initMessageListener();
// const response = await Auth.getInstance().importProfile(profileData);
// For now, we'll simulate a successful import // Wait for the iframe to be ready
// In a real implementation, you would: await MessageBus.getInstance().isReady();
// 1. Send the profile data to your backend
// 2. Validate the profile on the server await MessageBus.getInstance().importBackup(profileData);
// 3. Return authentication tokens
// 4. Connect the user // Clean up message listener after import
MessageBus.getInstance().destroyMessageListener();
// Simulate API call setShowIframeForImport(false);
await new Promise(resolve => setTimeout(resolve, 1000));
// For demo purposes, we'll just redirect to the dashboard
// In reality, you'd use the response from the API
// CustomerStore.instance.connect(response.accessToken, response.refreshToken);
// Redirect to dashboard
router.push(Module.getInstance().get().modules.pages.Folder.pages.Select.props.path); router.push(Module.getInstance().get().modules.pages.Folder.pages.Select.props.path);
} catch (error: any) { } catch (error: any) {
setValidationErrors([ setValidationErrors([
@ -280,6 +279,7 @@ export default function Login() {
]); ]);
} finally { } finally {
setIsLoading(false); setIsLoading(false);
setShowIframeForImport(false);
} }
}, [router]); }, [router]);
@ -337,6 +337,7 @@ export default function Login() {
/> />
)} )}
</div> </div>
{showIframeForImport && <Iframe showIframe={false} />}
{/* <Confirm {/* <Confirm
isOpen={isErrorModalOpen} isOpen={isErrorModalOpen}
onClose={closeErrorModal} onClose={closeErrorModal}

View File

@ -105,12 +105,7 @@ export default function LoginCallBack() {
}; };
useEffect(() => { useEffect(() => {
async function getUser() { UserStore.instance.disconnect().then(() => {
UserStore.instance.disconnect();
// TODO: review
// HACK: If start with http://local.lecoffreio.4nkweb:3000/authorized-client
// Replace with http://localhost:3000/authorized-client
if (window.location.href.startsWith('http://local.lecoffreio.4nkweb:3000/authorized-client')) { if (window.location.href.startsWith('http://local.lecoffreio.4nkweb:3000/authorized-client')) {
window.location.href = window.location.href.replace('http://local.lecoffreio.4nkweb: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;
@ -119,22 +114,11 @@ export default function LoginCallBack() {
const code = router.query["code"]; const code = router.query["code"];
if (code) { if (code) {
try { try {
const idNotUser: any = await Auth.getInstance().getIdNotUser(code as string); Auth.getInstance().getIdNotUser(code as string).then((idNotUser: any) => {
setIdNotUser(idNotUser); setIdNotUser(idNotUser);
setIsAuthModalOpen(true); setIsAuthModalOpen(true);
/* return;
const token: any = null; });
if (!token) return router.push(Module.getInstance().get().modules.pages.Login.props.path);
await UserStore.instance.connect(token.accessToken, token.refreshToken);
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);
}
setIsAuthModalOpen(true);
//return router.push(Module.getInstance().get().modules.pages.Folder.props.path);
*/
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") {
return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=3"); return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=3");
@ -145,24 +129,8 @@ 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();
}, [router]); }, [router]);
return ( return (

View File

@ -677,6 +677,37 @@ export default class MessageBus {
}); });
} }
public importBackup(backupFile: any): Promise<void> {
return new Promise<void>((resolve: () => void, reject: (error: string) => void) => {
const messageId = `IMPORT_BACKUP_${uuidv4()}`;
const unsubscribe = EventBus.getInstance().on('BACKUP_IMPORTED', (responseId: string) => {
console.log('BACKUP_IMPORTED', responseId, messageId);
if (responseId !== messageId) {
return;
}
unsubscribe();
resolve();
});
const unsubscribeError = EventBus.getInstance().on('ERROR_BACKUP_IMPORTED', (responseId: string, error: string) => {
if (responseId !== messageId) {
return;
}
unsubscribeError();
reject(error);
});
console.log('IMPORT_BACKUP', backupFile);
this.sendMessage({
type: 'IMPORT_BACKUP',
backupFile: JSON.stringify(backupFile),
messageId
});
});
}
public verifyMerkleProof(merkleProof: string, documentHash: string): Promise<boolean> { public verifyMerkleProof(merkleProof: string, documentHash: string): Promise<boolean> {
return new Promise<boolean>((resolve: (isValid: boolean) => void, reject: (error: string) => void) => { return new Promise<boolean>((resolve: (isValid: boolean) => void, reject: (error: string) => void) => {
this.checkToken().then(() => { this.checkToken().then(() => {
@ -900,6 +931,10 @@ export default class MessageBus {
this.doHandleMessage(message.messageId, 'BACKUP_RETRIEVED', message, (message: any) => message.backupFile); this.doHandleMessage(message.messageId, 'BACKUP_RETRIEVED', message, (message: any) => message.backupFile);
break; break;
case 'BACKUP_IMPORTED': // IMPORT_BACKUP
this.doHandleMessage(message.messageId, 'BACKUP_IMPORTED', message, () => { });
break;
case 'ERROR': case 'ERROR':
console.error('Error:', message); console.error('Error:', message);
this.errors[message.messageId] = message.error; this.errors[message.messageId] = message.error;