From a2f66835a9dae3dc92781de887029877e9a3f69e Mon Sep 17 00:00:00 2001 From: mortezaei Date: Fri, 15 May 2026 17:15:28 +0330 Subject: [PATCH] feat: implement AuthBridge for managing HABIB_TOKEN and HABIB_COINS from window --- src/app/api/proxy/route.ts | 10 ++++-- src/app/layout.tsx | 25 +++++++++++-- src/lib/auth-bridge.ts | 74 ++++++++++++++++++++++++++++++++++++++ src/lib/http.ts | 6 ++++ src/types/window.d.ts | 12 +++++++ 5 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 src/lib/auth-bridge.ts create mode 100644 src/types/window.d.ts diff --git a/src/app/api/proxy/route.ts b/src/app/api/proxy/route.ts index 7923dd3..1e12588 100644 --- a/src/app/api/proxy/route.ts +++ b/src/app/api/proxy/route.ts @@ -103,11 +103,17 @@ function getRequestHeaders(request: NextRequest, targetUrl: URL) { const value = request.headers.get(header); if (value) { - headers.set(header, value); + if (header === "token") { + // Convert token header to Authorization + headers.set("authorization", `Token ${value}`); + } else { + headers.set(header, value); + } } } - if (authKey) { + // Override with authKey if set and no authorization header exists + if (authKey && !headers.has("authorization")) { headers.set("authorization", `token ${authKey}`); } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 107dd78..d9d0ae3 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -29,8 +29,8 @@ const amiri = Amiri({ }); export const metadata: Metadata = { - title: "ازدواج حبیب", - description: "سامانه ازدواج اسلامی حبیب", + title: "Habib Marriage", + description: "Islamic Marriage Platform", viewport: { width: "device-width", initialScale: 1, @@ -55,6 +55,27 @@ export default function RootLayout({ if (typeof window !== 'undefined') { window.onFlutterResponse = window.onFlutterResponse || function() {}; + // Save to sessionStorage when injected + Object.defineProperty(window, 'HABIB_TOKEN', { + set: function(value) { + this._habib_token = value; + if (value) sessionStorage.setItem('habib_token', value); + }, + get: function() { + return this._habib_token || sessionStorage.getItem('habib_token'); + } + }); + + Object.defineProperty(window, 'HABIB_COINS', { + set: function(value) { + this._habib_coins = value; + if (value) sessionStorage.setItem('habib_coins', String(value)); + }, + get: function() { + return this._habib_coins || parseInt(sessionStorage.getItem('habib_coins') || '0'); + } + }); + // Performance monitoring window.addEventListener('load', function() { var perfData = performance.getEntriesByType('navigation')[0]; diff --git a/src/lib/auth-bridge.ts b/src/lib/auth-bridge.ts new file mode 100644 index 0000000..ed52eae --- /dev/null +++ b/src/lib/auth-bridge.ts @@ -0,0 +1,74 @@ +class AuthBridge { + private token: string | null = null; + private coins: number = 0; + private isReady = false; + private readyCallbacks: Array<() => void> = []; + + constructor() { + this.init(); + } + + private init() { + if (typeof window !== 'undefined') { + const win = window as any; + + if (win.HABIB_TOKEN) { + this.token = win.HABIB_TOKEN; + this.coins = win.HABIB_COINS || 0; + this.isReady = true; + this.notifyReady(); + } else { + this.waitForInjection(); + } + } + } + + private waitForInjection() { + let attempts = 0; + const maxAttempts = 50; + + const checkInterval = setInterval(() => { + const win = window as any; + + if (win.HABIB_TOKEN) { + this.token = win.HABIB_TOKEN; + this.coins = win.HABIB_COINS || 0; + this.isReady = true; + clearInterval(checkInterval); + this.notifyReady(); + } else if (++attempts >= maxAttempts) { + clearInterval(checkInterval); + console.warn('⚠️ Token not received from Flutter'); + this.isReady = true; + this.notifyReady(); + } + }, 100); + } + + private notifyReady() { + this.readyCallbacks.forEach(cb => cb()); + this.readyCallbacks = []; + } + + public onReady(callback: () => void) { + if (this.isReady) { + callback(); + } else { + this.readyCallbacks.push(callback); + } + } + + public getToken(): string | null { + return this.token; + } + + public getCoins(): number { + return this.coins; + } + + public isAuthenticated(): boolean { + return !!this.token; + } +} + +export const authBridge = new AuthBridge(); diff --git a/src/lib/http.ts b/src/lib/http.ts index 620f102..5ee745e 100644 --- a/src/lib/http.ts +++ b/src/lib/http.ts @@ -1,4 +1,5 @@ import axios, { type InternalAxiosRequestConfig } from "axios"; +import { authBridge } from "./auth-bridge"; const PROXY_PATH_PARAM = "__proxyPath"; const LOCALHOST_HOSTNAMES = new Set(["localhost", "127.0.0.1", "::1"]); @@ -59,6 +60,11 @@ export const http = axios.create({ }); http.interceptors.request.use((config) => { + const token = authBridge.getToken(); + if (token) { + config.headers.Authorization = `Token ${token}`; + } + if (isLocalhost() && config.url && !isAbsoluteUrl(config.url)) { config.params = withProxyPathParam(config.params, config.url); config.url = ""; diff --git a/src/types/window.d.ts b/src/types/window.d.ts new file mode 100644 index 0000000..e2501e0 --- /dev/null +++ b/src/types/window.d.ts @@ -0,0 +1,12 @@ +declare global { + interface Window { + HABIB_TOKEN?: string; + HABIB_COINS?: number; + onFlutterResponse?: (data: any) => void; + HabibApp?: { + postMessage: (message: string) => void; + }; + } +} + +export {};