@withwiz/toolkit 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/auth/adapters/prisma/index.d.ts +20 -4
  2. package/dist/auth/adapters/prisma/index.js +51 -61
  3. package/dist/auth/core/jwt/cookie.d.ts +1 -0
  4. package/dist/auth/core/jwt/cookie.js +1 -1
  5. package/dist/auth/core/jwt/index.d.ts +1 -1
  6. package/dist/auth/core/jwt/index.js +1 -1
  7. package/dist/auth/core/jwt/types.js +5 -7
  8. package/dist/auth/core/oauth/index.d.ts +17 -27
  9. package/dist/auth/core/oauth/index.js +18 -5
  10. package/dist/auth/core/oauth/providers/github.d.ts +12 -0
  11. package/dist/auth/core/oauth/providers/github.js +8 -0
  12. package/dist/auth/core/oauth/providers/google.d.ts +12 -0
  13. package/dist/auth/core/oauth/providers/google.js +8 -0
  14. package/dist/auth/core/oauth/providers/index.d.ts +3 -0
  15. package/dist/auth/core/oauth/providers/index.js +17 -0
  16. package/dist/auth/core/oauth/providers/kakao.d.ts +12 -0
  17. package/dist/auth/core/oauth/providers/kakao.js +8 -0
  18. package/dist/auth/core/password/client-helper.js +1 -1
  19. package/dist/auth/core/password/index.js +1 -1
  20. package/dist/auth/index.d.ts +2 -3
  21. package/dist/auth/index.js +35 -24
  22. package/dist/auth/types/index.d.ts +31 -31
  23. package/dist/auth/types/index.js +5 -7
  24. package/dist/chunk-2QQNL4ND.js +62 -0
  25. package/dist/chunk-6PGZTY4N.js +30 -0
  26. package/dist/chunk-GBLNXNEH.js +64 -0
  27. package/dist/chunk-HZLJYEOO.js +92 -0
  28. package/dist/{chunk-N4D6OPPP.js → chunk-LLVIA4JX.js} +4 -2
  29. package/dist/{chunk-5WP57PJK.js → chunk-O3UR234Y.js} +4 -4
  30. package/dist/{chunk-6D3HHYER.js → chunk-QKC73SWB.js} +6 -12
  31. package/dist/chunk-RVL5ROWQ.js +62 -0
  32. package/dist/chunk-T4MDCVUQ.js +0 -0
  33. package/dist/{chunk-J5AISIJS.js → chunk-ZBXSEQ6W.js} +18 -14
  34. package/dist/config/registry.d.ts +42 -0
  35. package/dist/config/registry.js +12 -0
  36. package/dist/constants/index.js +6 -6
  37. package/dist/initialize.d.ts +2 -0
  38. package/dist/initialize.js +18 -10
  39. package/dist/middleware/auth.d.ts +1 -0
  40. package/dist/middleware/auth.js +4 -2
  41. package/dist/middleware/index.d.ts +1 -1
  42. package/dist/middleware/index.js +8 -6
  43. package/dist/middleware/rate-limit.d.ts +3 -7
  44. package/dist/middleware/types.d.ts +1 -1
  45. package/dist/middleware/wrappers.js +4 -4
  46. package/dist/types/geoip.d.ts +4 -4
  47. package/dist/types/qr-code.d.ts +20 -20
  48. package/dist/types/qr-code.js +4 -4
  49. package/package.json +6 -1
  50. package/dist/chunk-V5K5FYU7.js +0 -200
