@withwiz/toolkit 0.2.2 → 0.2.4
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.
- package/dist/auth/core/jwt/index.js +1 -1
- package/dist/auth/index.js +20 -20
- package/dist/{chunk-H5I5GWAA.js → chunk-6DD3MUDZ.js} +2 -2
- package/dist/{chunk-2KAPUVIS.js → chunk-7JWIX5BS.js} +1 -1
- package/dist/{chunk-N4YGR5WH.js → chunk-HCF3C3AQ.js} +11 -10
- package/dist/{chunk-3DZA6AGS.js → chunk-IJCVFLEY.js} +11 -4
- package/dist/{chunk-KXAWBFJN.js → chunk-IYXMGIEP.js} +11 -10
- package/dist/{chunk-MLGO3HLS.js → chunk-KXXROUA5.js} +20 -5
- package/dist/{chunk-XPASCCUA.js → chunk-M4VXPTKV.js} +2 -2
- package/dist/{chunk-TMVS4F7E.js → chunk-QYIUGQ5Q.js} +32 -7
- package/dist/{chunk-POKGHK3L.js → chunk-S54PFAEU.js} +2 -2
- package/dist/{chunk-Y3OTJH2S.js → chunk-SNVGJPPX.js} +36 -13
- package/dist/{chunk-T3LJYAMO.js → chunk-UT4J7NPS.js} +1 -1
- package/dist/{chunk-UXQRU3EM.js → chunk-V4KAPU2S.js} +3 -3
- package/dist/{chunk-GPBOMJSZ.js → chunk-WDRDGFHO.js} +74 -16
- package/dist/{chunk-SLG26KHZ.js → chunk-WTRBW2UY.js} +3 -3
- package/dist/{chunk-EEUBKZV4.js → chunk-Y2JYA2AR.js} +2 -2
- package/dist/constants/error-codes.d.ts +19 -12
- package/dist/constants/error-codes.js +3 -1
- package/dist/constants/index.js +3 -1
- package/dist/error/app-error.d.ts +1 -0
- package/dist/error/app-error.js +2 -2
- package/dist/error/error-display.d.ts +1 -1
- package/dist/error/error-display.js +7 -3
- package/dist/error/error-handler.d.ts +51 -12
- package/dist/error/error-handler.js +6 -3
- package/dist/error/friendly-messages-v2.js +5 -5
- package/dist/error/friendly-messages.d.ts +3 -0
- package/dist/error/friendly-messages.js +113 -5
- package/dist/error/index.js +22 -20
- package/dist/error/messages/en.js +1 -1
- package/dist/error/messages/index.js +3 -3
- package/dist/error/messages/ko.js +1 -1
- package/dist/middleware/auth.js +4 -4
- package/dist/middleware/error-handler.js +8 -6
- package/dist/middleware/index.js +11 -10
- package/dist/middleware/rate-limit.js +3 -3
- package/dist/middleware/wrappers.js +11 -10
- package/dist/storage/r2-storage.js +7 -3
- package/dist/utils/api-helpers.js +2 -2
- package/dist/utils/error-processor.js +3 -3
- package/dist/utils/index.js +4 -4
- package/dist/utils/input-validation.js +4 -4
- package/dist/utils/optimistic-lock.js +2 -2
- package/package.json +2 -1
- package/dist/chunk-2DVWSDST.js +0 -119
package/dist/auth/index.js
CHANGED
|
@@ -1,25 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
JWTClientManager,
|
|
3
|
-
clearStoredTokens,
|
|
4
|
-
clearTokens,
|
|
5
|
-
createApiHeaders,
|
|
6
|
-
createAuthHeader,
|
|
7
|
-
decodeJWTPayload,
|
|
8
|
-
extractUserFromToken,
|
|
9
|
-
getStoredTokens,
|
|
10
|
-
getTokenExpirationString,
|
|
11
|
-
getTokenIssuedAtString,
|
|
12
|
-
getTokenRemainingTime,
|
|
13
|
-
isTokenExpired,
|
|
14
|
-
isTokenExpiringSoon,
|
|
15
|
-
storeTokens
|
|
16
|
-
} from "../chunk-XRRPEBKB.js";
|
|
17
1
|
import {
|
|
18
2
|
OAuthManager
|
|
19
3
|
} from "../chunk-V5K5FYU7.js";
|
|
20
|
-
import {
|
|
21
|
-
TokenGenerator
|
|
22
|
-
} from "../chunk-GDWEDUHO.js";
|
|
23
4
|
import {
|
|
24
5
|
DEFAULT_PASSWORD_CONFIG,
|
|
25
6
|
createPasswordHasher,
|
|
@@ -35,6 +16,25 @@ import {
|
|
|
35
16
|
defaultPasswordSchema,
|
|
36
17
|
strongPasswordSchema
|
|
37
18
|
} from "../chunk-IHXRF3BH.js";
|
|
19
|
+
import {
|
|
20
|
+
TokenGenerator
|
|
21
|
+
} from "../chunk-GDWEDUHO.js";
|
|
22
|
+
import {
|
|
23
|
+
JWTClientManager,
|
|
24
|
+
clearStoredTokens,
|
|
25
|
+
clearTokens,
|
|
26
|
+
createApiHeaders,
|
|
27
|
+
createAuthHeader,
|
|
28
|
+
decodeJWTPayload,
|
|
29
|
+
extractUserFromToken,
|
|
30
|
+
getStoredTokens,
|
|
31
|
+
getTokenExpirationString,
|
|
32
|
+
getTokenIssuedAtString,
|
|
33
|
+
getTokenRemainingTime,
|
|
34
|
+
isTokenExpired,
|
|
35
|
+
isTokenExpiringSoon,
|
|
36
|
+
storeTokens
|
|
37
|
+
} from "../chunk-XRRPEBKB.js";
|
|
38
38
|
import {
|
|
39
39
|
OAuthProvider,
|
|
40
40
|
PasswordStrength,
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
import {
|
|
45
45
|
JWTManager,
|
|
46
46
|
JWTService
|
|
47
|
-
} from "../chunk-
|
|
47
|
+
} from "../chunk-UT4J7NPS.js";
|
|
48
48
|
import {
|
|
49
49
|
AUTH_ERROR_CODES,
|
|
50
50
|
AuthError,
|
|
@@ -61,6 +61,16 @@ var enMessages = {
|
|
|
61
61
|
description: "Your session has expired for security.",
|
|
62
62
|
action: "Please login again."
|
|
63
63
|
},
|
|
64
|
+
40104: {
|
|
65
|
+
title: "Password required",
|
|
66
|
+
description: "You must enter a password.",
|
|
67
|
+
action: "Please enter the password."
|
|
68
|
+
},
|
|
69
|
+
40105: {
|
|
70
|
+
title: "Incorrect password",
|
|
71
|
+
description: "The password is incorrect.",
|
|
72
|
+
action: "Please check again."
|
|
73
|
+
},
|
|
64
74
|
40106: {
|
|
65
75
|
title: "Login failed",
|
|
66
76
|
description: "Email or password is incorrect.",
|
|
@@ -173,16 +183,7 @@ var enMessages = {
|
|
|
173
183
|
description: "Usage has been stopped.",
|
|
174
184
|
action: "Please contact the owner."
|
|
175
185
|
},
|
|
176
|
-
42206:
|
|
177
|
-
title: "Password required",
|
|
178
|
-
description: "You must enter a password.",
|
|
179
|
-
action: "Please enter the password."
|
|
180
|
-
},
|
|
181
|
-
42207: {
|
|
182
|
-
title: "Incorrect password",
|
|
183
|
-
description: "The password is incorrect.",
|
|
184
|
-
action: "Please check again."
|
|
185
|
-
},
|
|
186
|
+
// 42206, 42207: moved to 401xx → see 40104, 40105 below
|
|
186
187
|
42208: {
|
|
187
188
|
title: "Alias not allowed",
|
|
188
189
|
description: "This is a system reserved word.",
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getErrorDisplayInfo,
|
|
3
3
|
getFriendlyMessage
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-S54PFAEU.js";
|
|
5
|
+
import {
|
|
6
|
+
AppError
|
|
7
|
+
} from "./chunk-KXXROUA5.js";
|
|
5
8
|
import {
|
|
6
9
|
getErrorCategory
|
|
7
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-SNVGJPPX.js";
|
|
8
11
|
|
|
9
12
|
// src/error/error-display.ts
|
|
10
13
|
import { toast } from "sonner";
|
|
@@ -51,7 +54,7 @@ function showFriendlyError(errorOrCode, options) {
|
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
async function handleApiResponse(response, options) {
|
|
54
|
-
var _a;
|
|
57
|
+
var _a, _b;
|
|
55
58
|
if (response.ok) {
|
|
56
59
|
const data = await response.json();
|
|
57
60
|
return data.data;
|
|
@@ -63,7 +66,11 @@ async function handleApiResponse(response, options) {
|
|
|
63
66
|
onAuthError: options == null ? void 0 : options.onAuthError
|
|
64
67
|
});
|
|
65
68
|
}
|
|
66
|
-
|
|
69
|
+
const errorCode = (_a = errorData.error) == null ? void 0 : _a.code;
|
|
70
|
+
if (errorCode) {
|
|
71
|
+
throw new AppError(errorCode, (_b = errorData.error) == null ? void 0 : _b.message);
|
|
72
|
+
}
|
|
73
|
+
throw AppError.fromKey("SERVER_ERROR", `Request failed with status ${response.status}`);
|
|
67
74
|
}
|
|
68
75
|
function formatInlineError(code, locale) {
|
|
69
76
|
const lang = locale || getLocale();
|
|
@@ -61,6 +61,16 @@ var koMessages = {
|
|
|
61
61
|
description: "\uBCF4\uC548\uC744 \uC704\uD574 \uC138\uC158\uC774 \uB9CC\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
|
|
62
62
|
action: "\uB2E4\uC2DC \uB85C\uADF8\uC778\uD574 \uC8FC\uC138\uC694."
|
|
63
63
|
},
|
|
64
|
+
40104: {
|
|
65
|
+
title: "\uBE44\uBC00\uBC88\uD638\uAC00 \uD544\uC694\uD574\uC694",
|
|
66
|
+
description: "\uBE44\uBC00\uBC88\uD638\uB97C \uC785\uB825\uD574\uC57C \uD569\uB2C8\uB2E4.",
|
|
67
|
+
action: "\uBE44\uBC00\uBC88\uD638\uB97C \uC785\uB825\uD574 \uC8FC\uC138\uC694."
|
|
68
|
+
},
|
|
69
|
+
40105: {
|
|
70
|
+
title: "\uBE44\uBC00\uBC88\uD638\uAC00 \uD2C0\uB838\uC5B4\uC694",
|
|
71
|
+
description: "\uBE44\uBC00\uBC88\uD638\uAC00 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
72
|
+
action: "\uB2E4\uC2DC \uD655\uC778\uD574 \uC8FC\uC138\uC694."
|
|
73
|
+
},
|
|
64
74
|
40106: {
|
|
65
75
|
title: "\uB85C\uADF8\uC778\uC5D0 \uC2E4\uD328\uD588\uC5B4\uC694",
|
|
66
76
|
description: "\uC774\uBA54\uC77C \uB610\uB294 \uBE44\uBC00\uBC88\uD638\uAC00 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
@@ -173,16 +183,7 @@ var koMessages = {
|
|
|
173
183
|
description: "\uC0AC\uC6A9\uC774 \uC911\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
|
|
174
184
|
action: "\uC18C\uC720\uC790\uC5D0\uAC8C \uBB38\uC758\uD574 \uC8FC\uC138\uC694."
|
|
175
185
|
},
|
|
176
|
-
42206:
|
|
177
|
-
title: "\uBE44\uBC00\uBC88\uD638\uAC00 \uD544\uC694\uD574\uC694",
|
|
178
|
-
description: "\uBE44\uBC00\uBC88\uD638\uB97C \uC785\uB825\uD574\uC57C \uD569\uB2C8\uB2E4.",
|
|
179
|
-
action: "\uBE44\uBC00\uBC88\uD638\uB97C \uC785\uB825\uD574 \uC8FC\uC138\uC694."
|
|
180
|
-
},
|
|
181
|
-
42207: {
|
|
182
|
-
title: "\uBE44\uBC00\uBC88\uD638\uAC00 \uD2C0\uB838\uC5B4\uC694",
|
|
183
|
-
description: "\uBE44\uBC00\uBC88\uD638\uAC00 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
184
|
-
action: "\uB2E4\uC2DC \uD655\uC778\uD574 \uC8FC\uC138\uC694."
|
|
185
|
-
},
|
|
186
|
+
// 42206, 42207: 401xx로 이동 → 아래 40104, 40105 참조
|
|
186
187
|
42208: {
|
|
187
188
|
title: "\uC0AC\uC6A9\uD560 \uC218 \uC5C6\uB294 \uBCC4\uCE6D\uC774\uC5D0\uC694",
|
|
188
189
|
description: "\uC2DC\uC2A4\uD15C \uC608\uC57D\uC5B4\uC785\uB2C8\uB2E4.",
|
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ERROR_CODES,
|
|
3
|
+
classifyError,
|
|
3
4
|
formatErrorMessage,
|
|
4
5
|
getErrorCategory,
|
|
5
6
|
getHttpStatus
|
|
6
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-SNVGJPPX.js";
|
|
7
8
|
|
|
8
9
|
// src/error/app-error.ts
|
|
9
10
|
var AppError = class _AppError extends Error {
|
|
10
11
|
constructor(code, message, details, requestId) {
|
|
11
12
|
const formattedMessage = formatErrorMessage(code, message);
|
|
12
13
|
super(formattedMessage);
|
|
14
|
+
if (!Number.isInteger(code) || code < 1e4 || code > 59999) {
|
|
15
|
+
const safeCode = ERROR_CODES.INTERNAL_SERVER_ERROR.code;
|
|
16
|
+
this.name = "AppError";
|
|
17
|
+
this.code = safeCode;
|
|
18
|
+
this.status = getHttpStatus(safeCode);
|
|
19
|
+
this.key = "INTERNAL_SERVER_ERROR";
|
|
20
|
+
this.category = getErrorCategory(safeCode);
|
|
21
|
+
this.details = details;
|
|
22
|
+
this.timestamp = /* @__PURE__ */ new Date();
|
|
23
|
+
this.requestId = requestId;
|
|
24
|
+
Object.setPrototypeOf(this, _AppError.prototype);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
13
27
|
this.name = "AppError";
|
|
14
28
|
this.code = code;
|
|
15
29
|
this.status = getHttpStatus(code);
|
|
@@ -97,10 +111,8 @@ var AppError = class _AppError extends Error {
|
|
|
97
111
|
const code = parseInt(codeMatch[1], 10);
|
|
98
112
|
return new _AppError(code, error.message.replace(/\s*\[\d{5}\]$/, ""));
|
|
99
113
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
fallbackMessage || ERROR_CODES.SERVER_ERROR.message
|
|
103
|
-
);
|
|
114
|
+
const classified = classifyError(error);
|
|
115
|
+
return new _AppError(classified.code, fallbackMessage || classified.message);
|
|
104
116
|
}
|
|
105
117
|
return new _AppError(
|
|
106
118
|
ERROR_CODES.INTERNAL_SERVER_ERROR.code,
|
|
@@ -320,6 +332,9 @@ var AppError = class _AppError extends Error {
|
|
|
320
332
|
static ipBlocked(ip) {
|
|
321
333
|
return _AppError.fromKey("IP_BLOCKED", void 0, { value: ip });
|
|
322
334
|
}
|
|
335
|
+
static corsViolation(origin) {
|
|
336
|
+
return _AppError.fromKey("CORS_VIOLATION", void 0, { value: origin });
|
|
337
|
+
}
|
|
323
338
|
};
|
|
324
339
|
var app_error_default = AppError;
|
|
325
340
|
|
|
@@ -1,12 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AUTH_ERROR_CODE_MAP
|
|
3
|
+
} from "./chunk-WDRDGFHO.js";
|
|
1
4
|
import {
|
|
2
5
|
getErrorMessage
|
|
3
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-Y2JYA2AR.js";
|
|
4
7
|
import {
|
|
5
8
|
AppError
|
|
6
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-KXXROUA5.js";
|
|
10
|
+
import {
|
|
11
|
+
ERROR_CODES,
|
|
12
|
+
classifyError
|
|
13
|
+
} from "./chunk-SNVGJPPX.js";
|
|
7
14
|
import {
|
|
8
|
-
|
|
9
|
-
} from "./chunk-
|
|
15
|
+
AuthError
|
|
16
|
+
} from "./chunk-AIH3F7JV.js";
|
|
10
17
|
import {
|
|
11
18
|
logger
|
|
12
19
|
} from "./chunk-EZR55KV2.js";
|
|
@@ -46,15 +53,33 @@ var errorHandlerMiddleware = async (context, next) => {
|
|
|
46
53
|
{ issues }
|
|
47
54
|
);
|
|
48
55
|
}
|
|
56
|
+
if (error instanceof AuthError) {
|
|
57
|
+
const mappedCode = AUTH_ERROR_CODE_MAP[error.code];
|
|
58
|
+
if (mappedCode) {
|
|
59
|
+
return createErrorResponse(
|
|
60
|
+
mappedCode,
|
|
61
|
+
error.message,
|
|
62
|
+
context.locale,
|
|
63
|
+
context.requestId
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
const fallbackCode = error.statusCode === 401 ? ERROR_CODES.UNAUTHORIZED.code : ERROR_CODES.BAD_REQUEST.code;
|
|
67
|
+
return createErrorResponse(
|
|
68
|
+
fallbackCode,
|
|
69
|
+
error.message,
|
|
70
|
+
context.locale,
|
|
71
|
+
context.requestId
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
const classified = error instanceof Error ? classifyError(error) : ERROR_CODES.INTERNAL_SERVER_ERROR;
|
|
49
75
|
logger.error("[ErrorHandler] Unexpected error:", error);
|
|
50
76
|
const isProduction = process.env.NODE_ENV === "production";
|
|
51
|
-
const safeMessage = isProduction ?
|
|
77
|
+
const safeMessage = isProduction ? classified.message : error instanceof Error ? error.message : "Unknown error";
|
|
52
78
|
return createErrorResponse(
|
|
53
|
-
|
|
79
|
+
classified.code,
|
|
54
80
|
safeMessage,
|
|
55
81
|
context.locale,
|
|
56
82
|
context.requestId,
|
|
57
|
-
// 개발 환경에서만 스택 트레이스 포함
|
|
58
83
|
!isProduction && error instanceof Error ? { stack: (_b = error.stack) == null ? void 0 : _b.split("\n").slice(0, 5) } : void 0
|
|
59
84
|
);
|
|
60
85
|
}
|
|
@@ -92,6 +92,18 @@ var ERROR_CODES = {
|
|
|
92
92
|
status: HTTP_STATUS.UNAUTHORIZED,
|
|
93
93
|
message: "Invalid email or password."
|
|
94
94
|
},
|
|
95
|
+
LINK_PASSWORD_REQUIRED: {
|
|
96
|
+
code: 40104,
|
|
97
|
+
key: "LINK_PASSWORD_REQUIRED",
|
|
98
|
+
status: HTTP_STATUS.UNAUTHORIZED,
|
|
99
|
+
message: "Link password is required."
|
|
100
|
+
},
|
|
101
|
+
LINK_PASSWORD_INCORRECT: {
|
|
102
|
+
code: 40105,
|
|
103
|
+
key: "LINK_PASSWORD_INCORRECT",
|
|
104
|
+
status: HTTP_STATUS.UNAUTHORIZED,
|
|
105
|
+
message: "Incorrect link password."
|
|
106
|
+
},
|
|
95
107
|
SESSION_EXPIRED: {
|
|
96
108
|
code: 40107,
|
|
97
109
|
key: "SESSION_EXPIRED",
|
|
@@ -100,6 +112,7 @@ var ERROR_CODES = {
|
|
|
100
112
|
},
|
|
101
113
|
// ============================================
|
|
102
114
|
// Permissions Related (403xx - 01~09)
|
|
115
|
+
// Reserved: 40301~40303 (미사용, 향후 세분화 확장용)
|
|
103
116
|
// ============================================
|
|
104
117
|
FORBIDDEN: {
|
|
105
118
|
code: 40304,
|
|
@@ -113,6 +126,7 @@ var ERROR_CODES = {
|
|
|
113
126
|
status: HTTP_STATUS.FORBIDDEN,
|
|
114
127
|
message: "Email verification required."
|
|
115
128
|
},
|
|
129
|
+
// Reserved: 40306~40307 (미사용, 향후 확장용)
|
|
116
130
|
ACCOUNT_DISABLED: {
|
|
117
131
|
code: 40308,
|
|
118
132
|
key: "ACCOUNT_DISABLED",
|
|
@@ -127,6 +141,7 @@ var ERROR_CODES = {
|
|
|
127
141
|
},
|
|
128
142
|
// ============================================
|
|
129
143
|
// Resource Not Found (404xx)
|
|
144
|
+
// Reserved: 40404~40407 (미사용, 향후 리소스 타입 확장용)
|
|
130
145
|
// ============================================
|
|
131
146
|
NOT_FOUND: {
|
|
132
147
|
code: 40401,
|
|
@@ -166,6 +181,7 @@ var ERROR_CODES = {
|
|
|
166
181
|
},
|
|
167
182
|
// ============================================
|
|
168
183
|
// Conflict (409xx)
|
|
184
|
+
// Reserved: 40901~40903 (미사용, 향후 확장용)
|
|
169
185
|
// ============================================
|
|
170
186
|
CONFLICT: {
|
|
171
187
|
code: 40904,
|
|
@@ -224,18 +240,8 @@ var ERROR_CODES = {
|
|
|
224
240
|
status: HTTP_STATUS.UNPROCESSABLE_ENTITY,
|
|
225
241
|
message: "Link is inactive."
|
|
226
242
|
},
|
|
227
|
-
LINK_PASSWORD_REQUIRED:
|
|
228
|
-
|
|
229
|
-
key: "LINK_PASSWORD_REQUIRED",
|
|
230
|
-
status: HTTP_STATUS.UNPROCESSABLE_ENTITY,
|
|
231
|
-
message: "Link password is required."
|
|
232
|
-
},
|
|
233
|
-
LINK_PASSWORD_INCORRECT: {
|
|
234
|
-
code: 42207,
|
|
235
|
-
key: "LINK_PASSWORD_INCORRECT",
|
|
236
|
-
status: HTTP_STATUS.UNPROCESSABLE_ENTITY,
|
|
237
|
-
message: "Incorrect link password."
|
|
238
|
-
},
|
|
243
|
+
// LINK_PASSWORD_REQUIRED: 401xx로 이동 (40104)
|
|
244
|
+
// LINK_PASSWORD_INCORRECT: 401xx로 이동 (40105)
|
|
239
245
|
RESERVED_WORD_USED: {
|
|
240
246
|
code: 42208,
|
|
241
247
|
key: "RESERVED_WORD_USED",
|
|
@@ -308,6 +314,7 @@ var ERROR_CODES = {
|
|
|
308
314
|
status: HTTP_STATUS.INTERNAL_SERVER_ERROR,
|
|
309
315
|
message: "An error occurred during data processing."
|
|
310
316
|
},
|
|
317
|
+
// Reserved: 50004~50005 (미사용, 향후 서버 에러 세분화용)
|
|
311
318
|
EMAIL_SEND_FAILED: {
|
|
312
319
|
code: 50006,
|
|
313
320
|
key: "EMAIL_SEND_FAILED",
|
|
@@ -328,6 +335,7 @@ var ERROR_CODES = {
|
|
|
328
335
|
},
|
|
329
336
|
// ============================================
|
|
330
337
|
// Service Unavailable (503xx)
|
|
338
|
+
// Reserved: 50301~50303 (미사용, 향후 확장용)
|
|
331
339
|
// ============================================
|
|
332
340
|
EXTERNAL_SERVICE_ERROR: {
|
|
333
341
|
code: 50304,
|
|
@@ -457,6 +465,20 @@ function formatErrorMessage(code, customMessage) {
|
|
|
457
465
|
const errorInfo = getErrorByCode(code);
|
|
458
466
|
return customMessage || (errorInfo == null ? void 0 : errorInfo.message) || getDefaultErrorMessage(getHttpStatus(code));
|
|
459
467
|
}
|
|
468
|
+
function classifyError(error) {
|
|
469
|
+
const msg = error.message.toLowerCase();
|
|
470
|
+
const errCode = error.code;
|
|
471
|
+
if (msg.includes("not found")) return ERROR_CODES.NOT_FOUND;
|
|
472
|
+
if (msg.includes("unauthorized")) return ERROR_CODES.UNAUTHORIZED;
|
|
473
|
+
if (msg.includes("forbidden") || msg.includes("access denied")) return ERROR_CODES.FORBIDDEN;
|
|
474
|
+
if (msg.includes("too many request") || msg.includes("rate limit")) return ERROR_CODES.RATE_LIMIT_EXCEEDED;
|
|
475
|
+
if (error.message.match(/P\d{4}/) || msg.includes("database") || msg.includes("prisma")) return ERROR_CODES.DATABASE_ERROR;
|
|
476
|
+
if (errCode === "ECONNREFUSED" || errCode === "ECONNRESET" || errCode === "ETIMEDOUT" || errCode === "ENOTFOUND" || msg.includes("fetch failed") || msg.includes("network")) return ERROR_CODES.EXTERNAL_SERVICE_ERROR;
|
|
477
|
+
if (msg.includes("redis") || msg.includes("cache") || msg.includes("upstash")) return ERROR_CODES.CACHE_ERROR;
|
|
478
|
+
if (msg.includes("email") && (msg.includes("send") || msg.includes("smtp"))) return ERROR_CODES.EMAIL_SEND_FAILED;
|
|
479
|
+
if (msg.includes("upload") || msg.includes("s3") || msg.includes("r2")) return ERROR_CODES.FILE_UPLOAD_FAILED;
|
|
480
|
+
return ERROR_CODES.SERVER_ERROR;
|
|
481
|
+
}
|
|
460
482
|
|
|
461
483
|
export {
|
|
462
484
|
HTTP_STATUS,
|
|
@@ -469,5 +491,6 @@ export {
|
|
|
469
491
|
getLogLevel,
|
|
470
492
|
getAllErrorCodes,
|
|
471
493
|
getErrorCodesByCategory,
|
|
472
|
-
formatErrorMessage
|
|
494
|
+
formatErrorMessage,
|
|
495
|
+
classifyError
|
|
473
496
|
};
|
|
@@ -10,7 +10,7 @@ var JWTManager = class {
|
|
|
10
10
|
this.logger = logger;
|
|
11
11
|
this.secretKey = new TextEncoder().encode(config.secret);
|
|
12
12
|
if (config.secret.length < 32) {
|
|
13
|
-
throw new
|
|
13
|
+
throw new JWTError("JWT secret must be at least 32 characters long", "TOKEN_CREATION_FAILED");
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AppError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-KXXROUA5.js";
|
|
4
4
|
import {
|
|
5
5
|
ERROR_CODES
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-SNVGJPPX.js";
|
|
7
7
|
import {
|
|
8
8
|
JWTManager
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-UT4J7NPS.js";
|
|
10
10
|
import {
|
|
11
11
|
logger
|
|
12
12
|
} from "./chunk-EZR55KV2.js";
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AppError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-KXXROUA5.js";
|
|
4
4
|
import {
|
|
5
5
|
ERROR_CODES,
|
|
6
|
+
classifyError,
|
|
6
7
|
formatErrorMessage,
|
|
7
8
|
getHttpStatus
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-SNVGJPPX.js";
|
|
10
|
+
import {
|
|
11
|
+
AuthError
|
|
12
|
+
} from "./chunk-AIH3F7JV.js";
|
|
9
13
|
import {
|
|
10
14
|
logger
|
|
11
15
|
} from "./chunk-EZR55KV2.js";
|
|
@@ -17,6 +21,37 @@ import {
|
|
|
17
21
|
// src/error/error-handler.ts
|
|
18
22
|
import { NextResponse } from "next/server";
|
|
19
23
|
import { z } from "zod";
|
|
24
|
+
var AUTH_ERROR_CODE_MAP = {
|
|
25
|
+
// JWT
|
|
26
|
+
TOKEN_EXPIRED: ERROR_CODES.TOKEN_EXPIRED.code,
|
|
27
|
+
TOKEN_INVALID: ERROR_CODES.INVALID_TOKEN.code,
|
|
28
|
+
TOKEN_CREATION_FAILED: ERROR_CODES.SERVER_ERROR.code,
|
|
29
|
+
TOKEN_VERIFICATION_FAILED: ERROR_CODES.INVALID_TOKEN.code,
|
|
30
|
+
REFRESH_TOKEN_EXPIRED: ERROR_CODES.TOKEN_EXPIRED.code,
|
|
31
|
+
INVALID_PAYLOAD: ERROR_CODES.INVALID_TOKEN.code,
|
|
32
|
+
// OAuth
|
|
33
|
+
OAUTH_PROVIDER_NOT_CONFIGURED: ERROR_CODES.EXTERNAL_SERVICE_ERROR.code,
|
|
34
|
+
OAUTH_TOKEN_EXCHANGE_FAILED: ERROR_CODES.EXTERNAL_SERVICE_ERROR.code,
|
|
35
|
+
OAUTH_USER_INFO_FAILED: ERROR_CODES.EXTERNAL_SERVICE_ERROR.code,
|
|
36
|
+
// Password
|
|
37
|
+
PASSWORD_TOO_SHORT: ERROR_CODES.PASSWORD_TOO_WEAK.code,
|
|
38
|
+
PASSWORD_TOO_LONG: ERROR_CODES.PASSWORD_TOO_WEAK.code,
|
|
39
|
+
PASSWORD_MISSING_NUMBER: ERROR_CODES.PASSWORD_TOO_WEAK.code,
|
|
40
|
+
PASSWORD_MISSING_UPPERCASE: ERROR_CODES.PASSWORD_TOO_WEAK.code,
|
|
41
|
+
PASSWORD_MISSING_LOWERCASE: ERROR_CODES.PASSWORD_TOO_WEAK.code,
|
|
42
|
+
PASSWORD_MISSING_SPECIAL_CHAR: ERROR_CODES.PASSWORD_TOO_WEAK.code,
|
|
43
|
+
PASSWORD_HASH_FAILED: ERROR_CODES.SERVER_ERROR.code,
|
|
44
|
+
PASSWORD_VERIFY_FAILED: ERROR_CODES.SERVER_ERROR.code,
|
|
45
|
+
// Email
|
|
46
|
+
EMAIL_SEND_FAILED: ERROR_CODES.EMAIL_SEND_FAILED.code,
|
|
47
|
+
EMAIL_TOKEN_EXPIRED: ERROR_CODES.TOKEN_EXPIRED.code,
|
|
48
|
+
EMAIL_TOKEN_INVALID: ERROR_CODES.INVALID_TOKEN.code,
|
|
49
|
+
// Generic
|
|
50
|
+
INVALID_CREDENTIALS: ERROR_CODES.INVALID_CREDENTIALS.code,
|
|
51
|
+
USER_NOT_FOUND: ERROR_CODES.USER_NOT_FOUND.code,
|
|
52
|
+
EMAIL_ALREADY_EXISTS: ERROR_CODES.EMAIL_ALREADY_EXISTS.code,
|
|
53
|
+
UNAUTHORIZED: ERROR_CODES.UNAUTHORIZED.code
|
|
54
|
+
};
|
|
20
55
|
var PRISMA_ERROR_MAP = {
|
|
21
56
|
P2000: { code: 40001, message: "\uC785\uB825\uAC12\uC774 \uB108\uBB34 \uAE41\uB2C8\uB2E4." },
|
|
22
57
|
P2001: { code: 40401, message: "\uB808\uCF54\uB4DC\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." },
|
|
@@ -50,6 +85,14 @@ function processError(error) {
|
|
|
50
85
|
details: { issues }
|
|
51
86
|
};
|
|
52
87
|
}
|
|
88
|
+
if (error instanceof AuthError) {
|
|
89
|
+
const mappedCode = AUTH_ERROR_CODE_MAP[error.code];
|
|
90
|
+
if (mappedCode) {
|
|
91
|
+
return { code: mappedCode, message: formatErrorMessage(mappedCode, error.message), status: getHttpStatus(mappedCode) };
|
|
92
|
+
}
|
|
93
|
+
const fallbackCode = error.statusCode === 401 ? ERROR_CODES.UNAUTHORIZED.code : ERROR_CODES.BAD_REQUEST.code;
|
|
94
|
+
return { code: fallbackCode, message: formatErrorMessage(fallbackCode, error.message), status: error.statusCode };
|
|
95
|
+
}
|
|
53
96
|
if (error instanceof Error) {
|
|
54
97
|
const prismaMatch = error.message.match(/P\d{4}/);
|
|
55
98
|
if (prismaMatch) {
|
|
@@ -58,13 +101,8 @@ function processError(error) {
|
|
|
58
101
|
return { code: mapping.code, message: formatErrorMessage(mapping.code, mapping.message), status: getHttpStatus(mapping.code) };
|
|
59
102
|
}
|
|
60
103
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
if (error.message.toLowerCase().includes("unauthorized")) {
|
|
65
|
-
return { code: ERROR_CODES.UNAUTHORIZED.code, message: formatErrorMessage(ERROR_CODES.UNAUTHORIZED.code), status: 401 };
|
|
66
|
-
}
|
|
67
|
-
return { code: ERROR_CODES.SERVER_ERROR.code, message: formatErrorMessage(ERROR_CODES.SERVER_ERROR.code), status: 500 };
|
|
104
|
+
const classified = classifyError(error);
|
|
105
|
+
return { code: classified.code, message: formatErrorMessage(classified.code), status: classified.status };
|
|
68
106
|
}
|
|
69
107
|
return { code: ERROR_CODES.INTERNAL_SERVER_ERROR.code, message: formatErrorMessage(ERROR_CODES.INTERNAL_SERVER_ERROR.code), status: 500 };
|
|
70
108
|
}
|
|
@@ -81,13 +119,18 @@ var ErrorResponse = {
|
|
|
81
119
|
// 400xx
|
|
82
120
|
validation: (message, details) => errorToResponse(AppError.validation(message, details ? __spreadValues({}, details) : void 0)),
|
|
83
121
|
badRequest: (message) => errorToResponse(AppError.badRequest(message)),
|
|
122
|
+
invalidInput: (message) => errorToResponse(AppError.invalidInput(message)),
|
|
123
|
+
missingField: (fieldName) => errorToResponse(AppError.missingField(fieldName)),
|
|
84
124
|
invalidUrl: () => errorToResponse(AppError.invalidUrl()),
|
|
85
125
|
invalidEmail: () => errorToResponse(AppError.invalidEmail()),
|
|
126
|
+
weakPassword: () => errorToResponse(AppError.weakPassword()),
|
|
86
127
|
invalidAlias: () => errorToResponse(AppError.invalidAlias()),
|
|
87
128
|
// 401xx
|
|
88
129
|
unauthorized: (message) => errorToResponse(AppError.unauthorized(message)),
|
|
89
130
|
invalidToken: () => errorToResponse(AppError.invalidToken()),
|
|
90
131
|
tokenExpired: () => errorToResponse(AppError.tokenExpired()),
|
|
132
|
+
linkPasswordRequired: () => errorToResponse(AppError.linkPasswordRequired()),
|
|
133
|
+
linkPasswordIncorrect: () => errorToResponse(AppError.linkPasswordIncorrect()),
|
|
91
134
|
invalidCredentials: () => errorToResponse(AppError.invalidCredentials()),
|
|
92
135
|
sessionExpired: () => errorToResponse(AppError.sessionExpired()),
|
|
93
136
|
// 403xx
|
|
@@ -99,6 +142,9 @@ var ErrorResponse = {
|
|
|
99
142
|
notFound: (message) => errorToResponse(AppError.notFound(message)),
|
|
100
143
|
userNotFound: () => errorToResponse(AppError.userNotFound()),
|
|
101
144
|
linkNotFound: () => errorToResponse(AppError.linkNotFound()),
|
|
145
|
+
tagNotFound: () => errorToResponse(AppError.tagNotFound()),
|
|
146
|
+
favoriteNotFound: () => errorToResponse(AppError.favoriteNotFound()),
|
|
147
|
+
groupNotFound: () => errorToResponse(AppError.groupNotFound()),
|
|
102
148
|
// 409xx
|
|
103
149
|
conflict: (message) => errorToResponse(AppError.conflict(message)),
|
|
104
150
|
duplicate: (resource) => errorToResponse(AppError.duplicate(resource)),
|
|
@@ -106,28 +152,40 @@ var ErrorResponse = {
|
|
|
106
152
|
aliasExists: () => errorToResponse(AppError.aliasExists()),
|
|
107
153
|
// 422xx
|
|
108
154
|
businessRule: (message) => errorToResponse(AppError.businessRule(message)),
|
|
155
|
+
invalidOperation: (message) => errorToResponse(AppError.invalidOperation(message)),
|
|
109
156
|
linkExpired: () => errorToResponse(AppError.linkExpired()),
|
|
110
157
|
linkInactive: () => errorToResponse(AppError.linkInactive()),
|
|
111
|
-
linkPasswordRequired: () => errorToResponse(AppError.linkPasswordRequired()),
|
|
112
|
-
linkPasswordIncorrect: () => errorToResponse(AppError.linkPasswordIncorrect()),
|
|
113
158
|
reservedWord: () => errorToResponse(AppError.reservedWord()),
|
|
114
159
|
quotaExceeded: () => errorToResponse(AppError.quotaExceeded()),
|
|
160
|
+
alreadyFavorited: () => errorToResponse(AppError.alreadyFavorited()),
|
|
161
|
+
fileTooLarge: (maxSize) => errorToResponse(AppError.fileTooLarge(maxSize)),
|
|
162
|
+
unsupportedFileType: (fileType) => errorToResponse(AppError.unsupportedFileType(fileType)),
|
|
115
163
|
// 429xx
|
|
116
164
|
rateLimit: () => errorToResponse(AppError.rateLimit()),
|
|
117
165
|
dailyLimit: () => errorToResponse(AppError.dailyLimit()),
|
|
166
|
+
apiQuotaExceeded: () => errorToResponse(AppError.apiQuotaExceeded()),
|
|
118
167
|
// 500xx
|
|
119
168
|
serverError: (message) => errorToResponse(AppError.serverError(message)),
|
|
120
|
-
|
|
169
|
+
internalError: (message) => errorToResponse(AppError.internalError(message)),
|
|
170
|
+
databaseError: (message) => errorToResponse(AppError.databaseError(message)),
|
|
171
|
+
emailSendFailed: () => errorToResponse(AppError.emailSendFailed()),
|
|
172
|
+
cacheError: () => errorToResponse(AppError.cacheError()),
|
|
173
|
+
fileUploadFailed: () => errorToResponse(AppError.fileUploadFailed()),
|
|
121
174
|
// 503xx
|
|
122
|
-
serviceUnavailable: () => errorToResponse(AppError.serviceUnavailable()),
|
|
175
|
+
serviceUnavailable: (message) => errorToResponse(AppError.serviceUnavailable(message)),
|
|
123
176
|
externalServiceError: (service) => errorToResponse(AppError.externalServiceError(service)),
|
|
124
|
-
// 보안
|
|
125
|
-
accessBlocked: () => errorToResponse(AppError.accessBlocked()),
|
|
126
|
-
|
|
177
|
+
// 보안 (403xx 71~79)
|
|
178
|
+
accessBlocked: (reason) => errorToResponse(AppError.accessBlocked(reason)),
|
|
179
|
+
securityValidationFailed: () => errorToResponse(AppError.securityValidationFailed()),
|
|
180
|
+
blockedUrl: (url) => errorToResponse(AppError.blockedUrl(url)),
|
|
181
|
+
suspiciousActivity: () => errorToResponse(AppError.suspiciousActivity()),
|
|
182
|
+
ipBlocked: (ip) => errorToResponse(AppError.ipBlocked(ip)),
|
|
183
|
+
corsViolation: (origin) => errorToResponse(AppError.corsViolation(origin))
|
|
127
184
|
};
|
|
128
185
|
var error_handler_default = { errorToResponse, processError, withErrorHandler, ErrorResponse };
|
|
129
186
|
|
|
130
187
|
export {
|
|
188
|
+
AUTH_ERROR_CODE_MAP,
|
|
131
189
|
errorToResponse,
|
|
132
190
|
processError,
|
|
133
191
|
withErrorHandler,
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-4ODT3Q4A.js";
|
|
4
4
|
import {
|
|
5
5
|
rateLimitMiddleware
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-6DD3MUDZ.js";
|
|
7
7
|
import {
|
|
8
8
|
responseLoggerMiddleware
|
|
9
9
|
} from "./chunk-KAWVMIRJ.js";
|
|
@@ -14,13 +14,13 @@ import {
|
|
|
14
14
|
adminMiddleware,
|
|
15
15
|
authMiddleware,
|
|
16
16
|
optionalAuthMiddleware
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-V4KAPU2S.js";
|
|
18
18
|
import {
|
|
19
19
|
corsMiddleware
|
|
20
20
|
} from "./chunk-FOKAATUQ.js";
|
|
21
21
|
import {
|
|
22
22
|
errorHandlerMiddleware
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-QYIUGQ5Q.js";
|
|
24
24
|
import {
|
|
25
25
|
initRequestMiddleware
|
|
26
26
|
} from "./chunk-62Q7DN5G.js";
|