diff --git a/apps/fo/app/api/proxy/[...url]/route.ts b/apps/fo/app/api/proxy/[...url]/route.ts index 48dd3c72c..9fdba23ea 100644 --- a/apps/fo/app/api/proxy/[...url]/route.ts +++ b/apps/fo/app/api/proxy/[...url]/route.ts @@ -19,6 +19,11 @@ async function handler( "Content-Type": "application/json", "X-Site-Id": SITE_ID, }; + const deviceId = request.headers.get("X-Device-Id"); + if (deviceId) { + headers["X-Device-Id"] = deviceId; + } + let body = {}; const isRefreshHandler = params.url.includes("refresh"); diff --git a/apps/fo/package.json b/apps/fo/package.json index 8116359df..84b192049 100644 --- a/apps/fo/package.json +++ b/apps/fo/package.json @@ -35,7 +35,8 @@ "react-hook-form": "^7.54.2", "zod": "^3.24.2", "axios": "^1.8.1", - "proj4": "^2.9.0" + "proj4": "^2.9.0", + "uuid": "^14.0.1" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/apps/fo/src/entities/auth/api/auth-service.ts b/apps/fo/src/entities/auth/api/auth-service.ts index 1bc235e5e..2ea6bab12 100644 --- a/apps/fo/src/entities/auth/api/auth-service.ts +++ b/apps/fo/src/entities/auth/api/auth-service.ts @@ -10,10 +10,9 @@ import { type AuthService, getRemainingMs, } from "@common/auth"; - +import { clearDeviceId, getOrCreateDeviceId } from "../lib/device-id"; import { clearRefreshTimer, - removeToken, setToken, setupRefreshTimer, tokenManager, @@ -24,7 +23,10 @@ import { clearTabStorePersist } from "@/src/store/tab-store"; async function login(payload: AuthPayload): Promise { const response = await fetch(`/api/proxy${ENDPOINT.USER_SERVICE.LOGIN}`, { method: "POST", - headers: { "Content-Type": "application/json" }, + headers: { + "Content-Type": "application/json", + "X-Device-Id": getOrCreateDeviceId(), + }, body: JSON.stringify(payload), }); @@ -56,7 +58,12 @@ async function silentRefresh(): Promise { try { const response = await fetch( `/api/proxy${ENDPOINT.USER_SERVICE.SILENT_REFRESH}`, - { method: "PUT" }, + { + method: "PUT", + headers: { + "X-Device-Id": getOrCreateDeviceId(), + }, + }, ); const data = await response.json(); @@ -93,8 +100,7 @@ async function silentRefresh(): Promise { export async function logout(): Promise { tokenManager.clearAuthToken(); - removeToken("site-id-key"); - removeToken("site-theme-key"); + clearDeviceId(); if (typeof window !== "undefined") { window.dispatchEvent(new CustomEvent("auth:logout")); diff --git a/apps/fo/src/entities/auth/lib/device-id.ts b/apps/fo/src/entities/auth/lib/device-id.ts new file mode 100644 index 000000000..f844fcbef --- /dev/null +++ b/apps/fo/src/entities/auth/lib/device-id.ts @@ -0,0 +1,20 @@ +import { v4 as uuidv4 } from "uuid"; + +const DEVICE_ID_KEY = "x-device-id"; + +export function getOrCreateDeviceId(): string { + if (typeof window === "undefined") return ""; + + const existing = localStorage.getItem(DEVICE_ID_KEY); + if (existing) return existing; + + const id = uuidv4(); + localStorage.setItem(DEVICE_ID_KEY, id); + return id; +} + +export function clearDeviceId(): void { + if (typeof window === "undefined") return; + + localStorage.removeItem(DEVICE_ID_KEY); +} diff --git a/apps/sn/app/api/proxy/[...url]/route.ts b/apps/sn/app/api/proxy/[...url]/route.ts index cf6447b1a..ae5da571a 100644 --- a/apps/sn/app/api/proxy/[...url]/route.ts +++ b/apps/sn/app/api/proxy/[...url]/route.ts @@ -29,6 +29,11 @@ async function handler( "Content-Type": "application/json", "X-Site-Id": SITE_ID, }; + const deviceId = request.headers.get("X-Device-Id"); + if (deviceId) { + headers["X-Device-Id"] = deviceId; + } + let body = {}; const isRefreshHandler = params.url.includes("refresh"); diff --git a/apps/sn/package.json b/apps/sn/package.json index ec64de9ce..512eadd01 100644 --- a/apps/sn/package.json +++ b/apps/sn/package.json @@ -36,6 +36,7 @@ "react-hook-form": "^7.54.2", "tailwindcss-animate": "^1.0.7", "tus-js-client": "^4.3.1", + "uuid": "^14.0.1", "xlsx": "^0.18.5", "xlsx-populate": "^1.21.0", "zod": "^3.24.2", diff --git a/apps/sn/src/entities/auth/api/auth-service.ts b/apps/sn/src/entities/auth/api/auth-service.ts index b407c2f37..93b158ef9 100644 --- a/apps/sn/src/entities/auth/api/auth-service.ts +++ b/apps/sn/src/entities/auth/api/auth-service.ts @@ -10,10 +10,9 @@ import { type AuthService, getRemainingMs, } from "@common/auth"; - +import { clearDeviceId, getOrCreateDeviceId } from "../lib/device-id"; import { clearRefreshTimer, - removeToken, setToken, setupRefreshTimer, tokenManager, @@ -24,7 +23,10 @@ import { clearTabStorePersist } from "../../../store/tab-store"; async function login(payload: AuthPayload): Promise { const response = await fetch(`/api/proxy${ENDPOINT.USER_SERVICE.LOGIN}`, { method: "POST", - headers: { "Content-Type": "application/json" }, + headers: { + "Content-Type": "application/json", + "X-Device-Id": getOrCreateDeviceId(), + }, body: JSON.stringify(payload), }); @@ -56,7 +58,12 @@ async function silentRefresh(): Promise { try { const response = await fetch( `/api/proxy${ENDPOINT.USER_SERVICE.SILENT_REFRESH}`, - { method: "PUT" }, + { + method: "PUT", + headers: { + "X-Device-Id": getOrCreateDeviceId(), + }, + }, ); const data = await response.json(); @@ -93,8 +100,7 @@ async function silentRefresh(): Promise { export async function logout(): Promise { tokenManager.clearAuthToken(); - removeToken("site-id-key"); - removeToken("site-theme-key"); + clearDeviceId(); if (typeof window !== "undefined") { window.dispatchEvent(new CustomEvent("auth:logout")); diff --git a/apps/sn/src/entities/auth/lib/device-id.ts b/apps/sn/src/entities/auth/lib/device-id.ts new file mode 100644 index 000000000..f844fcbef --- /dev/null +++ b/apps/sn/src/entities/auth/lib/device-id.ts @@ -0,0 +1,20 @@ +import { v4 as uuidv4 } from "uuid"; + +const DEVICE_ID_KEY = "x-device-id"; + +export function getOrCreateDeviceId(): string { + if (typeof window === "undefined") return ""; + + const existing = localStorage.getItem(DEVICE_ID_KEY); + if (existing) return existing; + + const id = uuidv4(); + localStorage.setItem(DEVICE_ID_KEY, id); + return id; +} + +export function clearDeviceId(): void { + if (typeof window === "undefined") return; + + localStorage.removeItem(DEVICE_ID_KEY); +} diff --git a/apps/wr/app/api/proxy/[...url]/route.ts b/apps/wr/app/api/proxy/[...url]/route.ts index 48dd3c72c..9fdba23ea 100644 --- a/apps/wr/app/api/proxy/[...url]/route.ts +++ b/apps/wr/app/api/proxy/[...url]/route.ts @@ -19,6 +19,11 @@ async function handler( "Content-Type": "application/json", "X-Site-Id": SITE_ID, }; + const deviceId = request.headers.get("X-Device-Id"); + if (deviceId) { + headers["X-Device-Id"] = deviceId; + } + let body = {}; const isRefreshHandler = params.url.includes("refresh"); diff --git a/apps/wr/package.json b/apps/wr/package.json index 3ec9cecfb..812bc6fa0 100644 --- a/apps/wr/package.json +++ b/apps/wr/package.json @@ -49,6 +49,7 @@ "tailwind-merge": "^3.0.1", "tailwindcss-animate": "^1.0.7", "tus-js-client": "^4.3.1", + "uuid": "^14.0.1", "xlsx": "^0.18.5", "xlsx-populate": "^1.21.0", "zod": "^3.24.2", diff --git a/apps/wr/src/entities/auth/api/auth-service.ts b/apps/wr/src/entities/auth/api/auth-service.ts index 1bc235e5e..2ea6bab12 100644 --- a/apps/wr/src/entities/auth/api/auth-service.ts +++ b/apps/wr/src/entities/auth/api/auth-service.ts @@ -10,10 +10,9 @@ import { type AuthService, getRemainingMs, } from "@common/auth"; - +import { clearDeviceId, getOrCreateDeviceId } from "../lib/device-id"; import { clearRefreshTimer, - removeToken, setToken, setupRefreshTimer, tokenManager, @@ -24,7 +23,10 @@ import { clearTabStorePersist } from "@/src/store/tab-store"; async function login(payload: AuthPayload): Promise { const response = await fetch(`/api/proxy${ENDPOINT.USER_SERVICE.LOGIN}`, { method: "POST", - headers: { "Content-Type": "application/json" }, + headers: { + "Content-Type": "application/json", + "X-Device-Id": getOrCreateDeviceId(), + }, body: JSON.stringify(payload), }); @@ -56,7 +58,12 @@ async function silentRefresh(): Promise { try { const response = await fetch( `/api/proxy${ENDPOINT.USER_SERVICE.SILENT_REFRESH}`, - { method: "PUT" }, + { + method: "PUT", + headers: { + "X-Device-Id": getOrCreateDeviceId(), + }, + }, ); const data = await response.json(); @@ -93,8 +100,7 @@ async function silentRefresh(): Promise { export async function logout(): Promise { tokenManager.clearAuthToken(); - removeToken("site-id-key"); - removeToken("site-theme-key"); + clearDeviceId(); if (typeof window !== "undefined") { window.dispatchEvent(new CustomEvent("auth:logout")); diff --git a/apps/wr/src/entities/auth/lib/device-id.ts b/apps/wr/src/entities/auth/lib/device-id.ts new file mode 100644 index 000000000..f844fcbef --- /dev/null +++ b/apps/wr/src/entities/auth/lib/device-id.ts @@ -0,0 +1,20 @@ +import { v4 as uuidv4 } from "uuid"; + +const DEVICE_ID_KEY = "x-device-id"; + +export function getOrCreateDeviceId(): string { + if (typeof window === "undefined") return ""; + + const existing = localStorage.getItem(DEVICE_ID_KEY); + if (existing) return existing; + + const id = uuidv4(); + localStorage.setItem(DEVICE_ID_KEY, id); + return id; +} + +export function clearDeviceId(): void { + if (typeof window === "undefined") return; + + localStorage.removeItem(DEVICE_ID_KEY); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9997d5e45..fd99062b0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -951,6 +951,9 @@ importers: select: specifier: ^1.1.2 version: 1.1.2 + uuid: + specifier: ^14.0.1 + version: 14.0.1 webpack: specifier: ^5.98.0 version: 5.105.4 @@ -1072,6 +1075,9 @@ importers: tus-js-client: specifier: ^4.3.1 version: 4.3.1 + uuid: + specifier: ^14.0.1 + version: 14.0.1 xlsx: specifier: ^0.18.5 version: 0.18.5 @@ -1431,6 +1437,9 @@ importers: tus-js-client: specifier: ^4.3.1 version: 4.3.1 + uuid: + specifier: ^14.0.1 + version: 14.0.1 xlsx: specifier: ^0.18.5 version: 0.18.5