diff --git a/apps/extension/config.ts b/apps/extension/config.ts new file mode 100644 index 0000000..f6e7fc4 --- /dev/null +++ b/apps/extension/config.ts @@ -0,0 +1,14 @@ +const isProd = import.meta.env.PROD; + +export const API_BASE_URL = + import.meta.env.WXT_PUBLIC_API_URL || + (isProd ? 'https://api.dookmark.site' : 'http://localhost:3001'); + +export const WEB_BASE_URL = + import.meta.env.WXT_PUBLIC_WEB_URL || + (isProd ? 'https://dookmark.site' : 'http://localhost:3000'); + +export const SOCKET_URL = `${API_BASE_URL}/bookmarks`; + +// 열린 웹 탭을 찾을 때 쓰는 match 패턴 (browser.tabs.query) +export const WEB_TAB_MATCHES = isProd ? ['https://dookmark.site/*'] : ['http://localhost/*']; diff --git a/apps/extension/domain/auth/api.ts b/apps/extension/domain/auth/api.ts index e42952c..80c91eb 100644 --- a/apps/extension/domain/auth/api.ts +++ b/apps/extension/domain/auth/api.ts @@ -3,8 +3,7 @@ import { createApiClient, type TokenManager, type UserSession } from '@dookmark/ export type { UserSession }; import { browser } from 'wxt/browser'; - -const API_BASE_URL = 'http://localhost:3001'; +import { API_BASE_URL, WEB_BASE_URL, WEB_TAB_MATCHES } from '@/config'; export async function getTokens(): Promise<{ accessToken: string | null; @@ -49,11 +48,11 @@ async function importCookiesToStorage(): Promise { if (typeof browser === 'undefined' || !browser.cookies) return false; const accessTokenCookie = await browser.cookies.get({ - url: 'http://localhost:3001', + url: API_BASE_URL, name: 'access_token', }); const refreshTokenCookie = await browser.cookies.get({ - url: 'http://localhost:3001', + url: API_BASE_URL, name: 'refresh_token', }); @@ -134,9 +133,9 @@ export async function logout(): Promise { // 2. 현재 열린 웹 탭들에 로그아웃 메시지 브로드캐스트 try { - const tabs = await browser.tabs.query({ url: 'http://localhost/*' }); + const tabs = await browser.tabs.query({ url: WEB_TAB_MATCHES }); for (const tab of tabs) { - if (tab.id && tab.url?.includes(':3000')) { + if (tab.id && tab.url?.startsWith(WEB_BASE_URL)) { await browser.tabs.sendMessage(tab.id, { type: 'FORCE_LOGOUT' }).catch(() => {}); } } diff --git a/apps/extension/domain/auth/use-session.ts b/apps/extension/domain/auth/use-session.ts index 5e3cb78..c4dfaf1 100644 --- a/apps/extension/domain/auth/use-session.ts +++ b/apps/extension/domain/auth/use-session.ts @@ -1,10 +1,9 @@ import type { UserSession } from '@dookmark/api-client'; import { useEffect, useState } from 'react'; import { browser } from 'wxt/browser'; +import { WEB_BASE_URL, WEB_TAB_MATCHES } from '@/config'; import { checkSession, logout } from './api'; -const WEB_BASE_URL = 'http://localhost:3000'; - export function useSession() { const [user, setUser] = useState(null); const [checkingSession, setCheckingSession] = useState(true); @@ -12,9 +11,9 @@ export function useSession() { useEffect(() => { const requestSyncFromWebTabs = async () => { try { - const tabs = await browser.tabs.query({ url: 'http://localhost/*' }); + const tabs = await browser.tabs.query({ url: WEB_TAB_MATCHES }); for (const tab of tabs) { - if (tab.id && tab.url?.includes(':3000')) { + if (tab.id && tab.url?.startsWith(WEB_BASE_URL)) { await browser.tabs.sendMessage(tab.id, { type: 'REQUEST_SYNC' }).catch(() => {}); } } diff --git a/apps/extension/domain/bookmarks/sync/socket.ts b/apps/extension/domain/bookmarks/sync/socket.ts index a290f14..7a1ea0c 100644 --- a/apps/extension/domain/bookmarks/sync/socket.ts +++ b/apps/extension/domain/bookmarks/sync/socket.ts @@ -6,8 +6,7 @@ import type { } from '@dookmark/extension-bridge'; import { BOOKMARK_SOCKET_EVENTS } from '@dookmark/extension-bridge'; import { io, type Socket } from 'socket.io-client'; - -const SOCKET_URL = 'http://localhost:3001/bookmarks'; +import { SOCKET_URL } from '@/config'; // 소켓 페이로드 타입은 @dookmark/extension-bridge가 단일 출처 (서버 게이트웨이와 공유) export type { BookmarkNativeActionPayload, CrossBrowserMovePayload }; diff --git a/apps/extension/entrypoints/background/auth-messaging.ts b/apps/extension/entrypoints/background/auth-messaging.ts index d7243d9..c3d4367 100644 --- a/apps/extension/entrypoints/background/auth-messaging.ts +++ b/apps/extension/entrypoints/background/auth-messaging.ts @@ -1,11 +1,13 @@ import type { ExtensionMessage } from '@dookmark/extension-bridge'; import { browser } from 'wxt/browser'; +import { WEB_BASE_URL } from '@/config'; import { clearTokens, saveTokens } from '@/domain/auth/api'; type MessageListener = Parameters[0]; type MessageSender = Parameters[1]; -const TRUSTED_WEB_ORIGIN = 'http://localhost:3000'; +// 프로덕션 빌드는 dookmark.site, 개발은 localhost:3000 만 신뢰 +const TRUSTED_WEB_ORIGIN = WEB_BASE_URL; // 토큰 동기화/로그아웃 메시지를 처리 (외부/내부 공통) function handleAuthMessage( diff --git a/apps/extension/entrypoints/content.ts b/apps/extension/entrypoints/content.ts index 6f91ac5..4e0f888 100644 --- a/apps/extension/entrypoints/content.ts +++ b/apps/extension/entrypoints/content.ts @@ -11,9 +11,13 @@ function sendMessageToBackground(message: { type: string; payload?: unknown }) { } export default defineContentScript({ - matches: ['http://localhost/*'], + // 웹앱(개발=localhost:3000, 프로덕션=dookmark.site)에만 주입 + matches: ['http://localhost/*', 'https://dookmark.site/*'], main() { - if (window.location.port !== '3000') return; + const { hostname, port } = window.location; + const isDevWeb = hostname === 'localhost' && port === '3000'; + const isProdWeb = hostname === 'dookmark.site'; + if (!isDevWeb && !isProdWeb) return; document.documentElement.dataset.dookmarkInstalled = 'true'; diff --git a/apps/extension/entrypoints/sidepanel/pages/main.tsx b/apps/extension/entrypoints/sidepanel/pages/main.tsx index bc838d3..000314c 100644 --- a/apps/extension/entrypoints/sidepanel/pages/main.tsx +++ b/apps/extension/entrypoints/sidepanel/pages/main.tsx @@ -1,4 +1,5 @@ import { browser } from 'wxt/browser'; +import { WEB_BASE_URL } from '@/config'; import { openQuickBookmarkPopup } from '@/domain/bookmarks/quick-bookmark'; import { useLocalDnd } from '@/domain/dnd/use-local-dnd'; import { useSidepanel } from '@/entrypoints/sidepanel/providers/sidepanel-provider'; @@ -10,8 +11,6 @@ import { SidepanelMain } from '@/entrypoints/sidepanel/ui/shared/sidepanel-main' import { BookmarkItem } from '../ui/bookmarks/bookmark-item'; import { FavoritesSection } from '../ui/bookmarks/favorites-section'; -const WEB_BASE_URL = 'http://localhost:3000'; - export function MainPage() { const nav = useNavigation(); const { diff --git a/apps/extension/entrypoints/welcome/App.tsx b/apps/extension/entrypoints/welcome/App.tsx index 493ec54..ee00703 100644 --- a/apps/extension/entrypoints/welcome/App.tsx +++ b/apps/extension/entrypoints/welcome/App.tsx @@ -1,8 +1,7 @@ import { Bookmark, MousePointerClick, PanelRight, Search } from 'lucide-react'; import { useEffect } from 'react'; import { browser } from 'wxt/browser'; - -const WEB_BASE_URL = 'http://localhost:3000'; +import { WEB_BASE_URL } from '@/config'; const STEPS = [ { diff --git a/apps/extension/package.json b/apps/extension/package.json index 08599ae..1b11c08 100644 --- a/apps/extension/package.json +++ b/apps/extension/package.json @@ -2,7 +2,7 @@ "name": "dookmark", "description": "dookmark", "private": true, - "version": "0.0.0", + "version": "0.1.0", "type": "module", "scripts": { "dev": "wxt --port 3007", diff --git a/apps/extension/wxt.config.ts b/apps/extension/wxt.config.ts index 8e4a973..3231d4c 100644 --- a/apps/extension/wxt.config.ts +++ b/apps/extension/wxt.config.ts @@ -6,7 +6,7 @@ export default defineConfig({ modules: ['@wxt-dev/module-react'], manifest: ({ browser }) => { return { - host_permissions: ['http://localhost/*'], + host_permissions: ['http://localhost/*', 'https://api.dookmark.site/*'], permissions: [ 'bookmarks', 'tabs', @@ -18,7 +18,7 @@ export default defineConfig({ ], omnibox: { keyword: 'dk' }, externally_connectable: { - matches: ['http://localhost/*'], + matches: ['http://localhost/*', 'https://dookmark.site/*'], }, ...(browser === 'firefox' && { browser_action: { diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index da94347..56fa2c9 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -128,7 +128,10 @@ export default function LandingPage() { {/* Landing Footer */} -