Przeglądaj źródła

use taipy resource handler route to resolve cookies (#1956)

Dinh Long Nguyen 7 miesięcy temu
rodzic
commit
55283ed456

+ 16 - 5
frontend/taipy-gui/base/src/app.ts

@@ -9,6 +9,7 @@ import { initSocket } from "./socket";
 import { TaipyWsAdapter, WsAdapter } from "./wsAdapter";
 import { TaipyWsAdapter, WsAdapter } from "./wsAdapter";
 import { WsMessageType } from "../../src/context/wsUtils";
 import { WsMessageType } from "../../src/context/wsUtils";
 import { getBase } from "./utils";
 import { getBase } from "./utils";
+import { CookieHandler } from "./cookieHandler";
 
 
 export type OnInitHandler = (taipyApp: TaipyApp) => void;
 export type OnInitHandler = (taipyApp: TaipyApp) => void;
 export type OnChangeHandler = (taipyApp: TaipyApp, encodedName: string, value: unknown, dataEventKey?: string) => void;
 export type OnChangeHandler = (taipyApp: TaipyApp, encodedName: string, value: unknown, dataEventKey?: string) => void;
@@ -36,6 +37,7 @@ export class TaipyApp {
     _onWsStatusUpdate: OnWsStatusUpdate | undefined;
     _onWsStatusUpdate: OnWsStatusUpdate | undefined;
     _ackList: string[];
     _ackList: string[];
     _rdc: Record<string, Record<string, RequestDataCallback>>;
     _rdc: Record<string, Record<string, RequestDataCallback>>;
+    _cookieHandler: CookieHandler | undefined;
     variableData: DataManager | undefined;
     variableData: DataManager | undefined;
     functionData: DataManager | undefined;
     functionData: DataManager | undefined;
     appId: string;
     appId: string;
@@ -51,6 +53,7 @@ export class TaipyApp {
         onChange: OnChangeHandler | undefined = undefined,
         onChange: OnChangeHandler | undefined = undefined,
         path: string | undefined = undefined,
         path: string | undefined = undefined,
         socket: Socket | undefined = undefined,
         socket: Socket | undefined = undefined,
+        handleCookie: boolean = true,
     ) {
     ) {
         socket = socket || io("/", { autoConnect: false, path: `${this.getBaseUrl()}socket.io` });
         socket = socket || io("/", { autoConnect: false, path: `${this.getBaseUrl()}socket.io` });
         this.onInit = onInit;
         this.onInit = onInit;
@@ -67,8 +70,10 @@ export class TaipyApp {
         this.wsAdapters = [new TaipyWsAdapter()];
         this.wsAdapters = [new TaipyWsAdapter()];
         this._ackList = [];
         this._ackList = [];
         this._rdc = {};
         this._rdc = {};
-        // Init socket io connection
-        initSocket(socket, this);
+        this._cookieHandler = handleCookie ? new CookieHandler() : undefined;
+        // Init socket io connection only when cookie is not handled
+        // Socket will be initialized by cookie handler when it is used
+        this._cookieHandler ? this._cookieHandler?.init(socket, this) : initSocket(socket, this);
     }
     }
 
 
     // Getter and setter
     // Getter and setter
@@ -169,7 +174,7 @@ export class TaipyApp {
         this.sendWsMessage("ID", TAIPY_CLIENT_ID, id);
         this.sendWsMessage("ID", TAIPY_CLIENT_ID, id);
         if (id !== "") {
         if (id !== "") {
             this.clientId = id;
             this.clientId = id;
-            this.initApp()
+            this.initApp();
             this.updateContext(this.path);
             this.updateContext(this.path);
         }
         }
     }
     }
@@ -292,6 +297,12 @@ export class TaipyApp {
     }
     }
 }
 }
 
 
-export const createApp = (onInit?: OnInitHandler, onChange?: OnChangeHandler, path?: string, socket?: Socket) => {
-    return new TaipyApp(onInit, onChange, path, socket);
+export const createApp = (
+    onInit?: OnInitHandler,
+    onChange?: OnChangeHandler,
+    path?: string,
+    socket?: Socket,
+    handleCookie?: boolean,
+) => {
+    return new TaipyApp(onInit, onChange, path, socket, handleCookie);
 };
 };

+ 57 - 0
frontend/taipy-gui/base/src/cookieHandler.ts

