From e596c1f0a6f7bca9fd84b9fdccc59096c977b9a2 Mon Sep 17 00:00:00 2001 From: dongsilove Date: Sat, 18 Jul 2026 14:01:33 +0900 Subject: [PATCH] =?UTF-8?q?wr:=20sn:=20fo:=20X-Device-Id=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/fo/app/api/proxy/[...url]/route.ts | 5 +++++ apps/fo/package.json | 3 ++- apps/fo/src/entities/auth/api/auth-service.ts | 18 +++++++++++------ apps/fo/src/entities/auth/lib/device-id.ts | 20 +++++++++++++++++++ apps/sn/app/api/proxy/[...url]/route.ts | 5 +++++ apps/sn/package.json | 1 + apps/sn/src/entities/auth/api/auth-service.ts | 18 +++++++++++------ apps/sn/src/entities/auth/lib/device-id.ts | 20 +++++++++++++++++++ apps/wr/app/api/proxy/[...url]/route.ts | 5 +++++ apps/wr/package.json | 1 + apps/wr/src/entities/auth/api/auth-service.ts | 18 +++++++++++------ apps/wr/src/entities/auth/lib/device-id.ts | 20 +++++++++++++++++++ pnpm-lock.yaml | 15 ++++++++++++++ 13 files changed, 130 insertions(+), 19 deletions(-) create mode 100644 apps/fo/src/entities/auth/lib/device-id.ts create mode 100644 apps/sn/src/entities/auth/lib/device-id.ts create mode 100644 apps/wr/src/entities/auth/lib/device-id.ts 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 ea942780c..cbc0fb3f8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -942,6 +942,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 @@ -1063,6 +1066,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 @@ -1419,6 +1425,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 @@ -7417,6 +7426,10 @@ packages: utrie@1.0.2: resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==} + uuid@14.0.1: + resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} + hasBin: true + uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). @@ -13550,6 +13563,8 @@ snapshots: dependencies: base64-arraybuffer: 1.0.2 + uuid@14.0.1: {} + uuid@9.0.1: {} victory-vendor@36.9.2: