dn-react-router-toolkit 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.md +1 -0
- package/dist/cjs/auth-kit/apple_auth.js +60 -0
- package/dist/cjs/auth-kit/auth_service.js +204 -0
- package/dist/cjs/auth-kit/client/google_login_button.js +25 -0
- package/dist/cjs/auth-kit/client/redirect_page.js +21 -0
- package/dist/cjs/auth-kit/google_auth.js +65 -0
- package/dist/cjs/auth-kit/jwt.js +53 -0
- package/dist/cjs/auth-kit/kakao_auth.js +38 -0
- package/dist/cjs/auth-kit/repository.js +2 -0
- package/dist/cjs/auth-kit/with_auth.js +46 -0
- package/dist/cjs/cn.js +6 -0
- package/dist/cjs/components/index.js +18 -0
- package/dist/cjs/components/modal/fullscreen_container.js +64 -0
- package/dist/cjs/components/modal/hooks.js +78 -0
- package/dist/cjs/components/modal/index.js +19 -0
- package/dist/cjs/components/modal/modal.js +91 -0
- package/dist/cjs/components/styles.js +10 -0
- package/dist/cjs/date.js +31 -0
- package/dist/cjs/file-kit/cdn.js +9 -0
- package/dist/cjs/file-kit/client/drop_file_input.js +195 -0
- package/dist/cjs/file-kit/client/file_uploader.js +78 -0
- package/dist/cjs/file-kit/file_service.js +29 -0
- package/dist/cjs/file-kit/object_storage.js +50 -0
- package/dist/cjs/file-kit/repository.js +2 -0
- package/dist/cjs/file-kit/responsive_image.js +78 -0
- package/dist/cjs/http-kit/index.js +17 -0
- package/dist/cjs/http-kit/response.js +34 -0
- package/dist/cjs/index.js +20 -0
- package/dist/cjs/route/api/auth/login/[provider]/route.js +36 -0
- package/dist/cjs/route/api/auth/login/route.js +35 -0
- package/dist/cjs/route/api/auth/logout/route.js +22 -0
- package/dist/cjs/route/api/auth/refresh/route.js +23 -0
- package/dist/cjs/route/api/auth/route.js +12 -0
- package/dist/cjs/route/api/files/[fileId]/route.js +20 -0
- package/dist/cjs/route/api/files/route.js +34 -0
- package/dist/cjs/route/auth/callback/[provider]/route.js +35 -0
- package/dist/cjs/route/index.js +80 -0
- package/dist/cjs/seo-kit/index.js +19 -0
- package/dist/cjs/seo-kit/loader.js +17 -0
- package/dist/cjs/seo-kit/seo.js +286 -0
- package/dist/cjs/seo-kit/seo_loader.js +19 -0
- package/dist/cjs/singleton.js +12 -0
- package/dist/cjs/slug.js +10 -0
- package/dist/esm/auth-kit/apple_auth.d.ts +15 -0
- package/dist/esm/auth-kit/apple_auth.js +56 -0
- package/dist/esm/auth-kit/auth_service.d.ts +67 -0
- package/dist/esm/auth-kit/auth_service.js +197 -0
- package/dist/esm/auth-kit/client/google_login_button.d.ts +6 -0
- package/dist/esm/auth-kit/client/google_login_button.js +19 -0
- package/dist/esm/auth-kit/client/redirect_page.d.ts +2 -0
- package/dist/esm/auth-kit/client/redirect_page.js +15 -0
- package/dist/esm/auth-kit/google_auth.d.ts +18 -0
- package/dist/esm/auth-kit/google_auth.js +61 -0
- package/dist/esm/auth-kit/jwt.d.ts +15 -0
- package/dist/esm/auth-kit/jwt.js +49 -0
- package/dist/esm/auth-kit/kakao_auth.d.ts +15 -0
- package/dist/esm/auth-kit/kakao_auth.js +34 -0
- package/dist/esm/auth-kit/repository.d.ts +40 -0
- package/dist/esm/auth-kit/repository.js +1 -0
- package/dist/esm/auth-kit/with_auth.d.ts +12 -0
- package/dist/esm/auth-kit/with_auth.js +43 -0
- package/dist/esm/cn.d.ts +1 -0
- package/dist/esm/cn.js +3 -0
- package/dist/esm/components/index.d.ts +2 -0
- package/dist/esm/components/index.js +2 -0
- package/dist/esm/components/modal/fullscreen_container.d.ts +5 -0
- package/dist/esm/components/modal/fullscreen_container.js +57 -0
- package/dist/esm/components/modal/hooks.d.ts +15 -0
- package/dist/esm/components/modal/hooks.js +69 -0
- package/dist/esm/components/modal/index.d.ts +3 -0
- package/dist/esm/components/modal/index.js +3 -0
- package/dist/esm/components/modal/modal.d.ts +10 -0
- package/dist/esm/components/modal/modal.js +55 -0
- package/dist/esm/components/styles.d.ts +7 -0
- package/dist/esm/components/styles.js +7 -0
- package/dist/esm/date.d.ts +1 -0
- package/dist/esm/date.js +24 -0
- package/dist/esm/file-kit/cdn.d.ts +3 -0
- package/dist/esm/file-kit/cdn.js +5 -0
- package/dist/esm/file-kit/client/drop_file_input.d.ts +31 -0
- package/dist/esm/file-kit/client/drop_file_input.js +158 -0
- package/dist/esm/file-kit/client/file_uploader.d.ts +11 -0
- package/dist/esm/file-kit/client/file_uploader.js +74 -0
- package/dist/esm/file-kit/file_service.d.ts +23 -0
- package/dist/esm/file-kit/file_service.js +25 -0
- package/dist/esm/file-kit/object_storage.d.ts +13 -0
- package/dist/esm/file-kit/object_storage.js +46 -0
- package/dist/esm/file-kit/repository.d.ts +14 -0
- package/dist/esm/file-kit/repository.js +1 -0
- package/dist/esm/file-kit/responsive_image.d.ts +17 -0
- package/dist/esm/file-kit/responsive_image.js +70 -0
- package/dist/esm/http-kit/index.d.ts +1 -0
- package/dist/esm/http-kit/index.js +1 -0
- package/dist/esm/http-kit/response.d.ts +17 -0
- package/dist/esm/http-kit/response.js +28 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/route/api/auth/login/[provider]/route.d.ts +10 -0
- package/dist/esm/route/api/auth/login/[provider]/route.js +32 -0
- package/dist/esm/route/api/auth/login/route.d.ts +6 -0
- package/dist/esm/route/api/auth/login/route.js +31 -0
- package/dist/esm/route/api/auth/logout/route.d.ts +6 -0
- package/dist/esm/route/api/auth/logout/route.js +18 -0
- package/dist/esm/route/api/auth/refresh/route.d.ts +4 -0
- package/dist/esm/route/api/auth/refresh/route.js +19 -0
- package/dist/esm/route/api/auth/route.d.ts +4 -0
- package/dist/esm/route/api/auth/route.js +8 -0
- package/dist/esm/route/api/files/[fileId]/route.d.ts +8 -0
- package/dist/esm/route/api/files/[fileId]/route.js +16 -0
- package/dist/esm/route/api/files/route.d.ts +6 -0
- package/dist/esm/route/api/files/route.js +30 -0
- package/dist/esm/route/auth/callback/[provider]/route.d.ts +11 -0
- package/dist/esm/route/auth/callback/[provider]/route.js +31 -0
- package/dist/esm/route/index.d.ts +22 -0
- package/dist/esm/route/index.js +76 -0
- package/dist/esm/seo-kit/index.d.ts +3 -0
- package/dist/esm/seo-kit/index.js +3 -0
- package/dist/esm/seo-kit/loader.d.ts +5 -0
- package/dist/esm/seo-kit/loader.js +14 -0
- package/dist/esm/seo-kit/seo.d.ts +100 -0
- package/dist/esm/seo-kit/seo.js +280 -0
- package/dist/esm/seo-kit/seo_loader.d.ts +12 -0
- package/dist/esm/seo-kit/seo_loader.js +13 -0
- package/dist/esm/singleton.d.ts +1 -0
- package/dist/esm/singleton.js +9 -0
- package/dist/esm/slug.d.ts +1 -0
- package/dist/esm/slug.js +6 -0
- package/package.json +81 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.configSEO = configSEO;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
function configSEO(config) {
|
|
9
|
+
return {
|
|
10
|
+
config,
|
|
11
|
+
async init(props = {}) {
|
|
12
|
+
var _a;
|
|
13
|
+
const canonicalPath = props.canonicalPath;
|
|
14
|
+
const url = canonicalPath
|
|
15
|
+
? `${config.origin}${canonicalPath}`
|
|
16
|
+
: `${config.origin}${props.path || ""}`;
|
|
17
|
+
const pageTitle = props.title
|
|
18
|
+
? `${props.title} | ${config.siteName}`
|
|
19
|
+
: config.siteName;
|
|
20
|
+
const description = props.description || config.description;
|
|
21
|
+
const keywords = props.keywords || config.keywords;
|
|
22
|
+
const websiteSchemaId = `${config.origin}/#website`;
|
|
23
|
+
const websiteSchema = {
|
|
24
|
+
"@type": "WebSite",
|
|
25
|
+
"@id": websiteSchemaId,
|
|
26
|
+
url: config.origin,
|
|
27
|
+
name: config.siteName,
|
|
28
|
+
alternateName: config.siteName,
|
|
29
|
+
description: config.description,
|
|
30
|
+
inLanguage: "ko",
|
|
31
|
+
};
|
|
32
|
+
const thumbnailSchemaId = `${url}/#thumbnail`;
|
|
33
|
+
const thumbnail = props.thumbnail ||
|
|
34
|
+
(typeof config.thumbnail === "function"
|
|
35
|
+
? await config.thumbnail()
|
|
36
|
+
: config.thumbnail);
|
|
37
|
+
const thumbnailUrl = thumbnail === null || thumbnail === void 0 ? void 0 : thumbnail.url;
|
|
38
|
+
const socialImage = thumbnail
|
|
39
|
+
? [
|
|
40
|
+
typeof thumbnail === "string"
|
|
41
|
+
? {
|
|
42
|
+
url: thumbnail,
|
|
43
|
+
alt: `${props.title} 대표 이미지`,
|
|
44
|
+
}
|
|
45
|
+
: thumbnail,
|
|
46
|
+
]
|
|
47
|
+
: [];
|
|
48
|
+
const images = [
|
|
49
|
+
...(thumbnailUrl
|
|
50
|
+
? [
|
|
51
|
+
{
|
|
52
|
+
id: thumbnailSchemaId,
|
|
53
|
+
url: thumbnailUrl,
|
|
54
|
+
alt: `${props.title} 대표 이미지`,
|
|
55
|
+
},
|
|
56
|
+
]
|
|
57
|
+
: []),
|
|
58
|
+
...(props.images || []),
|
|
59
|
+
...(((_a = props.collection) === null || _a === void 0 ? void 0 : _a.map((portfolio) => portfolio.thumbnail)) || []),
|
|
60
|
+
].filter(Boolean);
|
|
61
|
+
const image = images
|
|
62
|
+
.filter((file) => file.id)
|
|
63
|
+
.map((file) => ({
|
|
64
|
+
"@type": "ImageObject",
|
|
65
|
+
"@id": `${url}/#${file.id}`,
|
|
66
|
+
}));
|
|
67
|
+
const breadcrumbSchemaId = `${url}#breadcrumb`;
|
|
68
|
+
const updatedAt = props.updatedAt;
|
|
69
|
+
const createdAt = props.createdAt;
|
|
70
|
+
const meta = () => {
|
|
71
|
+
return [
|
|
72
|
+
{ title: pageTitle },
|
|
73
|
+
{ name: "description", content: description },
|
|
74
|
+
{ name: "keywords", content: (keywords === null || keywords === void 0 ? void 0 : keywords.join(", ")) || "" },
|
|
75
|
+
{
|
|
76
|
+
tagName: "link",
|
|
77
|
+
rel: "canonical",
|
|
78
|
+
href: url,
|
|
79
|
+
},
|
|
80
|
+
{ property: "og:type", content: props.type || "website" },
|
|
81
|
+
{ property: "og:title", content: pageTitle },
|
|
82
|
+
{ property: "og:description", content: description },
|
|
83
|
+
{ property: "og:url", content: url },
|
|
84
|
+
{ property: "og:site_name", content: config.siteName },
|
|
85
|
+
...socialImage.map((file) => ({
|
|
86
|
+
property: "og:image",
|
|
87
|
+
content: file.url,
|
|
88
|
+
})),
|
|
89
|
+
{
|
|
90
|
+
property: "twitter:card",
|
|
91
|
+
content: socialImage.length > 0 ? "summary_large_image" : "summary",
|
|
92
|
+
},
|
|
93
|
+
{ property: "twitter:title", content: pageTitle },
|
|
94
|
+
{ property: "twitter:description", content: description },
|
|
95
|
+
...socialImage.map((file) => ({
|
|
96
|
+
property: "twitter:image",
|
|
97
|
+
content: file.url,
|
|
98
|
+
})),
|
|
99
|
+
];
|
|
100
|
+
};
|
|
101
|
+
const additionalStructedData = [
|
|
102
|
+
...(typeof props.structedData === "function"
|
|
103
|
+
? await props.structedData()
|
|
104
|
+
: props.structedData || []),
|
|
105
|
+
...(typeof config.structedData === "function"
|
|
106
|
+
? await config.structedData()
|
|
107
|
+
: config.structedData || []),
|
|
108
|
+
];
|
|
109
|
+
function getStructedData() {
|
|
110
|
+
const collectionMainEntity = props.collection
|
|
111
|
+
? {
|
|
112
|
+
"@type": "ItemList",
|
|
113
|
+
numberOfItems: props.collection.length,
|
|
114
|
+
itemListElement: props.collection.map((item, index) => {
|
|
115
|
+
var _a, _b, _c, _d;
|
|
116
|
+
return ({
|
|
117
|
+
"@type": "ListItem",
|
|
118
|
+
position: index + 1,
|
|
119
|
+
url: item.url,
|
|
120
|
+
item: {
|
|
121
|
+
"@type": "WebPage",
|
|
122
|
+
"@id": `${item.url}#webpage`,
|
|
123
|
+
url: item.url,
|
|
124
|
+
name: item.title,
|
|
125
|
+
thumbnailUrl: (_a = item.thumbnail) === null || _a === void 0 ? void 0 : _a.url,
|
|
126
|
+
dateModified: (_b = item.updatedAt) === null || _b === void 0 ? void 0 : _b.toISOString(),
|
|
127
|
+
dateCreated: (_c = item.createdAt) === null || _c === void 0 ? void 0 : _c.toISOString(),
|
|
128
|
+
datePublished: (_d = item.createdAt) === null || _d === void 0 ? void 0 : _d.toISOString(),
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
}),
|
|
132
|
+
}
|
|
133
|
+
: undefined;
|
|
134
|
+
return {
|
|
135
|
+
"@context": "https://schema.org",
|
|
136
|
+
"@graph": [
|
|
137
|
+
{
|
|
138
|
+
"@type": "WebPage",
|
|
139
|
+
"@id": `${url}#webpage`,
|
|
140
|
+
url,
|
|
141
|
+
name: pageTitle,
|
|
142
|
+
description,
|
|
143
|
+
keywords: keywords,
|
|
144
|
+
dateModified: updatedAt === null || updatedAt === void 0 ? void 0 : updatedAt.toISOString(),
|
|
145
|
+
datePublished: createdAt === null || createdAt === void 0 ? void 0 : createdAt.toISOString(),
|
|
146
|
+
dateCreated: createdAt === null || createdAt === void 0 ? void 0 : createdAt.toISOString(),
|
|
147
|
+
primaryImageOfPage: thumbnail
|
|
148
|
+
? {
|
|
149
|
+
"@id": thumbnailSchemaId,
|
|
150
|
+
}
|
|
151
|
+
: undefined,
|
|
152
|
+
isPartOf: {
|
|
153
|
+
"@id": websiteSchemaId,
|
|
154
|
+
},
|
|
155
|
+
inLanguage: "ko",
|
|
156
|
+
breadcrumb: props.breadcrumbs && props.breadcrumbs.length > 0
|
|
157
|
+
? {
|
|
158
|
+
"@id": breadcrumbSchemaId,
|
|
159
|
+
}
|
|
160
|
+
: undefined,
|
|
161
|
+
image,
|
|
162
|
+
mainEntity: collectionMainEntity,
|
|
163
|
+
},
|
|
164
|
+
props.article &&
|
|
165
|
+
{
|
|
166
|
+
"@type": "Article",
|
|
167
|
+
"@id": `${url}#article`,
|
|
168
|
+
url,
|
|
169
|
+
name: pageTitle,
|
|
170
|
+
description,
|
|
171
|
+
keywords: keywords,
|
|
172
|
+
headline: pageTitle,
|
|
173
|
+
dateModified: updatedAt === null || updatedAt === void 0 ? void 0 : updatedAt.toISOString(),
|
|
174
|
+
datePublished: createdAt === null || createdAt === void 0 ? void 0 : createdAt.toISOString(),
|
|
175
|
+
dateCreated: createdAt === null || createdAt === void 0 ? void 0 : createdAt.toISOString(),
|
|
176
|
+
isPartOf: {
|
|
177
|
+
"@id": websiteSchemaId,
|
|
178
|
+
},
|
|
179
|
+
inLanguage: "ko",
|
|
180
|
+
image,
|
|
181
|
+
},
|
|
182
|
+
props.blogPosting &&
|
|
183
|
+
{
|
|
184
|
+
"@type": "BlogPosting",
|
|
185
|
+
"@id": `${url}/#blogposting`,
|
|
186
|
+
url,
|
|
187
|
+
headline: pageTitle,
|
|
188
|
+
description: description,
|
|
189
|
+
thumbnailUrl,
|
|
190
|
+
datePublished: createdAt === null || createdAt === void 0 ? void 0 : createdAt.toISOString(),
|
|
191
|
+
dateModified: updatedAt === null || updatedAt === void 0 ? void 0 : updatedAt.toISOString(),
|
|
192
|
+
isPartOf: { "@id": websiteSchemaId },
|
|
193
|
+
image,
|
|
194
|
+
},
|
|
195
|
+
props.creativeWork &&
|
|
196
|
+
{
|
|
197
|
+
"@type": "CreativeWork",
|
|
198
|
+
"@id": `${url}/#creativework`,
|
|
199
|
+
url,
|
|
200
|
+
name: pageTitle,
|
|
201
|
+
description: description,
|
|
202
|
+
datePublished: createdAt === null || createdAt === void 0 ? void 0 : createdAt.toISOString(),
|
|
203
|
+
dateModified: updatedAt === null || updatedAt === void 0 ? void 0 : updatedAt.toISOString(),
|
|
204
|
+
isPartOf: { "@id": websiteSchemaId },
|
|
205
|
+
thumbnailUrl,
|
|
206
|
+
image,
|
|
207
|
+
},
|
|
208
|
+
props.visualArtwork &&
|
|
209
|
+
{
|
|
210
|
+
"@type": "VisualArtwork",
|
|
211
|
+
"@id": `${url}/#visualartwork`,
|
|
212
|
+
url,
|
|
213
|
+
name: pageTitle,
|
|
214
|
+
description: description,
|
|
215
|
+
datePublished: createdAt === null || createdAt === void 0 ? void 0 : createdAt.toISOString(),
|
|
216
|
+
dateModified: updatedAt === null || updatedAt === void 0 ? void 0 : updatedAt.toISOString(),
|
|
217
|
+
isPartOf: { "@id": websiteSchemaId },
|
|
218
|
+
thumbnailUrl,
|
|
219
|
+
image,
|
|
220
|
+
},
|
|
221
|
+
props.collection &&
|
|
222
|
+
{
|
|
223
|
+
"@type": "CollectionPage",
|
|
224
|
+
"@id": `${url}/#collectionpage`,
|
|
225
|
+
url: url,
|
|
226
|
+
name: pageTitle,
|
|
227
|
+
description: description,
|
|
228
|
+
isPartOf: { "@id": websiteSchemaId },
|
|
229
|
+
image,
|
|
230
|
+
inLanguage: "ko-KR",
|
|
231
|
+
breadcrumb: props.breadcrumbs && props.breadcrumbs.length > 0
|
|
232
|
+
? {
|
|
233
|
+
"@id": `${url}/#breadcrumb`,
|
|
234
|
+
}
|
|
235
|
+
: undefined,
|
|
236
|
+
mainEntity: collectionMainEntity,
|
|
237
|
+
},
|
|
238
|
+
websiteSchema,
|
|
239
|
+
props.breadcrumbs &&
|
|
240
|
+
props.breadcrumbs.length > 0 &&
|
|
241
|
+
{
|
|
242
|
+
"@id": breadcrumbSchemaId,
|
|
243
|
+
"@type": "BreadcrumbList",
|
|
244
|
+
itemListElement: props.breadcrumbs.map((breadcrumb, index) => ({
|
|
245
|
+
"@type": "ListItem",
|
|
246
|
+
position: index + 1,
|
|
247
|
+
name: breadcrumb.label,
|
|
248
|
+
item: `${config.origin}${breadcrumb.href}`,
|
|
249
|
+
})),
|
|
250
|
+
},
|
|
251
|
+
...additionalStructedData,
|
|
252
|
+
...images.map((file) => {
|
|
253
|
+
var _a, _b;
|
|
254
|
+
return {
|
|
255
|
+
"@type": "ImageObject",
|
|
256
|
+
"@id": `${url}/#${file.id}`,
|
|
257
|
+
url: file.url,
|
|
258
|
+
contentUrl: file.url,
|
|
259
|
+
name: file.alt,
|
|
260
|
+
description: file.alt,
|
|
261
|
+
width: (_a = file === null || file === void 0 ? void 0 : file.width) === null || _a === void 0 ? void 0 : _a.toString(),
|
|
262
|
+
height: (_b = file === null || file === void 0 ? void 0 : file.height) === null || _b === void 0 ? void 0 : _b.toString(),
|
|
263
|
+
creditText: config.copyright,
|
|
264
|
+
license: `${config.origin}/terms`,
|
|
265
|
+
copyrightNotice: config.copyright,
|
|
266
|
+
acquireLicensePage: `${config.origin}/terms`,
|
|
267
|
+
};
|
|
268
|
+
}),
|
|
269
|
+
].filter(Boolean),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function generateJSONLD() {
|
|
273
|
+
const structedData = getStructedData();
|
|
274
|
+
return (react_1.default.createElement("script", { type: "application/ld+json", dangerouslySetInnerHTML: {
|
|
275
|
+
__html: JSON.stringify(structedData).replace(/</g, "\\u003c"),
|
|
276
|
+
} }));
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
config,
|
|
280
|
+
props,
|
|
281
|
+
meta,
|
|
282
|
+
generateJSONLD,
|
|
283
|
+
};
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createSEOLoader = createSEOLoader;
|
|
7
|
+
const loader_1 = require("./loader");
|
|
8
|
+
const react_1 = __importDefault(require("react"));
|
|
9
|
+
function createSEOLoader(fn = () => ({})) {
|
|
10
|
+
const loader = (0, loader_1.createLoader)(fn);
|
|
11
|
+
return Object.assign(Object.assign({}, loader), { meta: loader.wrap((data) => data.seo.meta), Page(Fc) {
|
|
12
|
+
return loader.wrap((data) => {
|
|
13
|
+
var _a;
|
|
14
|
+
return (react_1.default.createElement(react_1.default.Fragment, null, (_a = data.seo) === null || _a === void 0 ? void 0 :
|
|
15
|
+
_a.generateJSONLD(),
|
|
16
|
+
react_1.default.createElement(Fc, Object.assign({}, data))));
|
|
17
|
+
});
|
|
18
|
+
} });
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.singleton = singleton;
|
|
4
|
+
function singleton(name, fn) {
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
+
const globalAny = globalThis;
|
|
7
|
+
globalAny.__singletons = globalAny.__singletons || {};
|
|
8
|
+
if (!globalAny.__singletons[name]) {
|
|
9
|
+
globalAny.__singletons[name] = fn();
|
|
10
|
+
}
|
|
11
|
+
return globalAny.__singletons[name];
|
|
12
|
+
}
|
package/dist/cjs/slug.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toSlug = void 0;
|
|
4
|
+
const toSlug = (str) => {
|
|
5
|
+
return str
|
|
6
|
+
.toLowerCase()
|
|
7
|
+
.replace(/[^a-zA-Z0-9가-힣ㄱ-ㅎㅏ-ㅣ]+/g, "-")
|
|
8
|
+
.replace(/^-|-$/g, "");
|
|
9
|
+
};
|
|
10
|
+
exports.toSlug = toSlug;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { AuthService } from "./auth_service";
|
|
2
|
+
export declare class AppleAuth {
|
|
3
|
+
AUTH: AuthService;
|
|
4
|
+
constructor(AUTH: AuthService);
|
|
5
|
+
signIn(code: string, type?: "web" | "app"): Promise<{
|
|
6
|
+
user: {
|
|
7
|
+
id: string;
|
|
8
|
+
role: string;
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
|
11
|
+
accessToken: string;
|
|
12
|
+
refreshToken: string;
|
|
13
|
+
}>;
|
|
14
|
+
private generateAppleClientSecret;
|
|
15
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { decodeJwt, importPKCS8, SignJWT } from "jose";
|
|
2
|
+
export class AppleAuth {
|
|
3
|
+
constructor(AUTH) {
|
|
4
|
+
this.AUTH = AUTH;
|
|
5
|
+
}
|
|
6
|
+
async signIn(code, type = "web") {
|
|
7
|
+
const url = "https://appleid.apple.com/auth/token";
|
|
8
|
+
const client_secret = await this.generateAppleClientSecret(type);
|
|
9
|
+
const res = await fetch(url, {
|
|
10
|
+
method: "POST",
|
|
11
|
+
headers: {
|
|
12
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
13
|
+
},
|
|
14
|
+
body: new URLSearchParams({
|
|
15
|
+
client_id: type === "web"
|
|
16
|
+
? process.env.APPLE_SERVICE_ID
|
|
17
|
+
: process.env.APPLE_CLIENT_ID,
|
|
18
|
+
client_secret,
|
|
19
|
+
code,
|
|
20
|
+
grant_type: "authorization_code",
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
if (!res.ok) {
|
|
24
|
+
console.error(await res.text());
|
|
25
|
+
throw new Error("Apple 인증에 실패했습니다.");
|
|
26
|
+
}
|
|
27
|
+
const data = await res.json();
|
|
28
|
+
const { id_token } = data;
|
|
29
|
+
const payload = decodeJwt(id_token);
|
|
30
|
+
const { sub, email, name } = payload;
|
|
31
|
+
const user = await this.AUTH.getOrCreateUser("apple", {
|
|
32
|
+
id: sub,
|
|
33
|
+
email,
|
|
34
|
+
name,
|
|
35
|
+
});
|
|
36
|
+
const { accessToken, refreshToken } = await this.AUTH.issueTokenPair(user);
|
|
37
|
+
return { user, accessToken, refreshToken };
|
|
38
|
+
}
|
|
39
|
+
async generateAppleClientSecret(type = "web") {
|
|
40
|
+
const authKey = process.env.APPLE_AUTH_KEY;
|
|
41
|
+
const teamId = process.env.APPLE_TEAM_ID;
|
|
42
|
+
const keyId = process.env.APPLE_KEY_ID;
|
|
43
|
+
const clientId = type === "web"
|
|
44
|
+
? process.env.APPLE_SERVICE_ID
|
|
45
|
+
: process.env.APPLE_CLIENT_ID;
|
|
46
|
+
const keyObject = await importPKCS8(authKey, "ES256");
|
|
47
|
+
return new SignJWT()
|
|
48
|
+
.setProtectedHeader({ alg: "ES256", kid: keyId })
|
|
49
|
+
.setIssuedAt()
|
|
50
|
+
.setIssuer(teamId)
|
|
51
|
+
.setExpirationTime("1h")
|
|
52
|
+
.setAudience("https://appleid.apple.com")
|
|
53
|
+
.setSubject(clientId)
|
|
54
|
+
.sign(keyObject);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { JWTManager } from "./jwt";
|
|
2
|
+
import { ObjectStorage } from "../file-kit/object_storage";
|
|
3
|
+
import { type AuthRepository } from "./repository";
|
|
4
|
+
export declare const ACCESS_TOKEN_KEY = "access_token";
|
|
5
|
+
export declare const REFRESH_TOKEN_KEY = "refresh_token";
|
|
6
|
+
export declare const ACCESS_TOKEN_COOKIE: import("react-router").Cookie;
|
|
7
|
+
export declare const REFRESH_TOKEN_COOKIE: import("react-router").Cookie;
|
|
8
|
+
export declare class AuthService {
|
|
9
|
+
repository: AuthRepository;
|
|
10
|
+
JWT_MANAGER: JWTManager;
|
|
11
|
+
OBJECT_STORAGE: ObjectStorage;
|
|
12
|
+
constructor({ repository, JWT_MANAGER, OBJECT_STORAGE, }: {
|
|
13
|
+
repository: AuthRepository;
|
|
14
|
+
JWT_MANAGER: JWTManager;
|
|
15
|
+
OBJECT_STORAGE: ObjectStorage;
|
|
16
|
+
});
|
|
17
|
+
verify(request: Request): Promise<import("jose").JWTPayload | undefined>;
|
|
18
|
+
verifyOrRefresh(request: Request): Promise<import("jose").JWTPayload | undefined>;
|
|
19
|
+
getAccessTokenFromCookies(request: Request): Promise<any>;
|
|
20
|
+
getRefreshTokenFromCookies(request: Request): Promise<any>;
|
|
21
|
+
refresh(request: Request): Promise<import("jose").JWTPayload | undefined>;
|
|
22
|
+
signIn({ id, password }: {
|
|
23
|
+
id: string;
|
|
24
|
+
password: string;
|
|
25
|
+
}): Promise<{
|
|
26
|
+
accessToken: string;
|
|
27
|
+
refreshToken: string;
|
|
28
|
+
}>;
|
|
29
|
+
issueAccessToken(user: {
|
|
30
|
+
id: string;
|
|
31
|
+
role: string;
|
|
32
|
+
name: string;
|
|
33
|
+
}): Promise<string>;
|
|
34
|
+
issueTokenPair(user: {
|
|
35
|
+
id: string;
|
|
36
|
+
role: string;
|
|
37
|
+
name: string;
|
|
38
|
+
}): Promise<{
|
|
39
|
+
refreshToken: string;
|
|
40
|
+
accessToken: string;
|
|
41
|
+
}>;
|
|
42
|
+
refreshAccessToken(refreshToken: string): Promise<string>;
|
|
43
|
+
getAccessTokenSetCookie(accessToken: string | null): Promise<string>;
|
|
44
|
+
getRefreshTokenSetCookie(refreshToken: string | null): Promise<string>;
|
|
45
|
+
findUser(provider: string, info: {
|
|
46
|
+
id: string;
|
|
47
|
+
email: string;
|
|
48
|
+
name?: string;
|
|
49
|
+
picture?: string;
|
|
50
|
+
}): Promise<{
|
|
51
|
+
id: string;
|
|
52
|
+
role: string;
|
|
53
|
+
name: string;
|
|
54
|
+
refreshToken: string | null;
|
|
55
|
+
} | undefined>;
|
|
56
|
+
getOrCreateUser(provider: string, info: {
|
|
57
|
+
id: string;
|
|
58
|
+
email: string;
|
|
59
|
+
name?: string;
|
|
60
|
+
picture?: string;
|
|
61
|
+
}): Promise<{
|
|
62
|
+
id: string;
|
|
63
|
+
role: string;
|
|
64
|
+
name: string;
|
|
65
|
+
}>;
|
|
66
|
+
protected savePicture(picture?: string): Promise<string | undefined>;
|
|
67
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import bcryptjs from "bcryptjs";
|
|
2
|
+
import { v4 } from "uuid";
|
|
3
|
+
import { createCookie } from "react-router";
|
|
4
|
+
export const ACCESS_TOKEN_KEY = "access_token";
|
|
5
|
+
export const REFRESH_TOKEN_KEY = "refresh_token";
|
|
6
|
+
export const ACCESS_TOKEN_COOKIE = createCookie(ACCESS_TOKEN_KEY, {
|
|
7
|
+
path: "/",
|
|
8
|
+
httpOnly: process.env.NODE_ENV === "production",
|
|
9
|
+
secure: process.env.NODE_ENV === "production",
|
|
10
|
+
sameSite: "strict",
|
|
11
|
+
});
|
|
12
|
+
export const REFRESH_TOKEN_COOKIE = createCookie(REFRESH_TOKEN_KEY, {
|
|
13
|
+
path: "/",
|
|
14
|
+
httpOnly: process.env.NODE_ENV === "production",
|
|
15
|
+
secure: process.env.NODE_ENV === "production",
|
|
16
|
+
sameSite: "strict",
|
|
17
|
+
});
|
|
18
|
+
export class AuthService {
|
|
19
|
+
constructor({ repository, JWT_MANAGER, OBJECT_STORAGE, }) {
|
|
20
|
+
this.repository = repository;
|
|
21
|
+
this.JWT_MANAGER = JWT_MANAGER;
|
|
22
|
+
this.OBJECT_STORAGE = OBJECT_STORAGE;
|
|
23
|
+
}
|
|
24
|
+
async verify(request) {
|
|
25
|
+
var _a;
|
|
26
|
+
const accessToken = ((_a = request === null || request === void 0 ? void 0 : request.headers.get("Authorization")) === null || _a === void 0 ? void 0 : _a.replace("Bearer ", "")) ||
|
|
27
|
+
(await this.getAccessTokenFromCookies(request));
|
|
28
|
+
if (accessToken) {
|
|
29
|
+
return this.JWT_MANAGER.verifyAccessToken(accessToken);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async verifyOrRefresh(request) {
|
|
33
|
+
const payload = await this.verify(request);
|
|
34
|
+
if (payload) {
|
|
35
|
+
return payload;
|
|
36
|
+
}
|
|
37
|
+
return this.refresh(request);
|
|
38
|
+
}
|
|
39
|
+
async getAccessTokenFromCookies(request) {
|
|
40
|
+
var _a;
|
|
41
|
+
const cookieStore = await ACCESS_TOKEN_COOKIE.parse(request.headers.get("cookie"));
|
|
42
|
+
return (_a = cookieStore.get(ACCESS_TOKEN_KEY)) === null || _a === void 0 ? void 0 : _a.value;
|
|
43
|
+
}
|
|
44
|
+
async getRefreshTokenFromCookies(request) {
|
|
45
|
+
var _a;
|
|
46
|
+
const cookieStore = await REFRESH_TOKEN_COOKIE.parse(request.headers.get("cookie"));
|
|
47
|
+
return (_a = cookieStore.get(REFRESH_TOKEN_KEY)) === null || _a === void 0 ? void 0 : _a.value;
|
|
48
|
+
}
|
|
49
|
+
async refresh(request) {
|
|
50
|
+
const refreshToken = await this.getRefreshTokenFromCookies(request);
|
|
51
|
+
if (refreshToken) {
|
|
52
|
+
try {
|
|
53
|
+
const refreshedAccessToken = await this.refreshAccessToken(refreshToken);
|
|
54
|
+
await this.getAccessTokenSetCookie(refreshedAccessToken);
|
|
55
|
+
const payload = this.JWT_MANAGER.decode(refreshedAccessToken);
|
|
56
|
+
if (payload) {
|
|
57
|
+
return payload;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
if (e instanceof Error) {
|
|
62
|
+
console.log(e.message);
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async signIn({ id, password }) {
|
|
69
|
+
const credential = await this.repository.findCredentialById(id);
|
|
70
|
+
if (!credential) {
|
|
71
|
+
throw Error("아이디 또는 비밀번호가 틀렸습니다.");
|
|
72
|
+
}
|
|
73
|
+
if (!(await bcryptjs.compare(password, credential.password))) {
|
|
74
|
+
throw Error("아이디 또는 비밀번호가 틀렸습니다.");
|
|
75
|
+
}
|
|
76
|
+
const user = await this.repository.findUserById(credential.userId);
|
|
77
|
+
if (!user) {
|
|
78
|
+
throw Error("회원 정보를 찾을 수 없습니다.");
|
|
79
|
+
}
|
|
80
|
+
const { accessToken, refreshToken } = await this.issueTokenPair(user);
|
|
81
|
+
return {
|
|
82
|
+
accessToken,
|
|
83
|
+
refreshToken,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
async issueAccessToken(user) {
|
|
87
|
+
return this.JWT_MANAGER.signAccessToken({
|
|
88
|
+
userId: user.id,
|
|
89
|
+
role: user.role,
|
|
90
|
+
name: user.name,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async issueTokenPair(user) {
|
|
94
|
+
const refreshToken = await this.JWT_MANAGER.signRefreshToken({
|
|
95
|
+
userId: user.id,
|
|
96
|
+
role: user.role,
|
|
97
|
+
name: user.name,
|
|
98
|
+
});
|
|
99
|
+
await this.repository.updateUserRefreshToken(user.id, await bcryptjs.hash(refreshToken, 10));
|
|
100
|
+
const accessToken = await this.issueAccessToken(user);
|
|
101
|
+
return { refreshToken, accessToken };
|
|
102
|
+
}
|
|
103
|
+
async refreshAccessToken(refreshToken) {
|
|
104
|
+
const payload = await this.JWT_MANAGER.verifyRefreshToken(refreshToken);
|
|
105
|
+
if (!payload) {
|
|
106
|
+
throw new Error("토큰이 유효하지 않습니다.");
|
|
107
|
+
}
|
|
108
|
+
const { userId } = payload;
|
|
109
|
+
if (typeof userId !== "string") {
|
|
110
|
+
throw new Error("토큰이 유효하지 않습니다.");
|
|
111
|
+
}
|
|
112
|
+
const user = await this.repository.findUserById(userId);
|
|
113
|
+
if (!user) {
|
|
114
|
+
throw Error("이용자를 찾지 못했습니다.");
|
|
115
|
+
}
|
|
116
|
+
if (!user.refreshToken) {
|
|
117
|
+
throw Error("인증 정보를 찾지 못했습니다.");
|
|
118
|
+
}
|
|
119
|
+
if (await bcryptjs.compare(user.refreshToken, refreshToken)) {
|
|
120
|
+
throw Error("토큰이 유효하지 않습니다.");
|
|
121
|
+
}
|
|
122
|
+
const accessToken = await this.issueAccessToken(user);
|
|
123
|
+
return accessToken;
|
|
124
|
+
}
|
|
125
|
+
async getAccessTokenSetCookie(accessToken) {
|
|
126
|
+
return ACCESS_TOKEN_COOKIE.serialize(accessToken, {
|
|
127
|
+
expires: accessToken
|
|
128
|
+
? this.JWT_MANAGER.getExpirationTime(accessToken)
|
|
129
|
+
: new Date(0),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
async getRefreshTokenSetCookie(refreshToken) {
|
|
133
|
+
return REFRESH_TOKEN_COOKIE.serialize(refreshToken, {
|
|
134
|
+
expires: refreshToken
|
|
135
|
+
? this.JWT_MANAGER.getExpirationTime(refreshToken)
|
|
136
|
+
: new Date(0),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
async findUser(provider, info) {
|
|
140
|
+
const thirdPartyAuth = await this.repository.findThirdPartyAuth(provider, info.id);
|
|
141
|
+
if (thirdPartyAuth) {
|
|
142
|
+
const user = await this.repository.findUserById(thirdPartyAuth.userId);
|
|
143
|
+
if (user) {
|
|
144
|
+
return user;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async getOrCreateUser(provider, info) {
|
|
149
|
+
const thirdPartyAuth = await this.repository.findThirdPartyAuth(provider, info.id);
|
|
150
|
+
if (thirdPartyAuth) {
|
|
151
|
+
const user = await this.repository.findUserById(thirdPartyAuth.userId);
|
|
152
|
+
if (user) {
|
|
153
|
+
return user;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const userId = v4();
|
|
157
|
+
const email = info.email;
|
|
158
|
+
const name = info.name || "익명";
|
|
159
|
+
const picture = info.picture;
|
|
160
|
+
const profileImageId = (await this.savePicture(picture)) || null;
|
|
161
|
+
const result = await this.repository.createUser({
|
|
162
|
+
id: userId,
|
|
163
|
+
role: "user",
|
|
164
|
+
profileImageId,
|
|
165
|
+
name,
|
|
166
|
+
email,
|
|
167
|
+
});
|
|
168
|
+
await this.repository.createThirdPartyAuth({
|
|
169
|
+
id: info.id,
|
|
170
|
+
provider,
|
|
171
|
+
userId,
|
|
172
|
+
});
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
async savePicture(picture) {
|
|
176
|
+
if (!picture) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const res = await fetch(picture);
|
|
180
|
+
if (!res.ok) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const buffer = await res.arrayBuffer();
|
|
184
|
+
const id = v4();
|
|
185
|
+
const key = `/users/${id}/picture`;
|
|
186
|
+
await this.OBJECT_STORAGE.put(key, Buffer.from(buffer));
|
|
187
|
+
await this.repository.createFile({
|
|
188
|
+
id,
|
|
189
|
+
name: "picture",
|
|
190
|
+
type: "image/*",
|
|
191
|
+
key,
|
|
192
|
+
size: buffer.byteLength,
|
|
193
|
+
metadata: {},
|
|
194
|
+
});
|
|
195
|
+
return id;
|
|
196
|
+
}
|
|
197
|
+
}
|