Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions apps/client/src/pages/onBoarding/GoogleCallback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@ const GoogleCallback = () => {
}
};

const redirectUri = import.meta.env.PROD
? import.meta.env.VITE_GOOGLE_REDIRECT_URI_PROD
: import.meta.env.VITE_GOOGLE_REDIRECT_URI_DEV;
const redirectUri = import.meta.env.VITE_GOOGLE_REDIRECT_URI;

const loginWithCode = async (code: string) => {
try {
Expand Down
44 changes: 42 additions & 2 deletions apps/client/src/shared/apis/setting/axiosInstance.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import axios from 'axios';
import { authStorage } from '@shared/utils/authStorage';
import { extensionBridge } from '@shared/utils/extensionBridge';
import axios from 'axios';

const noAuthNeeded = [
'/api/v1/auth/token',
Expand Down Expand Up @@ -30,7 +30,20 @@ const clearAuthSessionAndRedirect = () => {
window.location.href = '/onboarding?step=SOCIAL_LOGIN';
};

// Axios 인스턴스
// 대기열 패턴을 위한 상태 변수 선언
let isRefreshing = false;
let refreshSubscribers: ((token: string | null) => void)[] = [];

// 대기 중인 요청들을 일괄 실행하고 큐를 비우는 함수
const onRefreshed = (token: string | null) => {
refreshSubscribers.forEach((callback) => callback(token));
refreshSubscribers = [];
};
// 대기열에 요청을 추가
const addRefreshSubscriber = (callback: (token: string | null) => void) => {
refreshSubscribers.push(callback);
};

const apiRequest = axios.create({
baseURL: import.meta.env.VITE_BASE_URL,
headers: {
Expand Down Expand Up @@ -68,7 +81,26 @@ apiRequest.interceptors.response.use(
!isNoAuth &&
!isLoginPage
) {
if (isRefreshing) {
return new Promise((resolve, reject) => {
addRefreshSubscriber((token) => {
if (token) {
originalRequest._retry = true;
originalRequest.headers = originalRequest.headers ?? {};

originalRequest.headers.Authorization = `Bearer ${token}`;
resolve(apiRequest(originalRequest));
} else {
reject(
new Error('토큰 재발급에 실패하여 대기 중인 요청을 취소합니다.')
);
}
});
});
}

originalRequest._retry = true;
isRefreshing = true;

try {
const res = await reissueToken();
Expand All @@ -79,12 +111,20 @@ apiRequest.interceptors.response.use(
}

syncAccessToken(newAccessToken);

// 토큰 갱신 성공 처리
isRefreshing = false;
onRefreshed(newAccessToken);

originalRequest.headers = originalRequest.headers ?? {};
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
return apiRequest(originalRequest);
} catch (reissueError) {
console.error('토큰 재발급 실패. 다시 로그인해주세요.', reissueError);

// 토큰 갱신 실패 처리
isRefreshing = false;
onRefreshed(null);
clearAuthSessionAndRedirect();

return Promise.reject(reissueError);
Expand Down
5 changes: 2 additions & 3 deletions apps/client/src/shared/utils/handleGoogleLogin.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
export const handleGoogleLogin = () => {
const clientId = import.meta.env.VITE_GOOGLE_CLIENT_ID;

const redirectUri = import.meta.env.PROD
? import.meta.env.VITE_GOOGLE_REDIRECT_URI_PROD
: import.meta.env.VITE_GOOGLE_REDIRECT_URI_DEV;
const redirectUri = import.meta.env.VITE_GOOGLE_REDIRECT_URI;

if (!clientId || !redirectUri) {
alert('Google OAuth 설정이 누락되었습니다.');
return;
}

const googleAuthUrl =
`https://accounts.google.com/o/oauth2/v2/auth?` +
`client_id=${clientId}` +
Expand Down
2 changes: 2 additions & 0 deletions apps/extension/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
# misc
.DS_Store
.env.local
.env.development
.env.development.local
.env.test.local
.env.production
.env.production.local

npm-debug.log*
Expand Down
2 changes: 1 addition & 1 deletion apps/extension/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Pinback 핀백 - 북마크 관리와 리마인드 도구",
"version": "1.0.2",
"version": "1.0.3",
"action": { "default_popup": "popup.html" },
"icons": {
"128": "icon.png",
Expand Down
8 changes: 5 additions & 3 deletions apps/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"dev": "vite --mode development",
"build": "pnpm build:prod",
"build:dev": "tsc -b && vite build --mode development",
"build:prod": "tsc -b && vite build --mode production",
"lint": "eslint .",
"format": "prettier -w .",
"zip": "vite build && node scripts/zip.mjs"
"zip": "pnpm build:prod && node scripts/zip.mjs"
},
"dependencies": {
"@pinback/contracts": "workspace:*",
Expand Down
6 changes: 3 additions & 3 deletions apps/extension/src/background.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { EXTENSION_MESSAGE_TYPE } from '@pinback/contracts/extension-messages';

const webUrl = import.meta.env.VITE_WEB_URL || 'https://pinback.today';

chrome.runtime.onInstalled.addListener((details) => {
if (details.reason === 'install') {
chrome.identity.getProfileUserInfo(function (info) {
chrome.storage.local.set({ email: info.email }, () => {
console.log('User email saved:');

Check warning on line 9 in apps/extension/src/background.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement

Check warning on line 9 in apps/extension/src/background.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement
});
setTimeout(() => {
chrome.tabs.create({
url: `https://pinback.today/onboarding?email=${info.email}`,
url: `${webUrl}/onboarding?email=${info.email}`,
});
}, 1000);
});
Expand All @@ -18,15 +20,13 @@
chrome.runtime.onMessage.addListener((message) => {
if (message.type === EXTENSION_MESSAGE_TYPE.setToken) {
chrome.storage.local.set({ token: message.token }, () => {
console.log('Token saved!');

Check warning on line 23 in apps/extension/src/background.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement

Check warning on line 23 in apps/extension/src/background.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement
});
}
});

chrome.runtime.onMessage.addListener((message) => {
if (message.type === EXTENSION_MESSAGE_TYPE.logout) {
chrome.storage.local.remove('token', () => {
console.log('Token removed!');

Check warning on line 29 in apps/extension/src/background.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement

Check warning on line 29 in apps/extension/src/background.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement
});
}
});
30 changes: 21 additions & 9 deletions apps/extension/src/content.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
import { EXTENSION_MESSAGE_TYPE } from '@pinback/contracts/extension-messages';

const ALLOWED_ORIGINS = __ALLOWED_ORIGINS__;

if (ALLOWED_ORIGINS.length === 0) {
console.warn(

Check warning on line 6 in apps/extension/src/content.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement

Check warning on line 6 in apps/extension/src/content.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement
'[Pinback] 허용된 도메인(ALLOWED_ORIGINS) 설정이 비어 있습니다. 토큰 동기화가 작동하지 않으니 .env 파일을 확인해주세요.'
);
}

window.addEventListener('message', (event) => {
if (event.source !== window) return;
if (event.data.type === EXTENSION_MESSAGE_TYPE.setToken) {

if (!ALLOWED_ORIGINS.includes(event.origin)) return;

const { type, token } = event.data;

if (type === EXTENSION_MESSAGE_TYPE.setToken) {
chrome.runtime.sendMessage({
type: EXTENSION_MESSAGE_TYPE.setToken,
token: event.data.token,
token,
});
chrome.storage.local.set({ token: event.data.token }, () => {
console.log('Token saved!');

chrome.storage.local.set({ token }, () => {
console.log(`[${event.origin}] Token saved!`);

Check warning on line 25 in apps/extension/src/content.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement

Check warning on line 25 in apps/extension/src/content.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement
});
}
});

window.addEventListener('message', (event) => {
if (event.source !== window) return;
if (event.data.type === EXTENSION_MESSAGE_TYPE.logout) {
// 토큰 삭제 (로그아웃)
if (type === EXTENSION_MESSAGE_TYPE.logout) {
chrome.storage.local.remove('token', () => {
console.log('Token removed!');
console.log(`[${event.origin}] Token removed!`);

Check warning on line 32 in apps/extension/src/content.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement

Check warning on line 32 in apps/extension/src/content.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement
});
}
});
4 changes: 3 additions & 1 deletion apps/extension/src/pages/LogOutPop.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Icon } from '@pinback/design-system/icons';
import LogOutImg from '/logout_chippi.svg';
const LogOutPop = () => {
const webUrl = import.meta.env.VITE_WEB_URL || 'https://pinback.today';

return (
<div className="bg-white-bg common-shadow flex h-[37.5rem] w-[35.2rem] flex-col items-center justify-center rounded-[1.2rem] px-[3.2rem] py-[2.4rem] leading-normal">
<div className="mr-auto">
Expand All @@ -25,7 +27,7 @@ const LogOutPop = () => {
type="button"
onClick={() => {
chrome.tabs.create({
url: 'https://pinback.today/onboarding?step=SOCIAL_LOGIN',
url: `${webUrl}/onboarding?step=SOCIAL_LOGIN`,
});
}}
>
Expand Down
3 changes: 3 additions & 0 deletions apps/extension/src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/// <reference types="vite/client" />

declare const __ALLOWED_ORIGINS__: string[];
70 changes: 42 additions & 28 deletions apps/extension/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,50 @@
// vite.config.ts
import { defineConfig } from 'vite';
import { crx } from '@crxjs/vite-plugin';
import svgSpritePlugin from '@pivanov/vite-plugin-svg-sprite';
import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react-swc';
import tsconfigPaths from 'vite-tsconfig-paths';
import { resolve } from 'path';
import { crx } from '@crxjs/vite-plugin';
import { defineConfig, loadEnv } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
import manifest from './manifest.json';
import svgSpritePlugin from '@pivanov/vite-plugin-svg-sprite';

export default defineConfig({
plugins: [
react(),
tsconfigPaths({
projects: [resolve(__dirname, './tsconfig.json')],
}),
tailwindcss(),
crx({ manifest }),
svgSpritePlugin({
iconDirs: ['../../packages/design-system/src/icons/source'],
symbolId: 'icon-[name]',
inject: 'body-last',
}),
],
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd());

const dynamicManifest = {
...manifest,
name: env.VITE_EXTENSION_NAME || manifest.name,
};

return {
define: {
__ALLOWED_ORIGINS__: JSON.stringify(
env.VITE_ALLOWED_ORIGINS?.split(',').filter(Boolean) || []
),
},

plugins: [
react(),
tsconfigPaths({
projects: [resolve(__dirname, './tsconfig.json')],
}),
tailwindcss(),
crx({ manifest: dynamicManifest }),
svgSpritePlugin({
iconDirs: ['../../packages/design-system/src/icons/source'],
symbolId: 'icon-[name]',
inject: 'body-last',
}),
],

build: {
outDir: 'dist',
copyPublicDir: true,
},
publicDir: 'public',
build: {
outDir: 'dist',
copyPublicDir: true,
},
publicDir: 'public',

server: {
port: 5175,
strictPort: true,
},
server: {
port: 5175,
strictPort: true,
},
};
});
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
"private": true,
"scripts": {
"build": "turbo run build",
"build:dev": "turbo run build:dev --filter=@pinback/extension",
"build:prod": "turbo run build:prod --filter=@pinback/extension",
"build-storybook": "storybook build",
"dev": "turbo run dev",
"dev:client": "turbo run dev --filter=@pinback/client",
"storybook": "storybook dev -p 6006",
"lint": "turbo run lint",
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
"check-types": "turbo run check-types",
"test": "turbo run test",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
"test": "turbo run test"
},
"devDependencies": {
"@chromatic-com/storybook": "^4.1.1",
Expand Down
11 changes: 11 additions & 0 deletions turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@
"inputs": ["$TURBO_DEFAULT$", ".env*", "!.env.sample"],
"outputs": [".next/**", "!.next/cache/**", "dist/**"]
},

"build:dev": {
"dependsOn": ["^build"],
"inputs": ["$TURBO_DEFAULT$", ".env*", "!.env.sample"],
"outputs": ["dist/**"]
},
"build:prod": {
"dependsOn": ["^build"],
"inputs": ["$TURBO_DEFAULT$", ".env*", "!.env.sample"],
"outputs": ["dist/**"]
},
"dev": {
"cache": false,
"persistent": true
Expand Down
Loading