@@ -1,3 +1,9 @@
1
+ import {
2
+ GENERIC_CONFIRM_MESSAGES,
3
+ GENERIC_ERROR_MESSAGES,
4
+ GENERIC_INFO_MESSAGES,
5
+ GENERIC_SUCCESS_MESSAGES
6
+ } from "../chunk-2TO7WUKK.js";
1
7
  import {
2
8
  DATE_RANGE,
3
9
  FILTER_OPTIONS,
@@ -38,12 +44,6 @@ import {
38
44
  getHttpStatus,
39
45
  getLogLevel
40
46
  } from "../chunk-SNTLJ646.js";
41
- import {
42
- GENERIC_CONFIRM_MESSAGES,
43
- GENERIC_ERROR_MESSAGES,
44
- GENERIC_INFO_MESSAGES,
45
- GENERIC_SUCCESS_MESSAGES
46
- } from "../chunk-2TO7WUKK.js";
47
47
  import "../chunk-ORMEWXMH.js";
48
48
  export {
49
49
  CSRF,
@@ -16,3 +16,5 @@ export interface ToolkitConfig {
16
16
  }
17
17
  export declare function initialize(config: ToolkitConfig): void;
18
18
  export type { AuthConfig, LoggerConfig, CacheConfigInput as CacheConfig, StorageConfig, GeolocationConfig, CorsConfig, };
19
+ export { initConfig, config, resetConfig } from './config/registry';
20
+ export type { ConfigRegistry } from './config/registry';
@@ -7,6 +7,11 @@ import {
7
7
  import {
8
8
  initializeGeolocation
9
9
  } from "./chunk-3CBGGMIY.js";
10
+ import {
11
+ config,
12
+ initConfig,
13
+ resetConfig
14
+ } from "./chunk-6PGZTY4N.js";
10
15
  import {
11
16
  initializeAuth
12
17
  } from "./chunk-OZE5KUS3.js";
@@ -25,17 +30,20 @@ import "./chunk-57W32BFZ.js";
25
30
  import "./chunk-ORMEWXMH.js";
26
31
 
27
32
  // src/initialize.ts
28
- function initialize(config) {
29
- initializeCommon({ nodeEnv: config.nodeEnv });
30
- if (config.logger) {
31
- initializeLogger(config.logger);
33
+ function initialize(config2) {
34
+ initializeCommon({ nodeEnv: config2.nodeEnv });
35
+ if (config2.logger) {
36
+ initializeLogger(config2.logger);
32
37
  }
33
- if (config.auth) initializeAuth(config.auth);
34
- if (config.cache) initializeCache(config.cache);
35
- if (config.storage) initializeStorage(config.storage);
36
- if (config.geolocation) initializeGeolocation(config.geolocation);
37
- if (config.cors) initializeCors(config.cors);
38
+ if (config2.auth) initializeAuth(config2.auth);
39
+ if (config2.cache) initializeCache(config2.cache);
40
+ if (config2.storage) initializeStorage(config2.storage);
41
+ if (config2.geolocation) initializeGeolocation(config2.geolocation);
42
+ if (config2.cors) initializeCors(config2.cors);
38
43
  }
39
44
  export {
40
- initialize
45
+ config,
46
+ initConfig,
47
+ initialize,
48
+ resetConfig
41
49
  };
@@ -87,4 +87,5 @@ export declare const optionalAuthMiddleware: TApiMiddleware;
87
87
  * .use(adminMiddleware);
88
88
  * ```
89
89
  */
90
+ export declare function createRoleMiddleware(...allowedRoles: string[]): TApiMiddleware;
90
91
  export declare const adminMiddleware: TApiMiddleware;
@@ -1,15 +1,16 @@
1
1
  import {
2
2
  adminMiddleware,
3
3
  authMiddleware,
4
+ createRoleMiddleware,
4
5
  initializeAuthMiddleware,
5
6
  optionalAuthMiddleware,
6
7
  setAccessTokenBlacklistChecker
7
- } from "../chunk-J5AISIJS.js";
8
+ } from "../chunk-ZBXSEQ6W.js";
8
9
  import "../chunk-EOEMOXNT.js";
9
10
  import "../chunk-SNTLJ646.js";
10
11
  import "../chunk-OZE5KUS3.js";
11
12
  import "../chunk-3GAY2T2A.js";
12
- import "../chunk-N4D6OPPP.js";
13
+ import "../chunk-LLVIA4JX.js";
13
14
  import "../chunk-AIH3F7JV.js";
14
15
  import "../chunk-GQVBWZLH.js";
15
16
  import "../chunk-OHBAELPZ.js";
@@ -20,6 +21,7 @@ import "../chunk-ORMEWXMH.js";
20
21
  export {
21
22
  adminMiddleware,
22
23
  authMiddleware,
24
+ createRoleMiddleware,
23
25
  initializeAuthMiddleware,
24
26
  optionalAuthMiddleware,
25
27
  setAccessTokenBlacklistChecker
@@ -6,7 +6,7 @@
6
6
  export type { IApiContext, IUser, TApiHandler, TApiMiddleware, IMiddlewareChainOptions, } from './types';
7
7
  export { MiddlewareChain } from './middleware-chain';
8
8
  export { initRequestMiddleware } from './init-request';
9
- export { authMiddleware, optionalAuthMiddleware, adminMiddleware, initializeAuthMiddleware, setAccessTokenBlacklistChecker } from './auth';
9
+ export { authMiddleware, optionalAuthMiddleware, adminMiddleware, createRoleMiddleware, initializeAuthMiddleware, setAccessTokenBlacklistChecker } from './auth';
10
10
  export type { IAccessTokenBlacklistChecker } from './auth';
11
11
  export { rateLimitMiddleware, createRateLimitMiddleware, setRateLimitAdapter } from './rate-limit';
12
12
  export { errorHandlerMiddleware } from './error-handler';
@@ -4,7 +4,10 @@ import {
4
4
  withCustomApi,
5
5
  withOptionalAuthApi,
6
6
  withPublicApi
7
- } from "../chunk-5WP57PJK.js";
7
+ } from "../chunk-O3UR234Y.js";
8
+ import {
9
+ initRequestMiddleware
10
+ } from "../chunk-62Q7DN5G.js";
8
11
  import {
9
12
  MiddlewareChain
10
13
  } from "../chunk-FPESPW6P.js";
@@ -24,10 +27,11 @@ import {
24
27
  import {
25
28
  adminMiddleware,
26
29
  authMiddleware,
30
+ createRoleMiddleware,
27
31
  initializeAuthMiddleware,
28
32
  optionalAuthMiddleware,
29
33
  setAccessTokenBlacklistChecker
30
- } from "../chunk-J5AISIJS.js";
34
+ } from "../chunk-ZBXSEQ6W.js";
31
35
  import {
32
36
  corsMiddleware,
33
37
  validateCorsConfiguration
@@ -35,9 +39,6 @@ import {
35
39
  import {
36
40
  errorHandlerMiddleware
37
41
  } from "../chunk-3YUKCYHI.js";
38
- import {
39
- initRequestMiddleware
40
- } from "../chunk-62Q7DN5G.js";
41
42
  import "../chunk-XHZ5L4FO.js";
42
43
  import "../chunk-L6AU74EI.js";
43
44
  import "../chunk-EOEMOXNT.js";
@@ -48,7 +49,7 @@ import "../chunk-SNTLJ646.js";
48
49
  import "../chunk-GFWGLWTS.js";
49
50
  import "../chunk-OZE5KUS3.js";
50
51
  import "../chunk-3GAY2T2A.js";
51
- import "../chunk-N4D6OPPP.js";
52
+ import "../chunk-LLVIA4JX.js";
52
53
  import "../chunk-AIH3F7JV.js";
53
54
  import "../chunk-GQVBWZLH.js";
54
55
  import "../chunk-OHBAELPZ.js";
@@ -62,6 +63,7 @@ export {
62
63
  authMiddleware,
63
64
  corsMiddleware,
64
65
  createRateLimitMiddleware,
66
+ createRoleMiddleware,
65
67
  errorHandlerMiddleware,
66
68
  initRequestMiddleware,
67
69
  initializeAuthMiddleware,
@@ -48,12 +48,8 @@ export declare function setRateLimitAdapter(adapter: IRateLimitAdapter): void;
48
48
  /**
49
49
  * 기본 Rate Limit 타입 (범용)
50
50
  */
51
- export type TRateLimitType = "api" | "auth" | "admin";
52
- /**
53
- * 확장 가능한 Rate Limit 타입
54
- * 도메인 특화 타입은 소비 프로젝트의 extension 영역에서 정의하세요.
55
- */
56
- export type TExtendedRateLimitType = TRateLimitType | string;
51
+ export type TBuiltinRateLimitType = "api" | "auth" | "admin";
52
+ export type TRateLimitType = TBuiltinRateLimitType | (string & {});
57
53
  /**
58
54
  * Rate Limit 미들웨어 팩토리
59
55
  *
@@ -65,7 +61,7 @@ export type TExtendedRateLimitType = TRateLimitType | string;
65
61
  * .use(createRateLimitMiddleware('api'));
66
62
  * ```
67
63
  */
68
- export declare function createRateLimitMiddleware(type: TExtendedRateLimitType): TApiMiddleware;
64
+ export declare function createRateLimitMiddleware(type: TRateLimitType): TApiMiddleware;
69
65
  /**
70
66
  * 사전 정의된 Rate Limit 미들웨어 (범용)
71
67
  *
@@ -10,7 +10,7 @@ export interface IUser {
10
10
  id: string;
11
11
  email: string;
12
12
  name?: string;
13
- role: 'USER' | 'ADMIN';
13
+ role: string;
14
14
  }
15
15
  /**
16
16
  * API 컨텍스트
@@ -4,15 +4,15 @@ import {
4
4
  withCustomApi,
5
5
  withOptionalAuthApi,
6
6
  withPublicApi
7
- } from "../chunk-5WP57PJK.js";
7
+ } from "../chunk-O3UR234Y.js";
8
+ import "../chunk-62Q7DN5G.js";
8
9
  import "../chunk-FPESPW6P.js";
9
10
  import "../chunk-AQJAY5FA.js";
10
11
  import "../chunk-OIRAH57Y.js";
11
12
  import "../chunk-WMQE2YK7.js";
12
- import "../chunk-J5AISIJS.js";
13
+ import "../chunk-ZBXSEQ6W.js";
13
14
  import "../chunk-KGHWGLED.js";
14
15
  import "../chunk-3YUKCYHI.js";
15
- import "../chunk-62Q7DN5G.js";
16
16
  import "../chunk-XHZ5L4FO.js";
17
17
  import "../chunk-L6AU74EI.js";
18
18
  import "../chunk-EOEMOXNT.js";
@@ -23,7 +23,7 @@ import "../chunk-SNTLJ646.js";
23
23
  import "../chunk-GFWGLWTS.js";
24
24
  import "../chunk-OZE5KUS3.js";
25
25
  import "../chunk-3GAY2T2A.js";
26
- import "../chunk-N4D6OPPP.js";
26
+ import "../chunk-LLVIA4JX.js";
27
27
  import "../chunk-AIH3F7JV.js";
28
28
  import "../chunk-GQVBWZLH.js";
29
29
  import "../chunk-OHBAELPZ.js";
@@ -13,8 +13,8 @@ export interface IGeoIPConfig {
13
13
  export interface IGeoIPService {
14
14
  getGeoLocation(ip: string): Promise<IGeoLocationData | null>;
15
15
  getGeoLocationBatch(ips: string[]): Promise<Map<string, IGeoLocationData>>;
16
- updateClickHistoryGeoLocation(linkId: string, ip: string): Promise<void>;
17
- getGeographyAnalytics(linkId: string, days?: number): Promise<IGeographyAnalytics>;
16
+ updateGeoLocation(entityId: string, ip: string): Promise<void>;
17
+ getGeographyAnalytics(entityId: string, days?: number): Promise<IGeographyAnalytics>;
18
18
  getUserGeographyAnalytics(userId: string, days?: number): Promise<IGeographyAnalytics>;
19
19
  findOrCreateGeoLocation(ip: string): Promise<string | null>;
20
20
  isGeoLocationOutdated(validFrom: Date): boolean;
@@ -40,7 +40,7 @@ export interface IGeoIPServiceFactory {
40
40
  }
41
41
  export interface IGeoIPBatchProcessor {
42
42
  processMissingGeoIPData(): Promise<IBatchProcessResult>;
43
- processLinkGeoIPData(linkId: string): Promise<IBatchProcessResult>;
43
+ processGeoIPData(entityId: string): Promise<IBatchProcessResult>;
44
44
  cleanupOldGeoIPData(daysOld?: number): Promise<number>;
45
45
  getGeoIPStats(): Promise<IGeoIPStats>;
46
46
  processUserGeoIPData(userId: string): Promise<IBatchProcessResult>;
@@ -78,7 +78,7 @@ export interface IGeoIPProvider {
78
78
  getTimeout(): number;
79
79
  }
80
80
  export interface IGeographyAnalytics {
81
- totalClicks: number;
81
+ totalEvents: number;
82
82
  countries: Array<{
83
83
  country: string;
84
84
  countryName: string;
@@ -10,7 +10,7 @@ export interface IQRCodeSettings {
10
10
  }
11
11
  export interface IQRCodeConfig {
12
12
  id: string;
13
- linkId: string;
13
+ entityId: string;
14
14
  settings: IQRCodeSettings;
15
15
  isActive: boolean;
16
16
  createdAt: Date;
@@ -28,31 +28,31 @@ export interface IQRTemplate {
28
28
  updatedAt: Date;
29
29
  }
30
30
  export interface IQRCodeStats {
31
- totalClicks: number;
32
- qrClicks: number;
33
- normalClicks: number;
34
- clicks: number;
35
- apiClicks: number;
36
- qrUsageRate: number;
31
+ totalInteractions: number;
32
+ scanCount: number;
33
+ directInteractions: number;
34
+ interactions: number;
35
+ apiInteractions: number;
36
+ usageRate: number;
37
37
  }
38
38
  export interface IQRCodeAnalytics {
39
39
  dailyStats: Array<{
40
40
  date: string;
41
- totalClicks: number;
42
- qrClicks: number;
43
- qrUsageRate: number;
41
+ totalInteractions: number;
42
+ scanCount: number;
43
+ usageRate: number;
44
44
  }>;
45
45
  deviceStats: Array<{
46
46
  deviceType: string;
47
- totalClicks: number;
48
- qrClicks: number;
49
- qrUsageRate: number;
47
+ totalInteractions: number;
48
+ scanCount: number;
49
+ usageRate: number;
50
50
  }>;
51
51
  browserStats: Array<{
52
52
  browser: string;
53
- totalClicks: number;
54
- qrClicks: number;
55
- qrUsageRate: number;
53
+ totalInteractions: number;
54
+ scanCount: number;
55
+ usageRate: number;
56
56
  }>;
57
57
  }
58
58
  export interface IQRCodeDownloadOptions {
@@ -85,8 +85,8 @@ export declare const QR_CODE_TEMPLATES: readonly [{
85
85
  readonly name: "브랜드";
86
86
  readonly description: "브랜드 컬러 적용";
87
87
  readonly settings: {
88
- readonly color: "#3B82F6";
89
- readonly bgColor: "#F8FAFC";
88
+ readonly color: "#4A90D9";
89
+ readonly bgColor: "#F5F5F5";
90
90
  readonly errorCorrectionLevel: "H";
91
91
  readonly size: number;
92
92
  readonly includeMargin: boolean;
@@ -113,8 +113,8 @@ export declare const QR_CODE_TEMPLATES: readonly [{
113
113
  readonly name: "컬러풀";
114
114
  readonly description: "화려한 컬러 스타일";
115
115
  readonly settings: {
116
- readonly color: "#EF4444";
117
- readonly bgColor: "#FEF3C7";
116
+ readonly color: "#D94A4A";
117
+ readonly bgColor: "#FFF8E1";
118
118
  readonly errorCorrectionLevel: "H";
119
119
  readonly size: number;
120
120
  readonly includeMargin: boolean;
@@ -34,8 +34,8 @@ var QR_CODE_TEMPLATES = [
34
34
  name: "\uBE0C\uB79C\uB4DC",
35
35
  description: "\uBE0C\uB79C\uB4DC \uCEEC\uB7EC \uC801\uC6A9",
36
36
  settings: __spreadProps(__spreadValues({}, DEFAULT_QR_SETTINGS), {
37
- color: "#3B82F6",
38
- bgColor: "#F8FAFC",
37
+ color: "#4A90D9",
38
+ bgColor: "#F5F5F5",
39
39
  errorCorrectionLevel: "H"
40
40
  })
41
41
  },
@@ -54,8 +54,8 @@ var QR_CODE_TEMPLATES = [
54
54
  name: "\uCEEC\uB7EC\uD480",
55
55
  description: "\uD654\uB824\uD55C \uCEEC\uB7EC \uC2A4\uD0C0\uC77C",
56
56
  settings: __spreadProps(__spreadValues({}, DEFAULT_QR_SETTINGS), {
57
- color: "#EF4444",
58
- bgColor: "#FEF3C7",
57
+ color: "#D94A4A",
58
+ bgColor: "#FFF8E1",
59
59
  errorCorrectionLevel: "H"
60
60
  })
61
61
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withwiz/toolkit",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
4
4
  "description": "Shared utility library for withwiz projects",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "exports": {
18
18
  "./initialize": "./dist/initialize.js",
19
+ "./config": "./dist/config/registry.js",
19
20
  "./config/errors": "./dist/config/errors.js",
20
21
  "./config/common": "./dist/config/common.js",
21
22
  "./auth/config": "./dist/auth/config.js",
@@ -30,6 +31,10 @@
30
31
  "./auth/core/password": "./dist/auth/core/password/index.js",
31
32
  "./auth/core/password/client-helper": "./dist/auth/core/password/client-helper.js",
32
33
  "./auth/core/oauth": "./dist/auth/core/oauth/index.js",
34
+ "./auth/core/oauth/providers": "./dist/auth/core/oauth/providers/index.js",
35
+ "./auth/core/oauth/providers/google": "./dist/auth/core/oauth/providers/google.js",
36
+ "./auth/core/oauth/providers/github": "./dist/auth/core/oauth/providers/github.js",
37
+ "./auth/core/oauth/providers/kakao": "./dist/auth/core/oauth/providers/kakao.js",
33
38
  "./auth/adapters/prisma": "./dist/auth/adapters/prisma/index.js",
34
39
  "./cache": "./dist/cache/index.js",
35
40
  "./cache/cache": "./dist/cache/cache.js",
@@ -1,200 +0,0 @@
1
- import {
2
- OAuthError
3
- } from "./chunk-AIH3F7JV.js";
4
-
5
- // src/auth/core/oauth/index.ts
6
- var OAuthManager = class {
7
- constructor(config, logger) {
8
- this.config = config;
9
- this.logger = logger;
10
- }
11
- /**
12
- * OAuth 로그인 URL 생성
13
- */
14
- getLoginUrl(provider, state) {
15
- if (provider === "google" /* GOOGLE */) {
16
- return this.getGoogleLoginUrl(state);
17
- } else if (provider === "github" /* GITHUB */) {
18
- return this.getGitHubLoginUrl(state);
19
- }
20
- throw new OAuthError(`Unsupported OAuth provider: ${provider}`, "UNSUPPORTED_PROVIDER");
21
- }
22
- /**
23
- * Google OAuth 로그인 URL
24
- */
25
- getGoogleLoginUrl(state) {
26
- if (!this.config.google) {
27
- throw new OAuthError("Google OAuth not configured", "GOOGLE_NOT_CONFIGURED");
28
- }
29
- const params = new URLSearchParams({
30
- client_id: this.config.google.clientId,
31
- redirect_uri: this.config.google.redirectUri,
32
- response_type: "code",
33
- scope: "openid email profile",
34
- state,
35
- access_type: "offline",
36
- prompt: "select_account consent"
37
- });
38
- return `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
39
- }
40
- /**
41
- * GitHub OAuth 로그인 URL
42
- */
43
- getGitHubLoginUrl(state) {
44
- if (!this.config.github) {
45
- throw new OAuthError("GitHub OAuth not configured", "GITHUB_NOT_CONFIGURED");
46
- }
47
- const params = new URLSearchParams({
48
- client_id: this.config.github.clientId,
49
- redirect_uri: this.config.github.redirectUri,
50
- scope: "read:user user:email",
51
- state
52
- });
53
- return `https://github.com/login/oauth/authorize?${params.toString()}`;
54
- }
55
- /**
56
- * 인증 코드를 액세스 토큰으로 교환
57
- */
58
- async exchangeCodeForToken(provider, code) {
59
- if (provider === "google" /* GOOGLE */) {
60
- return this.exchangeGoogleCode(code);
61
- } else if (provider === "github" /* GITHUB */) {
62
- return this.exchangeGitHubCode(code);
63
- }
64
- throw new OAuthError(`Unsupported OAuth provider: ${provider}`, "UNSUPPORTED_PROVIDER");
65
- }
66
- /**
67
- * Google 코드 교환
68
- */
69
- async exchangeGoogleCode(code) {
70
- if (!this.config.google) {
71
- throw new OAuthError("Google OAuth not configured", "GOOGLE_NOT_CONFIGURED");
72
- }
73
- try {
74
- const response = await fetch("https://oauth2.googleapis.com/token", {
75
- method: "POST",
76
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
77
- body: new URLSearchParams({
78
- client_id: this.config.google.clientId,
79
- client_secret: this.config.google.clientSecret,
80
- code,
81
- grant_type: "authorization_code",
82
- redirect_uri: this.config.google.redirectUri
83
- })
84
- });
85
- if (!response.ok) {
86
- const errorData = await response.text();
87
- this.logger.error("Google token exchange failed", { status: response.status, errorData });
88
- throw new OAuthError("Failed to exchange Google code for token", "TOKEN_EXCHANGE_FAILED");
89
- }
90
- const data = await response.json();
91
- this.logger.debug("Google token exchange successful");
92
- return data.access_token;
93
- } catch (error) {
94
- this.logger.error("Google token exchange error", { error: error.message });
95
- throw new OAuthError("Google token exchange failed", "TOKEN_EXCHANGE_FAILED");
96
- }
97
- }
98
- /**
99
- * GitHub 코드 교환
100
- */
101
- async exchangeGitHubCode(code) {
102
- if (!this.config.github) {
103
- throw new OAuthError("GitHub OAuth not configured", "GITHUB_NOT_CONFIGURED");
104
- }
105
- try {
106
- const response = await fetch("https://github.com/login/oauth/access_token", {
107
- method: "POST",
108
- headers: {
109
- "Content-Type": "application/json",
110
- Accept: "application/json"
111
- },
112
- body: JSON.stringify({
113
- client_id: this.config.github.clientId,
114
- client_secret: this.config.github.clientSecret,
115
- code,
116
- redirect_uri: this.config.github.redirectUri
117
- })
118
- });
119
- if (!response.ok) {
120
- const errorData = await response.text();
121
- this.logger.error("GitHub token exchange failed", { status: response.status, errorData });
122
- throw new OAuthError("Failed to exchange GitHub code for token", "TOKEN_EXCHANGE_FAILED");
123
- }
124
- const data = await response.json();
125
- this.logger.debug("GitHub token exchange successful");
126
- return data.access_token;
127
- } catch (error) {
128
- this.logger.error("GitHub token exchange error", { error: error.message });
129
- throw new OAuthError("GitHub token exchange failed", "TOKEN_EXCHANGE_FAILED");
130
- }
131
- }
132
- /**
133
- * 사용자 정보 가져오기
134
- */
135
- async getUserInfo(provider, accessToken) {
136
- if (provider === "google" /* GOOGLE */) {
137
- return this.getGoogleUserInfo(accessToken);
138
- } else if (provider === "github" /* GITHUB */) {
139
- return this.getGitHubUserInfo(accessToken);
140
- }
141
- throw new OAuthError(`Unsupported OAuth provider: ${provider}`, "UNSUPPORTED_PROVIDER");
142
- }
143
- /**
144
- * Google 사용자 정보
145
- */
146
- async getGoogleUserInfo(accessToken) {
147
- try {
148
- const response = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
149
- headers: { Authorization: `Bearer ${accessToken}` }
150
- });
151
- if (!response.ok) {
152
- throw new OAuthError("Failed to get Google user info", "USER_INFO_FAILED");
153
- }
154
- const data = await response.json();
155
- this.logger.debug("Google user info retrieved", { userId: data.id, email: data.email });
156
- return {
157
- id: data.id,
158
- email: data.email,
159
- name: data.name || null,
160
- image: data.picture || null,
161
- emailVerified: data.verified_email || false
162
- };
163
- } catch (error) {
164
- this.logger.error("Google user info error", { error: error.message });
165
- throw new OAuthError("Failed to retrieve Google user info", "USER_INFO_FAILED");
166
- }
167
- }
168
- /**
169
- * GitHub 사용자 정보
170
- */
171
- async getGitHubUserInfo(accessToken) {
172
- try {
173
- const response = await fetch("https://api.github.com/user", {
174
- headers: {
175
- Authorization: `Bearer ${accessToken}`,
176
- "User-Agent": "Auth-Module"
177
- }
178
- });
179
- if (!response.ok) {
180
- throw new OAuthError("Failed to get GitHub user info", "USER_INFO_FAILED");
181
- }
182
- const data = await response.json();
183
- this.logger.debug("GitHub user info retrieved", { userId: data.id, login: data.login });
184
- return {
185
- id: data.id.toString(),
186
- email: data.email,
187
- name: data.name || null,
188
- image: data.avatar_url || null,
189
- emailVerified: !!data.email
190
- };
191
- } catch (error) {
192
- this.logger.error("GitHub user info error", { error: error.message });
193
- throw new OAuthError("Failed to retrieve GitHub user info", "USER_INFO_FAILED");
194
- }
195
- }
196
- };
197
-
198
- export {
199
- OAuthManager
200
- };