@@ -0,0 +1,57 @@
+import { Socket } from "socket.io-client";
+import { TaipyApp } from "./app";
+import { initSocket } from "./socket";
+import axios from "axios";
+import { getLocalStorageValue } from "../../src/context/utils";
+
+export const TAIPY_RESOURCE_HANDLER = "tprh";
+
+export class CookieHandler {
+    resourceHandlerId: string;
+    constructor() {
+        this.resourceHandlerId = getLocalStorageValue(TAIPY_RESOURCE_HANDLER, "");
+    }
+    async init(socket: Socket, taipyApp: TaipyApp) {
+        const hasValidCookies = await this.verifyCookieStatus();
+        if (!hasValidCookies) {
+            await this.deleteCookie();
+            localStorage.removeItem(TAIPY_RESOURCE_HANDLER);
+            window.location.reload();
+            return;
+        }
+        this.addBeforeUnloadListener();
+        initSocket(socket, taipyApp);
+    }
+
+    async verifyCookieStatus(): Promise<boolean> {
+        // check to see if local storage has the resource handler id (potentially having a cookie)
+        // If not, then some part of the code must have removed the cookie
+        // or wants to remove the cookie by removing the local storage
+        if (!this.resourceHandlerId) {
+            return new Promise((resolve) => resolve(false));
+        }
+        try {
+            // call to get cookie status
+            const { data } = await axios.get("taipy-rh");
+            // validate cookie status
+            if (data?.rh_id !== this.resourceHandlerId) {
+                return new Promise((resolve) => resolve(false));
+            }
+        } catch (error) {
+            console.error("Error while validating cookie:", error);
+            return new Promise((resolve) => resolve(false));
+        }
+        return new Promise((resolve) => resolve(true));
+    }
+
+    addBeforeUnloadListener() {
+        window.addEventListener("beforeunload", () => {
+            localStorage.removeItem(TAIPY_RESOURCE_HANDLER);
+            this.deleteCookie();
+        });
+    }
+
+    async deleteCookie() {
+        await axios.delete("taipy-rh");
+    }
+}

+ 0 - 6
frontend/taipy-gui/base/src/index-preview.ts

@@ -1,6 +0,0 @@
-import { TaipyApp, createApp, OnChangeHandler, OnInitHandler } from "./app";
-import { ModuleData } from "./dataManager";
-
-export default TaipyApp;
-export { TaipyApp, createApp };
-export type { OnChangeHandler, OnInitHandler, ModuleData };

+ 0 - 4
frontend/taipy-gui/base/src/index.ts

@@ -4,7 +4,3 @@ import { ModuleData } from "./dataManager";
 export default TaipyApp;
 export default TaipyApp;
 export { TaipyApp, createApp };
 export { TaipyApp, createApp };
 export type { OnChangeHandler, OnInitHandler, ModuleData };
 export type { OnChangeHandler, OnInitHandler, ModuleData };
-
-window.addEventListener("beforeunload", () => {
-    document.cookie = "tprh=;path=/;Max-Age=-99999999;";
-});

+ 13 - 1
frontend/taipy-gui/base/src/packaging/taipy-gui-base.d.ts

@@ -75,7 +75,8 @@ export type WsMessageType =
     | "GDT"
     | "GDT"
     | "AID"
     | "AID"
     | "GR"
     | "GR"
