@withwiz/cms-kit 0.1.0
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/README.ko.md +146 -0
- package/README.md +146 -0
- package/dist/chunk-2REYWBQ6.js +32 -0
- package/dist/chunk-2REYWBQ6.js.map +1 -0
- package/dist/chunk-2SYUEQ6J.mjs +17 -0
- package/dist/chunk-2SYUEQ6J.mjs.map +1 -0
- package/dist/chunk-3ILEMZAL.mjs +32 -0
- package/dist/chunk-3ILEMZAL.mjs.map +1 -0
- package/dist/chunk-3Y2TQCHU.mjs +33 -0
- package/dist/chunk-3Y2TQCHU.mjs.map +1 -0
- package/dist/chunk-4LSFAAZW.js +1 -0
- package/dist/chunk-4LSFAAZW.js.map +1 -0
- package/dist/chunk-4W3X7OKF.js +100 -0
- package/dist/chunk-4W3X7OKF.js.map +1 -0
- package/dist/chunk-5JUIMXNF.js +108 -0
- package/dist/chunk-5JUIMXNF.js.map +1 -0
- package/dist/chunk-5RDMODGV.mjs +34 -0
- package/dist/chunk-5RDMODGV.mjs.map +1 -0
- package/dist/chunk-6ES4W2DL.mjs +1 -0
- package/dist/chunk-6ES4W2DL.mjs.map +1 -0
- package/dist/chunk-6EZ3QE5N.js +71 -0
- package/dist/chunk-6EZ3QE5N.js.map +1 -0
- package/dist/chunk-6JFERXNE.mjs +16 -0
- package/dist/chunk-6JFERXNE.mjs.map +1 -0
- package/dist/chunk-7SWW3ZIN.js +17 -0
- package/dist/chunk-7SWW3ZIN.js.map +1 -0
- package/dist/chunk-AXR56AUD.mjs +1 -0
- package/dist/chunk-AXR56AUD.mjs.map +1 -0
- package/dist/chunk-BE7JDL64.js +154 -0
- package/dist/chunk-BE7JDL64.js.map +1 -0
- package/dist/chunk-BHN2SRZC.js +51 -0
- package/dist/chunk-BHN2SRZC.js.map +1 -0
- package/dist/chunk-BJTO5JO5.mjs +11 -0
- package/dist/chunk-BJTO5JO5.mjs.map +1 -0
- package/dist/chunk-BWKKHCDG.mjs +164 -0
- package/dist/chunk-BWKKHCDG.mjs.map +1 -0
- package/dist/chunk-C5WFZJM4.js +218 -0
- package/dist/chunk-C5WFZJM4.js.map +1 -0
- package/dist/chunk-CGVFB23E.mjs +123 -0
- package/dist/chunk-CGVFB23E.mjs.map +1 -0
- package/dist/chunk-D2VDTRXO.js +1 -0
- package/dist/chunk-D2VDTRXO.js.map +1 -0
- package/dist/chunk-D75ADHVM.mjs +181 -0
- package/dist/chunk-D75ADHVM.mjs.map +1 -0
- package/dist/chunk-DCIF4XJL.js +309 -0
- package/dist/chunk-DCIF4XJL.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-DSORCGJK.js +44 -0
- package/dist/chunk-DSORCGJK.js.map +1 -0
- package/dist/chunk-E5YUAOJI.js +27 -0
- package/dist/chunk-E5YUAOJI.js.map +1 -0
- package/dist/chunk-EG6NVPP2.js +164 -0
- package/dist/chunk-EG6NVPP2.js.map +1 -0
- package/dist/chunk-EL7YGOTY.js +1 -0
- package/dist/chunk-EL7YGOTY.js.map +1 -0
- package/dist/chunk-EM4MQEWV.mjs +100 -0
- package/dist/chunk-EM4MQEWV.mjs.map +1 -0
- package/dist/chunk-FCCCG5Z7.mjs +108 -0
- package/dist/chunk-FCCCG5Z7.mjs.map +1 -0
- package/dist/chunk-FEPGJ7WN.mjs +218 -0
- package/dist/chunk-FEPGJ7WN.mjs.map +1 -0
- package/dist/chunk-HXVWBFCN.js +16 -0
- package/dist/chunk-HXVWBFCN.js.map +1 -0
- package/dist/chunk-I5YMTJV6.mjs +51 -0
- package/dist/chunk-I5YMTJV6.mjs.map +1 -0
- package/dist/chunk-IMNWW7Q5.mjs +13 -0
- package/dist/chunk-IMNWW7Q5.mjs.map +1 -0
- package/dist/chunk-J5N76ASB.mjs +114 -0
- package/dist/chunk-J5N76ASB.mjs.map +1 -0
- package/dist/chunk-JBV26CUG.mjs +51 -0
- package/dist/chunk-JBV26CUG.mjs.map +1 -0
- package/dist/chunk-JMWXVXK5.js +53 -0
- package/dist/chunk-JMWXVXK5.js.map +1 -0
- package/dist/chunk-KWKV2GAH.js +60 -0
- package/dist/chunk-KWKV2GAH.js.map +1 -0
- package/dist/chunk-LDFE33ML.mjs +53 -0
- package/dist/chunk-LDFE33ML.mjs.map +1 -0
- package/dist/chunk-LF5LOI7K.mjs +174 -0
- package/dist/chunk-LF5LOI7K.mjs.map +1 -0
- package/dist/chunk-MMKAMH4U.mjs +16 -0
- package/dist/chunk-MMKAMH4U.mjs.map +1 -0
- package/dist/chunk-MUHFEEYW.js +33 -0
- package/dist/chunk-MUHFEEYW.js.map +1 -0
- package/dist/chunk-MUQYK5EO.js +1 -0
- package/dist/chunk-MUQYK5EO.js.map +1 -0
- package/dist/chunk-OBNALAMG.mjs +71 -0
- package/dist/chunk-OBNALAMG.mjs.map +1 -0
- package/dist/chunk-OLEYNPW6.js +25 -0
- package/dist/chunk-OLEYNPW6.js.map +1 -0
- package/dist/chunk-OYJRR3G2.js +123 -0
- package/dist/chunk-OYJRR3G2.js.map +1 -0
- package/dist/chunk-PLVG7AW3.js +16 -0
- package/dist/chunk-PLVG7AW3.js.map +1 -0
- package/dist/chunk-PYD4CES4.js +1 -0
- package/dist/chunk-PYD4CES4.js.map +1 -0
- package/dist/chunk-QACD2U6P.mjs +1 -0
- package/dist/chunk-QACD2U6P.mjs.map +1 -0
- package/dist/chunk-R5GGIVD4.js +10 -0
- package/dist/chunk-R5GGIVD4.js.map +1 -0
- package/dist/chunk-RCK5WCU5.mjs +25 -0
- package/dist/chunk-RCK5WCU5.mjs.map +1 -0
- package/dist/chunk-S6UUSM7S.js +13 -0
- package/dist/chunk-S6UUSM7S.js.map +1 -0
- package/dist/chunk-S7GOXCRM.js +114 -0
- package/dist/chunk-S7GOXCRM.js.map +1 -0
- package/dist/chunk-SMV5D5LO.mjs +28 -0
- package/dist/chunk-SMV5D5LO.mjs.map +1 -0
- package/dist/chunk-T7EVFD4G.mjs +60 -0
- package/dist/chunk-T7EVFD4G.mjs.map +1 -0
- package/dist/chunk-TLH3V4SY.mjs +1 -0
- package/dist/chunk-TLH3V4SY.mjs.map +1 -0
- package/dist/chunk-TUQ2CVAK.mjs +10 -0
- package/dist/chunk-TUQ2CVAK.mjs.map +1 -0
- package/dist/chunk-U6PVJKTB.js +51 -0
- package/dist/chunk-U6PVJKTB.js.map +1 -0
- package/dist/chunk-UXMMEEX4.mjs +27 -0
- package/dist/chunk-UXMMEEX4.mjs.map +1 -0
- package/dist/chunk-VVXNFFKI.mjs +44 -0
- package/dist/chunk-VVXNFFKI.mjs.map +1 -0
- package/dist/chunk-VZZ3RU3Q.js +28 -0
- package/dist/chunk-VZZ3RU3Q.js.map +1 -0
- package/dist/chunk-W4FWIZ2Y.js +34 -0
- package/dist/chunk-W4FWIZ2Y.js.map +1 -0
- package/dist/chunk-YEQFKOWD.mjs +154 -0
- package/dist/chunk-YEQFKOWD.mjs.map +1 -0
- package/dist/chunk-YPBKY4KY.mjs +1 -0
- package/dist/chunk-YPBKY4KY.mjs.map +1 -0
- package/dist/chunk-YY7K5CXX.js +174 -0
- package/dist/chunk-YY7K5CXX.js.map +1 -0
- package/dist/chunk-ZFORODOX.js +181 -0
- package/dist/chunk-ZFORODOX.js.map +1 -0
- package/dist/chunk-ZULSFSDG.mjs +309 -0
- package/dist/chunk-ZULSFSDG.mjs.map +1 -0
- package/dist/components/AdminManagerBase.d.mts +26 -0
- package/dist/components/AdminManagerBase.d.ts +26 -0
- package/dist/components/AdminManagerBase.js +13 -0
- package/dist/components/AdminManagerBase.js.map +1 -0
- package/dist/components/AdminManagerBase.mjs +12 -0
- package/dist/components/AdminManagerBase.mjs.map +1 -0
- package/dist/components/AdminManagerConfig.d.mts +83 -0
- package/dist/components/AdminManagerConfig.d.ts +83 -0
- package/dist/components/AdminManagerConfig.js +2 -0
- package/dist/components/AdminManagerConfig.js.map +1 -0
- package/dist/components/AdminManagerConfig.mjs +2 -0
- package/dist/components/AdminManagerConfig.mjs.map +1 -0
- package/dist/components/AdminShell.d.mts +30 -0
- package/dist/components/AdminShell.d.ts +30 -0
- package/dist/components/AdminShell.js +12 -0
- package/dist/components/AdminShell.js.map +1 -0
- package/dist/components/AdminShell.mjs +11 -0
- package/dist/components/AdminShell.mjs.map +1 -0
- package/dist/components/JsonLd.d.mts +32 -0
- package/dist/components/JsonLd.d.ts +32 -0
- package/dist/components/JsonLd.js +11 -0
- package/dist/components/JsonLd.js.map +1 -0
- package/dist/components/JsonLd.mjs +11 -0
- package/dist/components/JsonLd.mjs.map +1 -0
- package/dist/components/ResizableImage.d.mts +18 -0
- package/dist/components/ResizableImage.d.ts +18 -0
- package/dist/components/ResizableImage.js +10 -0
- package/dist/components/ResizableImage.js.map +1 -0
- package/dist/components/ResizableImage.mjs +9 -0
- package/dist/components/ResizableImage.mjs.map +1 -0
- package/dist/components/ToggleSwitch.d.mts +11 -0
- package/dist/components/ToggleSwitch.d.ts +11 -0
- package/dist/components/ToggleSwitch.js +10 -0
- package/dist/components/ToggleSwitch.js.map +1 -0
- package/dist/components/ToggleSwitch.mjs +9 -0
- package/dist/components/ToggleSwitch.mjs.map +1 -0
- package/dist/components/image-drop-zone.css +115 -0
- package/dist/components/index.d.mts +26 -0
- package/dist/components/index.d.ts +26 -0
- package/dist/components/index.js +35 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/index.mjs +35 -0
- package/dist/components/index.mjs.map +1 -0
- package/dist/components/toggle-switch.css +55 -0
- package/dist/hooks/index.d.mts +45 -0
- package/dist/hooks/index.d.ts +45 -0
- package/dist/hooks/index.js +25 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/index.mjs +25 -0
- package/dist/hooks/index.mjs.map +1 -0
- package/dist/hooks/useImageDropZone.d.mts +37 -0
- package/dist/hooks/useImageDropZone.d.ts +37 -0
- package/dist/hooks/useImageDropZone.js +13 -0
- package/dist/hooks/useImageDropZone.js.map +1 -0
- package/dist/hooks/useImageDropZone.mjs +12 -0
- package/dist/hooks/useImageDropZone.mjs.map +1 -0
- package/dist/hooks/useScrollReveal.d.mts +6 -0
- package/dist/hooks/useScrollReveal.d.ts +6 -0
- package/dist/hooks/useScrollReveal.js +12 -0
- package/dist/hooks/useScrollReveal.js.map +1 -0
- package/dist/hooks/useScrollReveal.mjs +11 -0
- package/dist/hooks/useScrollReveal.mjs.map +1 -0
- package/dist/image-drop-zone.css +115 -0
- package/dist/index-00a40-FQ.d.mts +136 -0
- package/dist/index-00a40-FQ.d.ts +136 -0
- package/dist/index.d.mts +35 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +169 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +169 -0
- package/dist/index.mjs.map +1 -0
- package/dist/infrastructure/index.d.mts +4 -0
- package/dist/infrastructure/index.d.ts +4 -0
- package/dist/infrastructure/index.js +20 -0
- package/dist/infrastructure/index.js.map +1 -0
- package/dist/infrastructure/index.mjs +20 -0
- package/dist/infrastructure/index.mjs.map +1 -0
- package/dist/infrastructure/middleware/index.d.mts +3 -0
- package/dist/infrastructure/middleware/index.d.ts +3 -0
- package/dist/infrastructure/middleware/index.js +15 -0
- package/dist/infrastructure/middleware/index.js.map +1 -0
- package/dist/infrastructure/middleware/index.mjs +15 -0
- package/dist/infrastructure/middleware/index.mjs.map +1 -0
- package/dist/infrastructure/middleware/wrappers.d.mts +10 -0
- package/dist/infrastructure/middleware/wrappers.d.ts +10 -0
- package/dist/infrastructure/middleware/wrappers.js +15 -0
- package/dist/infrastructure/middleware/wrappers.js.map +1 -0
- package/dist/infrastructure/middleware/wrappers.mjs +15 -0
- package/dist/infrastructure/middleware/wrappers.mjs.map +1 -0
- package/dist/infrastructure/prisma.d.mts +5 -0
- package/dist/infrastructure/prisma.d.ts +5 -0
- package/dist/infrastructure/prisma.js +12 -0
- package/dist/infrastructure/prisma.js.map +1 -0
- package/dist/infrastructure/prisma.mjs +12 -0
- package/dist/infrastructure/prisma.mjs.map +1 -0
- package/dist/services/index.d.mts +22 -0
- package/dist/services/index.d.ts +22 -0
- package/dist/services/index.js +36 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/index.mjs +36 -0
- package/dist/services/index.mjs.map +1 -0
- package/dist/toggle-switch.css +55 -0
- package/dist/types/common.d.mts +14 -0
- package/dist/types/common.d.ts +14 -0
- package/dist/types/common.js +8 -0
- package/dist/types/common.js.map +1 -0
- package/dist/types/common.mjs +8 -0
- package/dist/types/common.mjs.map +1 -0
- package/dist/types/index.d.mts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +9 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/index.mjs +9 -0
- package/dist/types/index.mjs.map +1 -0
- package/dist/utils/admin-fetch.d.mts +11 -0
- package/dist/utils/admin-fetch.d.ts +11 -0
- package/dist/utils/admin-fetch.js +13 -0
- package/dist/utils/admin-fetch.js.map +1 -0
- package/dist/utils/admin-fetch.mjs +13 -0
- package/dist/utils/admin-fetch.mjs.map +1 -0
- package/dist/utils/api-helpers.d.mts +20 -0
- package/dist/utils/api-helpers.d.ts +20 -0
- package/dist/utils/api-helpers.js +12 -0
- package/dist/utils/api-helpers.js.map +1 -0
- package/dist/utils/api-helpers.mjs +12 -0
- package/dist/utils/api-helpers.mjs.map +1 -0
- package/dist/utils/date.d.mts +5 -0
- package/dist/utils/date.d.ts +5 -0
- package/dist/utils/date.js +12 -0
- package/dist/utils/date.js.map +1 -0
- package/dist/utils/date.mjs +12 -0
- package/dist/utils/date.mjs.map +1 -0
- package/dist/utils/html-sanitizer.d.mts +56 -0
- package/dist/utils/html-sanitizer.d.ts +56 -0
- package/dist/utils/html-sanitizer.js +11 -0
- package/dist/utils/html-sanitizer.js.map +1 -0
- package/dist/utils/html-sanitizer.mjs +11 -0
- package/dist/utils/html-sanitizer.mjs.map +1 -0
- package/dist/utils/image-variant-utils.d.mts +14 -0
- package/dist/utils/image-variant-utils.d.ts +14 -0
- package/dist/utils/image-variant-utils.js +10 -0
- package/dist/utils/image-variant-utils.js.map +1 -0
- package/dist/utils/image-variant-utils.mjs +10 -0
- package/dist/utils/image-variant-utils.mjs.map +1 -0
- package/dist/utils/image-variants.d.mts +13 -0
- package/dist/utils/image-variants.d.ts +13 -0
- package/dist/utils/image-variants.js +12 -0
- package/dist/utils/image-variants.js.map +1 -0
- package/dist/utils/image-variants.mjs +12 -0
- package/dist/utils/image-variants.mjs.map +1 -0
- package/dist/utils/index.d.mts +88 -0
- package/dist/utils/index.d.ts +88 -0
- package/dist/utils/index.js +92 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +92 -0
- package/dist/utils/index.mjs.map +1 -0
- package/dist/utils/jwt.d.mts +19 -0
- package/dist/utils/jwt.d.ts +19 -0
- package/dist/utils/jwt.js +9 -0
- package/dist/utils/jwt.js.map +1 -0
- package/dist/utils/jwt.mjs +9 -0
- package/dist/utils/jwt.mjs.map +1 -0
- package/dist/utils/r2-helpers.d.mts +9 -0
- package/dist/utils/r2-helpers.d.ts +9 -0
- package/dist/utils/r2-helpers.js +20 -0
- package/dist/utils/r2-helpers.js.map +1 -0
- package/dist/utils/r2-helpers.mjs +20 -0
- package/dist/utils/r2-helpers.mjs.map +1 -0
- package/dist/utils/r2-storage.d.mts +38 -0
- package/dist/utils/r2-storage.d.ts +38 -0
- package/dist/utils/r2-storage.js +17 -0
- package/dist/utils/r2-storage.js.map +1 -0
- package/dist/utils/r2-storage.mjs +17 -0
- package/dist/utils/r2-storage.mjs.map +1 -0
- package/dist/utils/route-params.d.mts +3 -0
- package/dist/utils/route-params.d.ts +3 -0
- package/dist/utils/route-params.js +8 -0
- package/dist/utils/route-params.js.map +1 -0
- package/dist/utils/route-params.mjs +8 -0
- package/dist/utils/route-params.mjs.map +1 -0
- package/dist/validators/index.d.mts +2 -0
- package/dist/validators/index.d.ts +2 -0
- package/dist/validators/index.js +11 -0
- package/dist/validators/index.js.map +1 -0
- package/dist/validators/index.mjs +11 -0
- package/dist/validators/index.mjs.map +1 -0
- package/dist/validators/shared.d.mts +6 -0
- package/dist/validators/shared.d.ts +6 -0
- package/dist/validators/shared.js +10 -0
- package/dist/validators/shared.js.map +1 -0
- package/dist/validators/shared.mjs +10 -0
- package/dist/validators/shared.mjs.map +1 -0
- package/package.json +257 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
|
+
|
|
3
|
+
var _chunkEG6NVPP2js = require('./chunk-EG6NVPP2.js');
|
|
4
|
+
|
|
5
|
+
// src/utils/admin-fetch.ts
|
|
6
|
+
var isRefreshing = false;
|
|
7
|
+
var refreshPromise = null;
|
|
8
|
+
function getAuthHeaders() {
|
|
9
|
+
return {};
|
|
10
|
+
}
|
|
11
|
+
async function refreshAccessToken() {
|
|
12
|
+
return tryRefreshCookie();
|
|
13
|
+
}
|
|
14
|
+
async function tryRefreshCookie() {
|
|
15
|
+
try {
|
|
16
|
+
const { refreshEndpoint } = _chunkEG6NVPP2js.resolveRouteConfig.call(void 0, );
|
|
17
|
+
const res = await fetch(refreshEndpoint, {
|
|
18
|
+
method: "POST",
|
|
19
|
+
credentials: "same-origin"
|
|
20
|
+
});
|
|
21
|
+
if (!res.ok) return false;
|
|
22
|
+
const data = await res.json();
|
|
23
|
+
return data.success === true;
|
|
24
|
+
} catch (e) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function tryRefresh() {
|
|
29
|
+
if (isRefreshing && refreshPromise) {
|
|
30
|
+
return refreshPromise;
|
|
31
|
+
}
|
|
32
|
+
isRefreshing = true;
|
|
33
|
+
refreshPromise = tryRefreshCookie().finally(() => {
|
|
34
|
+
isRefreshing = false;
|
|
35
|
+
refreshPromise = null;
|
|
36
|
+
});
|
|
37
|
+
return refreshPromise;
|
|
38
|
+
}
|
|
39
|
+
async function adminFetch(url, options = {}) {
|
|
40
|
+
const res = await fetch(url, {
|
|
41
|
+
...options,
|
|
42
|
+
credentials: "same-origin"
|
|
43
|
+
});
|
|
44
|
+
if (res.status === 401 && typeof window !== "undefined") {
|
|
45
|
+
const refreshed = await tryRefresh();
|
|
46
|
+
if (refreshed) {
|
|
47
|
+
return fetch(url, { ...options, credentials: "same-origin" });
|
|
48
|
+
}
|
|
49
|
+
const { loginPath } = _chunkEG6NVPP2js.resolveRouteConfig.call(void 0, );
|
|
50
|
+
window.location.href = loginPath;
|
|
51
|
+
}
|
|
52
|
+
return res;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
exports.getAuthHeaders = getAuthHeaders; exports.refreshAccessToken = refreshAccessToken; exports.adminFetch = adminFetch;
|
|
60
|
+
//# sourceMappingURL=chunk-KWKV2GAH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-cms-kit/dist/chunk-KWKV2GAH.js","../src/utils/admin-fetch.ts"],"names":[],"mappings":"AAAA;AACE;AACF,sDAA4B;AAC5B;AACA;ACFA,IAAI,aAAA,EAAe,KAAA;AACnB,IAAI,eAAA,EAA0C,IAAA;AAKvC,SAAS,cAAA,CAAA,EAAyC;AACvD,EAAA,OAAO,CAAC,CAAA;AACV;AAKA,MAAA,SAAsB,kBAAA,CAAA,EAAuC;AAC3D,EAAA,OAAO,gBAAA,CAAiB,CAAA;AAC1B;AAEA,MAAA,SAAe,gBAAA,CAAA,EAAqC;AAClD,EAAA,IAAI;AAGF,IAAA,MAAM,EAAE,gBAAgB,EAAA,EAAI,iDAAA,CAAmB;AAC/C,IAAA,MAAM,IAAA,EAAM,MAAM,KAAA,CAAM,eAAA,EAAiB;AAAA,MACvC,MAAA,EAAQ,MAAA;AAAA,MACR,WAAA,EAAa;AAAA,IACf,CAAC,CAAA;AAED,IAAA,GAAA,CAAI,CAAC,GAAA,CAAI,EAAA,EAAI,OAAO,KAAA;AAEpB,IAAA,MAAM,KAAA,EAAO,MAAM,GAAA,CAAI,IAAA,CAAK,CAAA;AAC5B,IAAA,OAAO,IAAA,CAAK,QAAA,IAAY,IAAA;AAAA,EAC1B,EAAA,UAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAEA,MAAA,SAAe,UAAA,CAAA,EAA+B;AAC5C,EAAA,GAAA,CAAI,aAAA,GAAgB,cAAA,EAAgB;AAClC,IAAA,OAAO,cAAA;AAAA,EACT;AAEA,EAAA,aAAA,EAAe,IAAA;AACf,EAAA,eAAA,EAAiB,gBAAA,CAAiB,CAAA,CAAE,OAAA,CAAQ,CAAA,EAAA,GAAM;AAChD,IAAA,aAAA,EAAe,KAAA;AACf,IAAA,eAAA,EAAiB,IAAA;AAAA,EACnB,CAAC,CAAA;AAED,EAAA,OAAO,cAAA;AACT;AAEA,MAAA,SAAsB,UAAA,CACpB,GAAA,EACA,QAAA,EAAuB,CAAC,CAAA,EACL;AACnB,EAAA,MAAM,IAAA,EAAM,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,IAC3B,GAAG,OAAA;AAAA,IACH,WAAA,EAAa;AAAA,EACf,CAAC,CAAA;AAED,EAAA,GAAA,CAAI,GAAA,CAAI,OAAA,IAAW,IAAA,GAAO,OAAO,OAAA,IAAW,WAAA,EAAa;AACvD,IAAA,MAAM,UAAA,EAAY,MAAM,UAAA,CAAW,CAAA;AAEnC,IAAA,GAAA,CAAI,SAAA,EAAW;AACb,MAAA,OAAO,KAAA,CAAM,GAAA,EAAK,EAAE,GAAG,OAAA,EAAS,WAAA,EAAa,cAAc,CAAC,CAAA;AAAA,IAC9D;AAGA,IAAA,MAAM,EAAE,UAAU,EAAA,EAAI,iDAAA,CAAmB;AACzC,IAAA,MAAA,CAAO,QAAA,CAAS,KAAA,EAAO,SAAA;AAAA,EACzB;AAEA,EAAA,OAAO,GAAA;AACT;ADrBA;AACA;AACE;AACA;AACA;AACF,0HAAC","file":"/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-cms-kit/dist/chunk-KWKV2GAH.js","sourcesContent":[null,"import { resolveRouteConfig } from '../config';\n\nlet isRefreshing = false;\nlet refreshPromise: Promise<boolean> | null = null;\n\n/**\n * @deprecated Auth is now handled via httpOnly cookies. Returns empty headers.\n */\nexport function getAuthHeaders(): Record<string, string> {\n return {};\n}\n\n/**\n * @deprecated Auth is now handled via httpOnly cookies.\n */\nexport async function refreshAccessToken(): Promise<boolean> {\n return tryRefreshCookie();\n}\n\nasync function tryRefreshCookie(): Promise<boolean> {\n try {\n // refresh 엔드포인트는 §5 config boundary 에서 해석된다 (consumer\n // override 가능; 미설정 시 레거시 기본 경로 — spec.md §4.1 C2 / B5).\n const { refreshEndpoint } = resolveRouteConfig();\n const res = await fetch(refreshEndpoint, {\n method: 'POST',\n credentials: 'same-origin',\n });\n\n if (!res.ok) return false;\n\n const data = await res.json();\n return data.success === true;\n } catch {\n return false;\n }\n}\n\nasync function tryRefresh(): Promise<boolean> {\n if (isRefreshing && refreshPromise) {\n return refreshPromise;\n }\n\n isRefreshing = true;\n refreshPromise = tryRefreshCookie().finally(() => {\n isRefreshing = false;\n refreshPromise = null;\n });\n\n return refreshPromise;\n}\n\nexport async function adminFetch(\n url: string,\n options: RequestInit = {},\n): Promise<Response> {\n const res = await fetch(url, {\n ...options,\n credentials: 'same-origin',\n });\n\n if (res.status === 401 && typeof window !== 'undefined') {\n const refreshed = await tryRefresh();\n\n if (refreshed) {\n return fetch(url, { ...options, credentials: 'same-origin' });\n }\n\n // login 페이지 경로도 §5 config boundary 에서 해석된다.\n const { loginPath } = resolveRouteConfig();\n window.location.href = loginPath;\n }\n\n return res;\n}\n"]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveClientIdentity,
|
|
3
|
+
resolveRateLimitEnabled
|
|
4
|
+
} from "./chunk-BWKKHCDG.mjs";
|
|
5
|
+
|
|
6
|
+
// src/infrastructure/middleware/wrappers.ts
|
|
7
|
+
import {
|
|
8
|
+
withPublicApi as _withPublicApi,
|
|
9
|
+
withAdminApi as _withAdminApi,
|
|
10
|
+
withAuthApi as _withAuthApi,
|
|
11
|
+
withCustomApi as _withCustomApi
|
|
12
|
+
} from "@withwiz/toolkit/next/middleware/wrappers";
|
|
13
|
+
import { setRateLimitAdapter } from "@withwiz/toolkit/next/middleware/rate-limit";
|
|
14
|
+
function createInMemoryLimiter(limit, windowMs) {
|
|
15
|
+
const store = /* @__PURE__ */ new Map();
|
|
16
|
+
return {
|
|
17
|
+
check: async (identifier) => {
|
|
18
|
+
const now = Date.now();
|
|
19
|
+
const entry = store.get(identifier);
|
|
20
|
+
if (!entry || now > entry.resetAt) {
|
|
21
|
+
store.set(identifier, { count: 1, resetAt: now + windowMs });
|
|
22
|
+
return { success: true, remaining: limit - 1, resetIn: windowMs };
|
|
23
|
+
}
|
|
24
|
+
entry.count++;
|
|
25
|
+
const remaining = Math.max(0, limit - entry.count);
|
|
26
|
+
const resetIn = entry.resetAt - now;
|
|
27
|
+
return { success: entry.count <= limit, remaining, resetIn };
|
|
28
|
+
},
|
|
29
|
+
config: { limit }
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
var extractClientIdentity = (headers) => resolveClientIdentity(headers);
|
|
33
|
+
setRateLimitAdapter({
|
|
34
|
+
rateLimiters: {
|
|
35
|
+
api: createInMemoryLimiter(120, 6e4),
|
|
36
|
+
auth: createInMemoryLimiter(10, 6e4),
|
|
37
|
+
admin: createInMemoryLimiter(200, 6e4)
|
|
38
|
+
},
|
|
39
|
+
extractClientIp: extractClientIdentity,
|
|
40
|
+
isEnabled: async () => resolveRateLimitEnabled()
|
|
41
|
+
});
|
|
42
|
+
var withPublicApi = _withPublicApi;
|
|
43
|
+
var withAdminApi = _withAdminApi;
|
|
44
|
+
var withAuthApi = _withAuthApi;
|
|
45
|
+
var withCustomApi = _withCustomApi;
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
withPublicApi,
|
|
49
|
+
withAdminApi,
|
|
50
|
+
withAuthApi,
|
|
51
|
+
withCustomApi
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=chunk-LDFE33ML.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/infrastructure/middleware/wrappers.ts"],"sourcesContent":["import type { NextRequest } from 'next/server';\nimport {\n withPublicApi as _withPublicApi,\n withAdminApi as _withAdminApi,\n withAuthApi as _withAuthApi,\n withCustomApi as _withCustomApi,\n} from '@withwiz/toolkit/next/middleware/wrappers';\nimport { setRateLimitAdapter } from '@withwiz/toolkit/next/middleware/rate-limit';\nimport { resolveClientIdentity, resolveRateLimitEnabled } from '../../config';\n\nfunction createInMemoryLimiter(limit: number, windowMs: number) {\n const store = new Map<string, { count: number; resetAt: number }>();\n return {\n check: async (identifier: string) => {\n const now = Date.now();\n const entry = store.get(identifier);\n if (!entry || now > entry.resetAt) {\n store.set(identifier, { count: 1, resetAt: now + windowMs });\n return { success: true, remaining: limit - 1, resetIn: windowMs };\n }\n entry.count++;\n const remaining = Math.max(0, limit - entry.count);\n const resetIn = entry.resetAt - now;\n return { success: entry.count <= limit, remaining, resetIn };\n },\n config: { limit },\n };\n}\n\n/**\n * rate-limit client-identity 추출 (spec.md §4.6 / Sprint 1 S4).\n *\n * 더 이상 spoofable 한 `x-forwarded-for` 의 첫 값을 무조건 신뢰하지 않고\n * `127.0.0.1` 매직 fallback 도 쓰지 않는다. 식별자 추출은 §5 config\n * boundary 의 `resolveClientIdentity` 로 위임되며, consumer 가 자신의 proxy\n * topology / trusted hop 수를 아는 `rateLimit.identityExtractor` 를 주입하면\n * 그것이 사용된다. 미설정 시 안전 기본값(헤더만 바꿔 회전 불가)을 쓴다.\n *\n * NOTE: import-time `setRateLimitAdapter(...)` 무조건 호출 자체의 제거는\n * §4.3 / Sprint 2 범위다. Sprint 1 은 §4.6 보안 결함(identity 추출)만 고치고\n * 그것을 overridable 하게 만든다 (in-memory limiter 기본값은 유지).\n */\nconst extractClientIdentity = (headers: Headers): string =>\n resolveClientIdentity(headers);\n\nsetRateLimitAdapter({\n rateLimiters: {\n api: createInMemoryLimiter(120, 60_000),\n auth: createInMemoryLimiter(10, 60_000),\n admin: createInMemoryLimiter(200, 60_000),\n },\n extractClientIp: extractClientIdentity,\n isEnabled: async () => resolveRateLimitEnabled(),\n});\n\ntype NextRouteHandler = (request: NextRequest, context?: any) => Promise<Response>;\n\nexport const withPublicApi = _withPublicApi as unknown as (handler: any) => NextRouteHandler;\nexport const withAdminApi = _withAdminApi as unknown as (handler: any) => NextRouteHandler;\nexport const withAuthApi = _withAuthApi as unknown as (handler: any) => NextRouteHandler;\nexport const withCustomApi = _withCustomApi as unknown as (handler: any, configureChain: any) => NextRouteHandler;\n\nexport type { IApiContext, IUser, TApiHandler } from '@withwiz/toolkit/next/middleware/types';\n"],"mappings":";;;;;;AACA;AAAA,EACE,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,iBAAiB;AAAA,OACZ;AACP,SAAS,2BAA2B;AAGpC,SAAS,sBAAsB,OAAe,UAAkB;AAC9D,QAAM,QAAQ,oBAAI,IAAgD;AAClE,SAAO;AAAA,IACL,OAAO,OAAO,eAAuB;AACnC,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,QAAQ,MAAM,IAAI,UAAU;AAClC,UAAI,CAAC,SAAS,MAAM,MAAM,SAAS;AACjC,cAAM,IAAI,YAAY,EAAE,OAAO,GAAG,SAAS,MAAM,SAAS,CAAC;AAC3D,eAAO,EAAE,SAAS,MAAM,WAAW,QAAQ,GAAG,SAAS,SAAS;AAAA,MAClE;AACA,YAAM;AACN,YAAM,YAAY,KAAK,IAAI,GAAG,QAAQ,MAAM,KAAK;AACjD,YAAM,UAAU,MAAM,UAAU;AAChC,aAAO,EAAE,SAAS,MAAM,SAAS,OAAO,WAAW,QAAQ;AAAA,IAC7D;AAAA,IACA,QAAQ,EAAE,MAAM;AAAA,EAClB;AACF;AAeA,IAAM,wBAAwB,CAAC,YAC7B,sBAAsB,OAAO;AAE/B,oBAAoB;AAAA,EAClB,cAAc;AAAA,IACZ,KAAK,sBAAsB,KAAK,GAAM;AAAA,IACtC,MAAM,sBAAsB,IAAI,GAAM;AAAA,IACtC,OAAO,sBAAsB,KAAK,GAAM;AAAA,EAC1C;AAAA,EACA,iBAAiB;AAAA,EACjB,WAAW,YAAY,wBAAwB;AACjD,CAAC;AAIM,IAAM,gBAAgB;AACtB,IAAM,eAAe;AACrB,IAAM,cAAc;AACpB,IAAM,gBAAgB;","names":[]}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resizeImageIfNeeded,
|
|
3
|
+
validateImageSize
|
|
4
|
+
} from "./chunk-J5N76ASB.mjs";
|
|
5
|
+
import {
|
|
6
|
+
adminFetch
|
|
7
|
+
} from "./chunk-T7EVFD4G.mjs";
|
|
8
|
+
import {
|
|
9
|
+
resolveRouteConfig
|
|
10
|
+
} from "./chunk-BWKKHCDG.mjs";
|
|
11
|
+
|
|
12
|
+
// src/hooks/useImageDropZone.ts
|
|
13
|
+
import { useState, useCallback, useRef } from "react";
|
|
14
|
+
var ALLOWED_TYPES = ["image/jpeg", "image/png", "image/webp", "image/gif"];
|
|
15
|
+
var MAX_UPLOAD_SIZE = 5 * 1024 * 1024;
|
|
16
|
+
function validateFile(file) {
|
|
17
|
+
if (!ALLOWED_TYPES.includes(file.type)) {
|
|
18
|
+
const allowed = ALLOWED_TYPES.map((t) => t.split("/")[1]).join(", ");
|
|
19
|
+
return `\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD30C\uC77C \uD615\uC2DD\uC785\uB2C8\uB2E4. (\uD5C8\uC6A9: ${allowed})`;
|
|
20
|
+
}
|
|
21
|
+
const sizeError = validateImageSize(file);
|
|
22
|
+
if (sizeError) return sizeError;
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
async function uploadSingleFile(file) {
|
|
26
|
+
const fd = new FormData();
|
|
27
|
+
fd.append("file", file);
|
|
28
|
+
const { uploadEndpoint } = resolveRouteConfig();
|
|
29
|
+
const res = await adminFetch(uploadEndpoint, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
body: fd
|
|
32
|
+
});
|
|
33
|
+
const json = await res.json();
|
|
34
|
+
if (!json.success) {
|
|
35
|
+
throw new Error(json.error?.message || "\uC5C5\uB85C\uB4DC \uC2E4\uD328");
|
|
36
|
+
}
|
|
37
|
+
return json.data;
|
|
38
|
+
}
|
|
39
|
+
function useImageDropZone(options) {
|
|
40
|
+
const {
|
|
41
|
+
multiple = false,
|
|
42
|
+
maxFiles,
|
|
43
|
+
onUpload,
|
|
44
|
+
onKeyTracked,
|
|
45
|
+
disabled = false
|
|
46
|
+
} = options;
|
|
47
|
+
const [isDragOver, setIsDragOver] = useState(false);
|
|
48
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
49
|
+
const [isResizing, setIsResizing] = useState(false);
|
|
50
|
+
const [error, setError] = useState(null);
|
|
51
|
+
const dragCounterRef = useRef(0);
|
|
52
|
+
const processFiles = useCallback(
|
|
53
|
+
async (files) => {
|
|
54
|
+
if (disabled || files.length === 0) return;
|
|
55
|
+
let toProcess = multiple ? files : [files[0]];
|
|
56
|
+
if (maxFiles && toProcess.length > maxFiles) {
|
|
57
|
+
toProcess = toProcess.slice(0, maxFiles);
|
|
58
|
+
setError(`\uCD5C\uB300 ${maxFiles}\uAC1C\uAE4C\uC9C0 \uC5C5\uB85C\uB4DC\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.`);
|
|
59
|
+
}
|
|
60
|
+
for (const file of toProcess) {
|
|
61
|
+
const validationError = validateFile(file);
|
|
62
|
+
if (validationError) {
|
|
63
|
+
setError(validationError);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
setError(null);
|
|
68
|
+
setIsResizing(true);
|
|
69
|
+
let resizedFiles;
|
|
70
|
+
try {
|
|
71
|
+
resizedFiles = [];
|
|
72
|
+
for (const file of toProcess) {
|
|
73
|
+
const result = await resizeImageIfNeeded(file);
|
|
74
|
+
if (result.wasResized && process.env.NODE_ENV === "development") {
|
|
75
|
+
console.log(
|
|
76
|
+
`[image-optimize] ${(result.originalSize / 1024 / 1024).toFixed(1)}MB \u2192 ${(result.newSize / 1024 / 1024).toFixed(1)}MB`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (result.file.size > MAX_UPLOAD_SIZE) {
|
|
80
|
+
setError(
|
|
81
|
+
`\uC774\uBBF8\uC9C0 \uCD5C\uC801\uD654 \uD6C4\uC5D0\uB3C4 5MB\uB97C \uCD08\uACFC\uD569\uB2C8\uB2E4. (${(result.file.size / 1024 / 1024).toFixed(1)}MB) \uB354 \uC791\uC740 \uC774\uBBF8\uC9C0\uB97C \uC0AC\uC6A9\uD574 \uC8FC\uC138\uC694.`
|
|
82
|
+
);
|
|
83
|
+
setIsResizing(false);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
resizedFiles.push(result.file);
|
|
87
|
+
}
|
|
88
|
+
} catch (err) {
|
|
89
|
+
const msg = err instanceof Error ? err.message : "\uC774\uBBF8\uC9C0 \uCD5C\uC801\uD654 \uC911 \uC624\uB958 \uBC1C\uC0DD";
|
|
90
|
+
setError(msg);
|
|
91
|
+
setIsResizing(false);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
setIsResizing(false);
|
|
95
|
+
setIsUploading(true);
|
|
96
|
+
try {
|
|
97
|
+
for (const file of resizedFiles) {
|
|
98
|
+
const result = await uploadSingleFile(file);
|
|
99
|
+
onUpload(result);
|
|
100
|
+
if (result.key) onKeyTracked?.(result.key);
|
|
101
|
+
if (result.variantKeys) {
|
|
102
|
+
for (const vk of result.variantKeys) onKeyTracked?.(vk);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} catch (err) {
|
|
106
|
+
const msg = err instanceof Error ? err.message : "\uC5C5\uB85C\uB4DC \uC911 \uC624\uB958 \uBC1C\uC0DD";
|
|
107
|
+
setError(msg);
|
|
108
|
+
} finally {
|
|
109
|
+
setIsUploading(false);
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
[multiple, maxFiles, onUpload, onKeyTracked, disabled]
|
|
113
|
+
);
|
|
114
|
+
const onDragEnter = useCallback((e) => {
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
e.stopPropagation();
|
|
117
|
+
dragCounterRef.current++;
|
|
118
|
+
if (e.dataTransfer?.types.includes("Files")) {
|
|
119
|
+
setIsDragOver(true);
|
|
120
|
+
}
|
|
121
|
+
}, []);
|
|
122
|
+
const onDragOver = useCallback((e) => {
|
|
123
|
+
e.preventDefault();
|
|
124
|
+
e.stopPropagation();
|
|
125
|
+
}, []);
|
|
126
|
+
const onDragLeave = useCallback((e) => {
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
e.stopPropagation();
|
|
129
|
+
dragCounterRef.current--;
|
|
130
|
+
if (dragCounterRef.current === 0) {
|
|
131
|
+
setIsDragOver(false);
|
|
132
|
+
}
|
|
133
|
+
}, []);
|
|
134
|
+
const onDrop = useCallback(
|
|
135
|
+
(e) => {
|
|
136
|
+
e.preventDefault();
|
|
137
|
+
e.stopPropagation();
|
|
138
|
+
dragCounterRef.current = 0;
|
|
139
|
+
setIsDragOver(false);
|
|
140
|
+
if (disabled) return;
|
|
141
|
+
const files = Array.from(e.dataTransfer?.files || []).filter(
|
|
142
|
+
(f) => f.type.startsWith("image/")
|
|
143
|
+
);
|
|
144
|
+
if (files.length === 0) {
|
|
145
|
+
setError("\uC774\uBBF8\uC9C0 \uD30C\uC77C\uB9CC \uC5C5\uB85C\uB4DC\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
processFiles(files);
|
|
149
|
+
},
|
|
150
|
+
[processFiles, disabled]
|
|
151
|
+
);
|
|
152
|
+
const handleFileInput = useCallback(
|
|
153
|
+
async (fileList) => {
|
|
154
|
+
const files = Array.from(fileList || []);
|
|
155
|
+
if (files.length > 0) {
|
|
156
|
+
await processFiles(files);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
[processFiles]
|
|
160
|
+
);
|
|
161
|
+
return {
|
|
162
|
+
isDragOver,
|
|
163
|
+
isUploading,
|
|
164
|
+
isResizing,
|
|
165
|
+
error,
|
|
166
|
+
dragHandlers: { onDragEnter, onDragOver, onDragLeave, onDrop },
|
|
167
|
+
handleFileInput
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export {
|
|
172
|
+
useImageDropZone
|
|
173
|
+
};
|
|
174
|
+
//# sourceMappingURL=chunk-LF5LOI7K.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useImageDropZone.ts"],"sourcesContent":["\"use client\";\n\nimport { useState, useCallback, useRef, type DragEvent } from \"react\";\nimport { adminFetch } from \"../utils/admin-fetch\";\nimport {\n resizeImageIfNeeded,\n validateImageSize,\n} from \"../utils/image-resize\";\nimport { resolveRouteConfig } from \"../config\";\n\nconst ALLOWED_TYPES = [\"image/jpeg\", \"image/png\", \"image/webp\", \"image/gif\"];\nconst MAX_UPLOAD_SIZE = 5 * 1024 * 1024; // 5MB\n\ninterface ImageVariantUrls {\n lg?: string;\n md?: string;\n sm?: string;\n thumb?: string;\n}\n\ninterface UploadResult {\n url: string;\n key: string;\n size: number;\n variants?: ImageVariantUrls;\n variantKeys?: string[];\n}\n\ninterface UseImageDropZoneOptions {\n multiple?: boolean;\n maxFiles?: number;\n onUpload: (result: UploadResult) => void;\n onKeyTracked?: (key: string) => void;\n disabled?: boolean;\n}\n\nfunction validateFile(file: File): string | null {\n if (!ALLOWED_TYPES.includes(file.type)) {\n const allowed = ALLOWED_TYPES.map((t) => t.split(\"/\")[1]).join(\", \");\n return `지원하지 않는 파일 형식입니다. (허용: ${allowed})`;\n }\n const sizeError = validateImageSize(file);\n if (sizeError) return sizeError;\n return null;\n}\n\nasync function uploadSingleFile(file: File): Promise<UploadResult> {\n const fd = new FormData();\n fd.append(\"file\", file);\n // upload 엔드포인트는 §5 config boundary 에서 해석된다 (consumer override\n // 가능; 미설정 시 레거시 기본 경로 — spec.md §4.1 C2 / B5).\n const { uploadEndpoint } = resolveRouteConfig();\n const res = await adminFetch(uploadEndpoint, {\n method: \"POST\",\n body: fd,\n });\n const json = await res.json();\n if (!json.success) {\n throw new Error(json.error?.message || \"업로드 실패\");\n }\n return json.data;\n}\n\nexport function useImageDropZone(options: UseImageDropZoneOptions) {\n const {\n multiple = false,\n maxFiles,\n onUpload,\n onKeyTracked,\n disabled = false,\n } = options;\n\n const [isDragOver, setIsDragOver] = useState(false);\n const [isUploading, setIsUploading] = useState(false);\n const [isResizing, setIsResizing] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const dragCounterRef = useRef(0);\n\n const processFiles = useCallback(\n async (files: File[]) => {\n if (disabled || files.length === 0) return;\n\n let toProcess = multiple ? files : [files[0]];\n\n if (maxFiles && toProcess.length > maxFiles) {\n toProcess = toProcess.slice(0, maxFiles);\n setError(`최대 ${maxFiles}개까지 업로드할 수 있습니다.`);\n }\n\n for (const file of toProcess) {\n const validationError = validateFile(file);\n if (validationError) {\n setError(validationError);\n return;\n }\n }\n\n setError(null);\n\n setIsResizing(true);\n let resizedFiles: File[];\n try {\n resizedFiles = [];\n for (const file of toProcess) {\n const result = await resizeImageIfNeeded(file);\n if (result.wasResized && process.env.NODE_ENV === \"development\") {\n console.log(\n `[image-optimize] ${(result.originalSize / 1024 / 1024).toFixed(1)}MB → ${(result.newSize / 1024 / 1024).toFixed(1)}MB`\n );\n }\n if (result.file.size > MAX_UPLOAD_SIZE) {\n setError(\n `이미지 최적화 후에도 5MB를 초과합니다. (${(result.file.size / 1024 / 1024).toFixed(1)}MB) 더 작은 이미지를 사용해 주세요.`\n );\n setIsResizing(false);\n return;\n }\n resizedFiles.push(result.file);\n }\n } catch (err) {\n const msg =\n err instanceof Error ? err.message : \"이미지 최적화 중 오류 발생\";\n setError(msg);\n setIsResizing(false);\n return;\n }\n setIsResizing(false);\n\n setIsUploading(true);\n try {\n for (const file of resizedFiles) {\n const result = await uploadSingleFile(file);\n onUpload(result);\n if (result.key) onKeyTracked?.(result.key);\n if (result.variantKeys) {\n for (const vk of result.variantKeys) onKeyTracked?.(vk);\n }\n }\n } catch (err) {\n const msg =\n err instanceof Error ? err.message : \"업로드 중 오류 발생\";\n setError(msg);\n } finally {\n setIsUploading(false);\n }\n },\n [multiple, maxFiles, onUpload, onKeyTracked, disabled]\n );\n\n const onDragEnter = useCallback((e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n dragCounterRef.current++;\n if (e.dataTransfer?.types.includes(\"Files\")) {\n setIsDragOver(true);\n }\n }, []);\n\n const onDragOver = useCallback((e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n }, []);\n\n const onDragLeave = useCallback((e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n dragCounterRef.current--;\n if (dragCounterRef.current === 0) {\n setIsDragOver(false);\n }\n }, []);\n\n const onDrop = useCallback(\n (e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n dragCounterRef.current = 0;\n setIsDragOver(false);\n\n if (disabled) return;\n\n const files = Array.from(e.dataTransfer?.files || []).filter((f) =>\n f.type.startsWith(\"image/\")\n );\n\n if (files.length === 0) {\n setError(\"이미지 파일만 업로드할 수 있습니다.\");\n return;\n }\n\n processFiles(files);\n },\n [processFiles, disabled]\n );\n\n const handleFileInput = useCallback(\n async (fileList: FileList | null) => {\n const files = Array.from(fileList || []);\n if (files.length > 0) {\n await processFiles(files);\n }\n },\n [processFiles]\n );\n\n return {\n isDragOver,\n isUploading,\n isResizing,\n error,\n dragHandlers: { onDragEnter, onDragOver, onDragLeave, onDrop },\n handleFileInput,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/utils/image-variant-utils.ts
|
|
2
|
+
var IMAGE_VARIANT_SIZES = {
|
|
3
|
+
lg: 1920,
|
|
4
|
+
md: 960,
|
|
5
|
+
sm: 480,
|
|
6
|
+
thumb: 240
|
|
7
|
+
};
|
|
8
|
+
function getVariantUrl(url, size = "thumb") {
|
|
9
|
+
return url.replace(/\.[^.]+$/, `-${size}.webp`);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
IMAGE_VARIANT_SIZES,
|
|
14
|
+
getVariantUrl
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=chunk-MMKAMH4U.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/image-variant-utils.ts"],"sourcesContent":["export const IMAGE_VARIANT_SIZES = {\n lg: 1920,\n md: 960,\n sm: 480,\n thumb: 240,\n} as const;\n\nexport type VariantSize = keyof typeof IMAGE_VARIANT_SIZES;\n\n/**\n * 이미지 URL에서 특정 variant URL을 생성합니다.\n * 예: (\"https://cdn.example.com/path/abc.jpg\", \"thumb\") → \"https://cdn.example.com/path/abc-thumb.webp\"\n */\nexport function getVariantUrl(url: string, size: VariantSize = 'thumb'): string {\n return url.replace(/\\.[^.]+$/, `-${size}.webp`);\n}\n"],"mappings":";AAAO,IAAM,sBAAsB;AAAA,EACjC,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,OAAO;AACT;AAQO,SAAS,cAAc,KAAa,OAAoB,SAAiB;AAC9E,SAAO,IAAI,QAAQ,YAAY,IAAI,IAAI,OAAO;AAChD;","names":[]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/components/JsonLd.tsx
|
|
2
|
+
var _jsxruntime = require('react/jsx-runtime');
|
|
3
|
+
var LS = String.fromCharCode(8232);
|
|
4
|
+
var PS = String.fromCharCode(8233);
|
|
5
|
+
var JSON_LD_UNSAFE = new RegExp(`[<>&${LS}${PS}]`, "g");
|
|
6
|
+
var JSON_LD_ESCAPES = {
|
|
7
|
+
"<": "\\u003c",
|
|
8
|
+
">": "\\u003e",
|
|
9
|
+
"&": "\\u0026",
|
|
10
|
+
[LS]: "\\u2028",
|
|
11
|
+
[PS]: "\\u2029"
|
|
12
|
+
};
|
|
13
|
+
function escapeJsonForScript(data) {
|
|
14
|
+
return JSON.stringify(data).replace(
|
|
15
|
+
JSON_LD_UNSAFE,
|
|
16
|
+
(ch) => JSON_LD_ESCAPES[ch]
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
function JsonLd({ data }) {
|
|
20
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
21
|
+
"script",
|
|
22
|
+
{
|
|
23
|
+
type: "application/ld+json",
|
|
24
|
+
dangerouslySetInnerHTML: { __html: escapeJsonForScript(data) }
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
exports.escapeJsonForScript = escapeJsonForScript; exports.JsonLd = JsonLd;
|
|
33
|
+
//# sourceMappingURL=chunk-MUHFEEYW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-cms-kit/dist/chunk-MUHFEEYW.js","../src/components/JsonLd.tsx"],"names":[],"mappings":"AAAA;ACgDI,+CAAA;AA1BJ,IAAM,GAAA,EAAK,MAAA,CAAO,YAAA,CAAa,IAAM,CAAA;AACrC,IAAM,GAAA,EAAK,MAAA,CAAO,YAAA,CAAa,IAAM,CAAA;AACrC,IAAM,eAAA,EAAiB,IAAI,MAAA,CAAO,CAAA,IAAA,EAAO,EAAE,CAAA,EAAA;AAEK;AACzC,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACA,EAAA;AACR;AAO2D;AAC7B,EAAA;AAC1B,IAAA;AAC0B,IAAA;AAC5B,EAAA;AACF;AAEoE;AAEhE,EAAA;AAAC,IAAA;AAAA,IAAA;AACM,MAAA;AAC8B,MAAA;AAA0B,IAAA;AAC/D,EAAA;AAEJ;AD1B0C;AACA;AACA;AACA;AACA","file":"/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-cms-kit/dist/chunk-MUHFEEYW.js","sourcesContent":[null,"/**\n * JSON-LD 구조화 데이터 스크립트 (spec.md §4.6 / Sprint 1 S2).\n *\n * `JSON.stringify(data)` 를 그대로 `<script>` 에 주입하면 `data` 값에 들어 있는\n * `</script>` / `<!--` / `<` / `&` / U+2028 / U+2029 가 스크립트 요소를\n * 탈출(breakout)시켜 저장/반사형 XSS 가 된다. 아래 escapeJsonForScript 가\n * 직렬화 결과를 안전하게 이스케이프한다 (codepoint 기반 — 소스에 비가시\n * 라인 종결자 문자를 두지 않는다). 이스케이프 대상:\n *\n * U+003C (escaped to backslash-u003c) -- blocks </script>, <!--, <\n * U+003E (escaped to backslash-u003e)\n * U+0026 (escaped to backslash-u0026)\n * U+2028 (escaped to backslash-u2028) -- JS string literal break guard\n * U+2029 (escaped to backslash-u2029)\n *\n * 이 치환들은 JSON 문자열 컨텍스트 안에서 유효한 \\uXXXX 이스케이프이므로,\n * 디코드된 스크립트 텍스트를 JSON.parse 하면 원본 data 와 정확히\n * deep-equal 한다 (round-trip 보존).\n */\n\n// 소스에 raw U+2028/U+2029 (JS line terminator) 를 두지 않도록 codepoint 으로\n// 문자 클래스를 구성한다.\nconst LS = String.fromCharCode(0x2028);\nconst PS = String.fromCharCode(0x2029);\nconst JSON_LD_UNSAFE = new RegExp(`[<>&${LS}${PS}]`, 'g');\n\nconst JSON_LD_ESCAPES: Record<string, string> = {\n '<': '\\\\u003c',\n '>': '\\\\u003e',\n '&': '\\\\u0026',\n [LS]: '\\\\u2028',\n [PS]: '\\\\u2029',\n};\n\n/**\n * `<script>` 안에 안전하게 넣을 수 있도록 직렬화된 JSON 을 이스케이프한다.\n * 결과는 여전히 유효한 `application/ld+json` 이며 `JSON.parse` 시 원본으로\n * 복원된다.\n */\nexport function escapeJsonForScript(data: unknown): string {\n return JSON.stringify(data).replace(\n JSON_LD_UNSAFE,\n (ch) => JSON_LD_ESCAPES[ch],\n );\n}\n\nexport function JsonLd({ data }: { data: Record<string, unknown> }) {\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: escapeJsonForScript(data) }}\n />\n );\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";//# sourceMappingURL=chunk-MUQYK5EO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-cms-kit/dist/chunk-MUQYK5EO.js"],"names":[],"mappings":"AAAA","file":"/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-cms-kit/dist/chunk-MUQYK5EO.js"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useImageDropZone
|
|
3
|
+
} from "./chunk-LF5LOI7K.mjs";
|
|
4
|
+
|
|
5
|
+
// src/components/ImageDropUpload.tsx
|
|
6
|
+
import { useRef } from "react";
|
|
7
|
+
import "./image-drop-zone.css";
|
|
8
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
function ImageDropUpload({
|
|
10
|
+
src,
|
|
11
|
+
onUpload,
|
|
12
|
+
onKeyTracked,
|
|
13
|
+
className = "",
|
|
14
|
+
placeholder = "\uD074\uB9AD \uB610\uB294 \uB4DC\uB798\uADF8\uD558\uC5EC \uC774\uBBF8\uC9C0 \uCD94\uAC00"
|
|
15
|
+
}) {
|
|
16
|
+
const inputRef = useRef(null);
|
|
17
|
+
const drop = useImageDropZone({
|
|
18
|
+
onUpload: (result) => onUpload(result.url, result.key),
|
|
19
|
+
onKeyTracked
|
|
20
|
+
});
|
|
21
|
+
const cls = [
|
|
22
|
+
className,
|
|
23
|
+
src ? "has" : "",
|
|
24
|
+
drop.isDragOver ? "is-drag-over" : "",
|
|
25
|
+
drop.isUploading ? "is-uploading" : "",
|
|
26
|
+
drop.isResizing ? "is-resizing" : ""
|
|
27
|
+
].filter(Boolean).join(" ");
|
|
28
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
29
|
+
/* @__PURE__ */ jsx(
|
|
30
|
+
"input",
|
|
31
|
+
{
|
|
32
|
+
ref: inputRef,
|
|
33
|
+
type: "file",
|
|
34
|
+
accept: "image/*",
|
|
35
|
+
style: { display: "none" },
|
|
36
|
+
onChange: async (e) => {
|
|
37
|
+
await drop.handleFileInput(e.target.files);
|
|
38
|
+
e.target.value = "";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
),
|
|
42
|
+
/* @__PURE__ */ jsx(
|
|
43
|
+
"div",
|
|
44
|
+
{
|
|
45
|
+
className: cls,
|
|
46
|
+
onClick: () => !drop.isUploading && !drop.isResizing && inputRef.current?.click(),
|
|
47
|
+
...drop.dragHandlers,
|
|
48
|
+
children: drop.isResizing ? /* @__PURE__ */ jsx("span", { className: "dz-upload-spinner", children: "\uCD5C\uC801\uD654 \uC911..." }) : drop.isUploading ? /* @__PURE__ */ jsx("span", { className: "dz-upload-spinner", children: "\uC5C5\uB85C\uB4DC \uC911..." }) : drop.isDragOver ? /* @__PURE__ */ jsx("span", { className: "dz-drag-hint", children: "\uB193\uC73C\uC138\uC694" }) : src ? (
|
|
49
|
+
/* eslint-disable-next-line @next/next/no-img-element */
|
|
50
|
+
/* @__PURE__ */ jsx("img", { src, alt: "", style: { maxWidth: "100%", height: "auto" } })
|
|
51
|
+
) : /* @__PURE__ */ jsxs("div", { className: "dz-placeholder", children: [
|
|
52
|
+
/* @__PURE__ */ jsx("div", { className: "dz-placeholder-icon", children: "+" }),
|
|
53
|
+
/* @__PURE__ */ jsx("div", { className: "dz-placeholder-text", children: placeholder }),
|
|
54
|
+
/* @__PURE__ */ jsxs("div", { className: "dz-placeholder-guide", children: [
|
|
55
|
+
"JPG, PNG, WebP, GIF | \uCD5C\uB300 5MB",
|
|
56
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
57
|
+
"\uCD08\uACFC \uC2DC \uC790\uB3D9 \uC555\uCD95 \xB7 1920px \uC774\uD558\uB85C \uB9AC\uC0AC\uC774\uC988",
|
|
58
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
59
|
+
"WebP \uBCC0\uD658 \xB7 4\uAC1C \uC0AC\uC774\uC988(lg/md/sm/thumb) \uC790\uB3D9 \uC0DD\uC131"
|
|
60
|
+
] })
|
|
61
|
+
] })
|
|
62
|
+
}
|
|
63
|
+
),
|
|
64
|
+
drop.error && /* @__PURE__ */ jsx("div", { className: "dz-error", children: drop.error })
|
|
65
|
+
] });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
ImageDropUpload
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=chunk-OBNALAMG.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ImageDropUpload.tsx"],"sourcesContent":["\"use client\";\n\nimport { useRef } from \"react\";\nimport { useImageDropZone } from \"../hooks/useImageDropZone\";\nimport \"./image-drop-zone.css\";\n\ninterface Props {\n /** Current image URL */\n src: string;\n /** Called with uploaded image URL and key */\n onUpload: (url: string, key: string) => void;\n /** Track uploaded key for orphan cleanup */\n onKeyTracked?: (key: string) => void;\n /** CSS class for the drop zone container */\n className?: string;\n /** Placeholder text when no image */\n placeholder?: string;\n}\n\nexport default function ImageDropUpload({\n src,\n onUpload,\n onKeyTracked,\n className = \"\",\n placeholder = \"클릭 또는 드래그하여 이미지 추가\",\n}: Props) {\n const inputRef = useRef<HTMLInputElement>(null);\n const drop = useImageDropZone({\n onUpload: (result) => onUpload(result.url, result.key),\n onKeyTracked,\n });\n\n const cls = [\n className,\n src ? \"has\" : \"\",\n drop.isDragOver ? \"is-drag-over\" : \"\",\n drop.isUploading ? \"is-uploading\" : \"\",\n drop.isResizing ? \"is-resizing\" : \"\",\n ].filter(Boolean).join(\" \");\n\n return (\n <>\n <input\n ref={inputRef}\n type=\"file\"\n accept=\"image/*\"\n style={{ display: \"none\" }}\n onChange={async (e) => {\n await drop.handleFileInput(e.target.files);\n e.target.value = \"\";\n }}\n />\n <div\n className={cls}\n onClick={() => !drop.isUploading && !drop.isResizing && inputRef.current?.click()}\n {...drop.dragHandlers}\n >\n {drop.isResizing ? (\n <span className=\"dz-upload-spinner\">최적화 중...</span>\n ) : drop.isUploading ? (\n <span className=\"dz-upload-spinner\">업로드 중...</span>\n ) : drop.isDragOver ? (\n <span className=\"dz-drag-hint\">놓으세요</span>\n ) : src ? (\n /* eslint-disable-next-line @next/next/no-img-element */\n <img src={src} alt=\"\" style={{ maxWidth: \"100%\", height: \"auto\" }} />\n ) : (\n <div className=\"dz-placeholder\">\n <div className=\"dz-placeholder-icon\">+</div>\n <div className=\"dz-placeholder-text\">{placeholder}</div>\n <div className=\"dz-placeholder-guide\">\n JPG, PNG, WebP, GIF | 최대 5MB\n <br />\n 초과 시 자동 압축 · 1920px 이하로 리사이즈\n <br />\n WebP 변환 · 4개 사이즈(lg/md/sm/thumb) 자동 생성\n </div>\n </div>\n )}\n </div>\n {drop.error && <div className=\"dz-error\">{drop.error}</div>}\n </>\n );\n}\n"],"mappings":";;;;;AAEA,SAAS,cAAc;AAEvB,OAAO;AAqCH,mBACE,KA4BM,YA7BR;AAtBW,SAAR,gBAAiC;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,cAAc;AAChB,GAAU;AACR,QAAM,WAAW,OAAyB,IAAI;AAC9C,QAAM,OAAO,iBAAiB;AAAA,IAC5B,UAAU,CAAC,WAAW,SAAS,OAAO,KAAK,OAAO,GAAG;AAAA,IACrD;AAAA,EACF,CAAC;AAED,QAAM,MAAM;AAAA,IACV;AAAA,IACA,MAAM,QAAQ;AAAA,IACd,KAAK,aAAa,iBAAiB;AAAA,IACnC,KAAK,cAAc,iBAAiB;AAAA,IACpC,KAAK,aAAa,gBAAgB;AAAA,EACpC,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAE1B,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,MAAK;AAAA,QACL,QAAO;AAAA,QACP,OAAO,EAAE,SAAS,OAAO;AAAA,QACzB,UAAU,OAAO,MAAM;AACrB,gBAAM,KAAK,gBAAgB,EAAE,OAAO,KAAK;AACzC,YAAE,OAAO,QAAQ;AAAA,QACnB;AAAA;AAAA,IACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,QACX,SAAS,MAAM,CAAC,KAAK,eAAe,CAAC,KAAK,cAAc,SAAS,SAAS,MAAM;AAAA,QAC/E,GAAG,KAAK;AAAA,QAER,eAAK,aACJ,oBAAC,UAAK,WAAU,qBAAoB,0CAAQ,IAC1C,KAAK,cACP,oBAAC,UAAK,WAAU,qBAAoB,0CAAQ,IAC1C,KAAK,aACP,oBAAC,UAAK,WAAU,gBAAe,sCAAI,IACjC;AAAA;AAAA,UAEF,oBAAC,SAAI,KAAU,KAAI,IAAG,OAAO,EAAE,UAAU,QAAQ,QAAQ,OAAO,GAAG;AAAA,YAEnE,qBAAC,SAAI,WAAU,kBACb;AAAA,8BAAC,SAAI,WAAU,uBAAsB,eAAC;AAAA,UACtC,oBAAC,SAAI,WAAU,uBAAuB,uBAAY;AAAA,UAClD,qBAAC,SAAI,WAAU,wBAAuB;AAAA;AAAA,YAEpC,oBAAC,QAAG;AAAA,YAAE;AAAA,YAEN,oBAAC,QAAG;AAAA,YAAE;AAAA,aAER;AAAA,WACF;AAAA;AAAA,IAEJ;AAAA,IACC,KAAK,SAAS,oBAAC,SAAI,WAAU,YAAY,eAAK,OAAM;AAAA,KACvD;AAEJ;","names":[]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/infrastructure/prisma.ts
|
|
2
|
+
var _prisma = null;
|
|
3
|
+
function setPrismaClient(client) {
|
|
4
|
+
_prisma = client;
|
|
5
|
+
}
|
|
6
|
+
function getPrisma() {
|
|
7
|
+
if (!_prisma) {
|
|
8
|
+
throw new Error(
|
|
9
|
+
"@withwiz/cms-kit: Prisma client not initialized. Call setPrismaClient() first."
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
return _prisma;
|
|
13
|
+
}
|
|
14
|
+
var prisma = new Proxy({}, {
|
|
15
|
+
get(_target, prop) {
|
|
16
|
+
return getPrisma()[prop];
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
exports.setPrismaClient = setPrismaClient; exports.getPrisma = getPrisma; exports.prisma = prisma;
|
|
25
|
+
//# sourceMappingURL=chunk-OLEYNPW6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-cms-kit/dist/chunk-OLEYNPW6.js","../src/infrastructure/prisma.ts"],"names":[],"mappings":"AAAA;ACEA,IAAI,QAAA,EAAe,IAAA;AAEZ,SAAS,eAAA,CAAgB,MAAA,EAAa;AAC3C,EAAA,QAAA,EAAU,MAAA;AACZ;AAEO,SAAS,SAAA,CAAA,EAAiB;AAC/B,EAAA,GAAA,CAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,IACF,CAAA;AAAA,EACF;AACA,EAAA,OAAO,OAAA;AACT;AAEO,IAAM,OAAA,EAAc,IAAI,KAAA,CAAM,CAAC,CAAA,EAAG;AAAA,EACvC,GAAA,CAAI,OAAA,EAAS,IAAA,EAAM;AACjB,IAAA,OAAO,SAAA,CAAU,CAAA,CAAE,IAAI,CAAA;AAAA,EACzB;AACF,CAAC,CAAA;ADHD;AACA;AACE;AACA;AACA;AACF,kGAAC","file":"/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-cms-kit/dist/chunk-OLEYNPW6.js","sourcesContent":[null,"/* eslint-disable @typescript-eslint/no-explicit-any */\n\nlet _prisma: any = null;\n\nexport function setPrismaClient(client: any) {\n _prisma = client;\n}\n\nexport function getPrisma(): any {\n if (!_prisma) {\n throw new Error(\n '@withwiz/cms-kit: Prisma client not initialized. Call setPrismaClient() first.'\n );\n }\n return _prisma;\n}\n\nexport const prisma: any = new Proxy({}, {\n get(_target, prop) {\n return getPrisma()[prop];\n },\n});\n"]}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
var _chunkEG6NVPP2js = require('./chunk-EG6NVPP2.js');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
var _chunkDGUM43GVjs = require('./chunk-DGUM43GV.js');
|
|
7
|
+
|
|
8
|
+
// src/utils/html-sanitizer.ts
|
|
9
|
+
var STRIP_TAGS_WITH_CONTENT = /(<\s*\/?\s*(script|object|embed|applet|form|input|textarea|select|button)\b[^>]*>)/gi;
|
|
10
|
+
var UNTRUSTED_IFRAME = /<\s*iframe\b[^>]*>[\s\S]*?<\s*\/\s*iframe\s*>/gi;
|
|
11
|
+
var STRIP_TAG_CONTENT = /<\s*(script|style)\b[^>]*>[\s\S]*?<\s*\/\s*\1\s*>/gi;
|
|
12
|
+
var EVENT_HANDLER_ATTRS = /\s+on\w+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]*)/gi;
|
|
13
|
+
var DANGEROUS_PROTOCOL = /(href|src|action)\s*=\s*["']\s*(javascript|vbscript|data\s*:(?!image\/))[^"']*["']/gi;
|
|
14
|
+
var cachedDomPurify;
|
|
15
|
+
function tryLoadDomPurify() {
|
|
16
|
+
if (cachedDomPurify !== void 0) return cachedDomPurify;
|
|
17
|
+
try {
|
|
18
|
+
const mod = _chunkDGUM43GVjs.__require.call(void 0, "isomorphic-dompurify");
|
|
19
|
+
const candidate = _nullishCoalesce(mod.default, () => ( mod));
|
|
20
|
+
if (candidate && typeof candidate.sanitize === "function") {
|
|
21
|
+
cachedDomPurify = candidate;
|
|
22
|
+
return candidate;
|
|
23
|
+
}
|
|
24
|
+
} catch (e) {
|
|
25
|
+
}
|
|
26
|
+
cachedDomPurify = null;
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
function regexSanitize(html, trustedOrigins) {
|
|
30
|
+
let result = html;
|
|
31
|
+
result = result.replace(STRIP_TAG_CONTENT, "");
|
|
32
|
+
result = result.replace(STRIP_TAGS_WITH_CONTENT, "");
|
|
33
|
+
result = result.replace(UNTRUSTED_IFRAME, (match) => {
|
|
34
|
+
const srcMatch = match.match(/\bsrc\s*=\s*["']([^"']+)["']/i);
|
|
35
|
+
if (srcMatch) {
|
|
36
|
+
const src = srcMatch[1].trim();
|
|
37
|
+
if (trustedOrigins.some((origin) => src.startsWith(origin))) {
|
|
38
|
+
return match;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return "";
|
|
42
|
+
});
|
|
43
|
+
result = result.replace(EVENT_HANDLER_ATTRS, "");
|
|
44
|
+
result = result.replace(DANGEROUS_PROTOCOL, '$1=""');
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
function dompurifySanitize(html, purify, trustedOrigins, config) {
|
|
48
|
+
const options = {
|
|
49
|
+
// iframe 은 hook 에서 origin 검증 후 허용
|
|
50
|
+
ADD_TAGS: ["iframe"],
|
|
51
|
+
ADD_ATTR: ["allowfullscreen", "frameborder", "allow"],
|
|
52
|
+
FORBID_TAGS: [
|
|
53
|
+
"script",
|
|
54
|
+
"object",
|
|
55
|
+
"embed",
|
|
56
|
+
"applet",
|
|
57
|
+
"form",
|
|
58
|
+
"input",
|
|
59
|
+
"textarea",
|
|
60
|
+
"select",
|
|
61
|
+
"button",
|
|
62
|
+
"style"
|
|
63
|
+
],
|
|
64
|
+
FORBID_ATTR: [
|
|
65
|
+
"onerror",
|
|
66
|
+
"onload",
|
|
67
|
+
"onclick",
|
|
68
|
+
"onmouseover",
|
|
69
|
+
"onfocus",
|
|
70
|
+
"onblur"
|
|
71
|
+
]
|
|
72
|
+
};
|
|
73
|
+
if (config.allowedTags) options.ALLOWED_TAGS = config.allowedTags;
|
|
74
|
+
if (config.allowedAttributes) {
|
|
75
|
+
const flat = /* @__PURE__ */ new Set();
|
|
76
|
+
for (const attrs of Object.values(config.allowedAttributes)) {
|
|
77
|
+
for (const a of attrs) flat.add(a);
|
|
78
|
+
}
|
|
79
|
+
options.ALLOWED_ATTR = Array.from(flat);
|
|
80
|
+
}
|
|
81
|
+
const purifyWithHook = purify;
|
|
82
|
+
if (typeof purifyWithHook.addHook === "function") {
|
|
83
|
+
purifyWithHook.addHook("uponSanitizeElement", (node) => {
|
|
84
|
+
if (node.nodeName && node.nodeName.toLowerCase() === "iframe") {
|
|
85
|
+
const src = _nullishCoalesce(_optionalChain([node, 'access', _ => _.getAttribute, 'optionalCall', _2 => _2("src")]), () => ( ""));
|
|
86
|
+
const trusted = trustedOrigins.some(
|
|
87
|
+
(origin) => src.startsWith(origin)
|
|
88
|
+
);
|
|
89
|
+
if (!trusted) {
|
|
90
|
+
_optionalChain([node, 'access', _3 => _3.remove, 'optionalCall', _4 => _4()]);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
return purify.sanitize(html, options);
|
|
97
|
+
} finally {
|
|
98
|
+
if (typeof purifyWithHook.removeHook === "function") {
|
|
99
|
+
purifyWithHook.removeHook("uponSanitizeElement");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function createSanitizer(config = {}) {
|
|
104
|
+
return function sanitize(html) {
|
|
105
|
+
if (!html) return html;
|
|
106
|
+
const trustedOrigins = _nullishCoalesce(config.trustedIframeOrigins, () => ( _chunkEG6NVPP2js.resolveTrustedIframeOrigins.call(void 0, )));
|
|
107
|
+
const purify = tryLoadDomPurify();
|
|
108
|
+
if (purify) {
|
|
109
|
+
return dompurifySanitize(html, purify, trustedOrigins, config);
|
|
110
|
+
}
|
|
111
|
+
return regexSanitize(html, trustedOrigins);
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function sanitizeHtmlContent(html) {
|
|
115
|
+
return defaultSanitize(html);
|
|
116
|
+
}
|
|
117
|
+
var defaultSanitize = createSanitizer();
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
exports.createSanitizer = createSanitizer; exports.sanitizeHtmlContent = sanitizeHtmlContent;
|
|
123
|
+
//# sourceMappingURL=chunk-OYJRR3G2.js.map
|