-    | "FV";
+    | "FV"
+    | "BC";
 export interface WsMessage {
 export interface WsMessage {
     type: WsMessageType | string;
     type: WsMessageType | string;
     name: string;
     name: string;
@@ -89,6 +90,14 @@ export declare abstract class WsAdapter {
     abstract supportedMessageTypes: string[];
     abstract supportedMessageTypes: string[];
     abstract handleWsMessage(message: WsMessage, app: TaipyApp): boolean;
     abstract handleWsMessage(message: WsMessage, app: TaipyApp): boolean;
 }
 }
+declare class CookieHandler {
+    resourceHandlerId: string;
+    constructor();
+    init(socket: Socket, taipyApp: TaipyApp): Promise<void>;
+    verifyCookieStatus(): Promise<boolean>;
+    addBeforeUnloadListener(): void;
+    deleteCookie(): Promise<void>;
+}
 export type OnInitHandler = (taipyApp: TaipyApp) => void;
 export type OnInitHandler = (taipyApp: TaipyApp) => void;
 export type OnChangeHandler = (taipyApp: TaipyApp, encodedName: string, value: unknown, dataEventKey?: string) => void;
 export type OnChangeHandler = (taipyApp: TaipyApp, encodedName: string, value: unknown, dataEventKey?: string) => void;
 export type OnNotifyHandler = (taipyApp: TaipyApp, type: string, message: string) => void;
 export type OnNotifyHandler = (taipyApp: TaipyApp, type: string, message: string) => void;
@@ -112,6 +121,7 @@ export declare class TaipyApp {
     _onWsStatusUpdate: OnWsStatusUpdate | undefined;
     _onWsStatusUpdate: OnWsStatusUpdate | undefined;
     _ackList: string[];
     _ackList: string[];
     _rdc: Record<string, Record<string, RequestDataCallback>>;
     _rdc: Record<string, Record<string, RequestDataCallback>>;
+    _cookieHandler: CookieHandler | undefined;
     variableData: DataManager | undefined;
     variableData: DataManager | undefined;
     functionData: DataManager | undefined;
     functionData: DataManager | undefined;
     appId: string;
     appId: string;
@@ -126,6 +136,7 @@ export declare class TaipyApp {
         onChange?: OnChangeHandler | undefined,
         onChange?: OnChangeHandler | undefined,
         path?: string | undefined,
         path?: string | undefined,
         socket?: Socket | undefined,
         socket?: Socket | undefined,
+        handleCookie?: boolean,
     );
     );
     get onInit(): OnInitHandler | undefined;
     get onInit(): OnInitHandler | undefined;
     set onInit(handler: OnInitHandler | undefined);
     set onInit(handler: OnInitHandler | undefined);
@@ -173,6 +184,7 @@ export declare const createApp: (
     onChange?: OnChangeHandler,
     onChange?: OnChangeHandler,
     path?: string,
     path?: string,
     socket?: Socket,
     socket?: Socket,
+    handleCookie?: boolean,
 ) => TaipyApp;
 ) => TaipyApp;
 
 
 export { TaipyApp as default };
 export { TaipyApp as default };

+ 0 - 1
frontend/taipy-gui/base/webpack.config.js

@@ -13,7 +13,6 @@ module.exports = [
         target: "web",
         target: "web",
         entry: {
         entry: {
             default: "./base/src/index.ts",
             default: "./base/src/index.ts",
-            preview: "./base/src/index-preview.ts",
         },
         },
         output: {
         output: {
             filename: (arg) => {
             filename: (arg) => {

+ 11 - 3
frontend/taipy-gui/src/components/Taipy/Navigate.tsx

@@ -12,7 +12,9 @@
  */
  */
 
 
 import { useContext, useEffect } from "react";
 import { useContext, useEffect } from "react";
+import axios from "axios";
 import { useLocation, useNavigate } from "react-router-dom";
 import { useLocation, useNavigate } from "react-router-dom";
+
 import { TaipyContext } from "../../context/taipyContext";
 import { TaipyContext } from "../../context/taipyContext";
 import { createNavigateAction } from "../../context/taipyReducers";
 import { createNavigateAction } from "../../context/taipyReducers";
 import { getBaseURL } from "../../utils";
 import { getBaseURL } from "../../utils";
@@ -65,9 +67,15 @@ const Navigate = ({ to, params, tab, force }: NavigateProps) => {
                     // Handle Resource Handler Id
                     // Handle Resource Handler Id
                     const tprh = params?.tprh;
                     const tprh = params?.tprh;
                     if (tprh !== undefined) {
                     if (tprh !== undefined) {
-                        // Add a session cookie for the resource handler id
-                        document.cookie = `tprh=${tprh};path=/;`;
-                        navigate(0);
+                        axios.post(`taipy-rh`, { tprh, is_secure: window.location.protocol.includes("https") }).then(() => {
+                            localStorage.setItem("tprh", tprh);
+                            navigate(0);
+                        }).catch((error) => {
+                            console.error(
+                                "Cannot resolve resource handler. Route `/taipy-rh` might be missing.",
+                                error,
+                            );
+                        });
                     }
                     }
                 }
                 }
             } else {
             } else {

+ 0 - 1
frontend/taipy-gui/webpack.config.js

@@ -175,7 +175,6 @@ module.exports = (env, options) => {
         target: "web",
         target: "web",
         entry: {
         entry: {
             "default": "./base/src/index.ts",
             "default": "./base/src/index.ts",
-            "preview": "./base/src/index-preview.ts",
         },
         },
         output: {
         output: {
             filename: (arg) => {
             filename: (arg) => {