sh-ui-cli 0.96.3 → 0.98.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 -1
- package/data/changelog/versions.json +26 -0
- package/package.json +1 -1
- package/src/api.d.ts +0 -4
- package/src/constants.js +0 -5
- package/src/create/cli-args.js +2 -6
- package/src/create/describeTemplate.js +3 -51
- package/src/create/generator.js +12 -816
- package/src/create/index.mjs +2 -7
- package/src/create/plugins/index.js +1 -3
- package/src/create/plugins/nextIntl.js +0 -4
- package/src/create/plugins/pluginSchema.js +1 -1
- package/src/create/templateManifest.js +0 -12
- package/src/mcp.mjs +1 -68
- package/templates/vite-standalone/_arch/flat/index.html +2 -2
- package/templates/vite-standalone/_arch/flat/src/lib/styles/globals.css +2 -2
- package/templates/vite-standalone/_arch/fsd/index.html +2 -2
- package/templates/vite-standalone/_arch/fsd/src/shared/styles/globals.css +2 -2
- package/src/create/plugins/authJwt.js +0 -420
- package/src/create/plugins/sentry.js +0 -467
- package/templates/tauri-shell/Cargo.toml +0 -21
- package/templates/tauri-shell/README.md +0 -49
- package/templates/tauri-shell/build.rs +0 -3
- package/templates/tauri-shell/capabilities/default.json +0 -12
- package/templates/tauri-shell/src/lib.rs +0 -8
- package/templates/tauri-shell/src/main.rs +0 -6
- package/templates/tauri-shell/tauri.conf.json +0 -29
|
@@ -1,420 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* auth-jwt 플러그인 — Layer 2 부터 arch-aware.
|
|
3
|
-
*
|
|
4
|
-
* fs 경로 / import alias 가 arch.paths.api / arch.aliases.api 에서 파생.
|
|
5
|
-
* FSD 기준 v0.57 까지의 하드코딩과 1:1 일치 (회귀 가드는 smoke).
|
|
6
|
-
*/
|
|
7
|
-
export const authJwtPlugin = {
|
|
8
|
-
name: 'auth-jwt',
|
|
9
|
-
label: '쿠키 기반 JWT 인증 (refresh 자리표시자 포함)',
|
|
10
|
-
description:
|
|
11
|
-
'쿠키 기반 JWT 인증. Next 16 proxy.ts 미들웨어, refresh-aware BFF, withAuthRetry 헬퍼. refresh 본문은 placeholder — 백엔드 명세 확정 후 한 파일 채우면 자동 활성화.',
|
|
12
|
-
priority: 2,
|
|
13
|
-
|
|
14
|
-
// 의존성 추가 없음 — 베이스의 fetch + cookies + react-query 만 사용
|
|
15
|
-
|
|
16
|
-
envVars: [
|
|
17
|
-
'# Auth (auth-jwt)',
|
|
18
|
-
'COOKIE_SECURE=false',
|
|
19
|
-
'# Dev 시 인증 가드 우회 — proxy.ts 가 이 flag 를 보면 /sign-in 으로 redirect 안 함.',
|
|
20
|
-
'# 실제 백엔드 연동 후엔 반드시 비워야 함 (또는 NODE_ENV 가 production 이면 무시).',
|
|
21
|
-
'NEXT_PUBLIC_DEV_AUTH_BYPASS=false',
|
|
22
|
-
],
|
|
23
|
-
|
|
24
|
-
turboEnvVars: [
|
|
25
|
-
'COOKIE_SECURE',
|
|
26
|
-
'NEXT_PUBLIC_DEV_AUTH_BYPASS',
|
|
27
|
-
],
|
|
28
|
-
|
|
29
|
-
providerImports: [],
|
|
30
|
-
providerWrappers: [],
|
|
31
|
-
|
|
32
|
-
// ─── 독립 파일 ───
|
|
33
|
-
//
|
|
34
|
-
// 베이스의 BFF (app/api/proxy/[...path]/route.ts) 를 refresh-aware 버전으로
|
|
35
|
-
// 덮어쓰고, 미들웨어와 인증 헬퍼를 추가한다.
|
|
36
|
-
// refreshSession.ts 는 v1 placeholder — 백엔드 명세 확정 후 본문만 채우면
|
|
37
|
-
// BFF 와 withAuthRetry 가 자동 활용한다.
|
|
38
|
-
|
|
39
|
-
files: (arch) => ({
|
|
40
|
-
// placeholder sign-in page — proxy.ts 가 미인증 요청을 /sign-in 으로 redirect
|
|
41
|
-
// 하므로, 페이지가 없으면 사용자가 dev 띄우자마자 무한 404 루프에 빠진다.
|
|
42
|
-
// 템플릿 단계에선 dev 우회용 "fake 토큰 set" 버튼 + 안내 문구로 최소화.
|
|
43
|
-
// next-intl 활성 시 nextIntl 플러그인의 transforms 가 [locale]/sign-in 으로 옮긴다.
|
|
44
|
-
'app/sign-in/page.tsx': `'use client';
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* auth-jwt 플러그인의 placeholder sign-in 페이지.
|
|
48
|
-
*
|
|
49
|
-
* 실제 프로덕션에선 백엔드 sign-in API 와 연동하는 폼으로 교체해야 한다.
|
|
50
|
-
* 지금은 dev 시 미인증 redirect 루프를 끊기 위한 최소 페이지.
|
|
51
|
-
*/
|
|
52
|
-
export default function SignInPage() {
|
|
53
|
-
const setDevToken = () => {
|
|
54
|
-
document.cookie = 'accessToken=dev-placeholder; path=/; max-age=86400';
|
|
55
|
-
document.cookie = 'refreshToken=dev-placeholder; path=/; max-age=86400';
|
|
56
|
-
window.location.href = '/';
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
// 토큰 기반 색상 — 다크/라이트 자동 적응. inline hex 사용 시 다크에서 시인성 깨짐.
|
|
60
|
-
return (
|
|
61
|
-
<main style={{ display: 'flex', minHeight: '100vh', alignItems: 'center', justifyContent: 'center', padding: 24 }}>
|
|
62
|
-
<div style={{ maxWidth: 420, width: '100%', display: 'flex', flexDirection: 'column', gap: 16 }}>
|
|
63
|
-
<h1 style={{ fontSize: 24, margin: 0, color: 'var(--foreground)' }}>Sign in</h1>
|
|
64
|
-
<p style={{ color: 'var(--foreground-muted)', fontSize: 14, lineHeight: 1.6, margin: 0 }}>
|
|
65
|
-
auth-jwt 플러그인 placeholder 페이지입니다. 실제 인증 연동 전까지는
|
|
66
|
-
아래 버튼으로 dev 용 가짜 토큰을 설정해 가드를 우회할 수 있습니다.
|
|
67
|
-
</p>
|
|
68
|
-
<button
|
|
69
|
-
type="button"
|
|
70
|
-
onClick={setDevToken}
|
|
71
|
-
style={{
|
|
72
|
-
padding: '10px 16px',
|
|
73
|
-
borderRadius: 8,
|
|
74
|
-
border: '1px solid var(--border-strong)',
|
|
75
|
-
background: 'var(--primary)',
|
|
76
|
-
color: 'var(--primary-foreground)',
|
|
77
|
-
cursor: 'pointer',
|
|
78
|
-
fontSize: 14,
|
|
79
|
-
fontWeight: 500,
|
|
80
|
-
}}
|
|
81
|
-
>
|
|
82
|
-
Continue (dev — set fake token)
|
|
83
|
-
</button>
|
|
84
|
-
</div>
|
|
85
|
-
</main>
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
`,
|
|
89
|
-
|
|
90
|
-
'proxy.ts': `import { NextRequest, NextResponse } from 'next/server';
|
|
91
|
-
|
|
92
|
-
const AUTH_ROUTES = ['/sign-in', '/sign-up'];
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* 홈(\`/\`) 진입 시 redirect 할 path. 빈 문자열이면 \`app/page.tsx\` 가 그대로 노출.
|
|
96
|
-
* 예: '/dashboard', '/projects'. 인증 가드 위에서 동작하므로 미인증이면
|
|
97
|
-
* 그대로 \`/sign-in\` 으로 빠진다.
|
|
98
|
-
*/
|
|
99
|
-
const HOME_REDIRECT = '';
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Next 16+ 의 proxy.ts (구 middleware.ts).
|
|
103
|
-
* 토큰 존재 여부만 검사한다 — 만료 검사나 refresh 는 하지 않는다.
|
|
104
|
-
*
|
|
105
|
-
* - \`/\` + HOME_REDIRECT 설정 → 해당 경로로 리다이렉트
|
|
106
|
-
* - AT 쿠키 없음 + 인증 라우트 아님 → /sign-in 으로 리다이렉트
|
|
107
|
-
* - AT 쿠키 있음 또는 인증 라우트 → 통과
|
|
108
|
-
* - dev + \`NEXT_PUBLIC_DEV_AUTH_BYPASS=true\` → 가드 전체 우회 (개발용)
|
|
109
|
-
*
|
|
110
|
-
* AT 가 만료된 채 통과한 요청은 BFF (/api/proxy) 가 401 을 받아
|
|
111
|
-
* refreshSession 으로 갱신을 시도한다.
|
|
112
|
-
*/
|
|
113
|
-
const DEV_BYPASS =
|
|
114
|
-
process.env.NODE_ENV !== 'production' &&
|
|
115
|
-
process.env.NEXT_PUBLIC_DEV_AUTH_BYPASS === 'true';
|
|
116
|
-
|
|
117
|
-
export default function proxy(req: NextRequest) {
|
|
118
|
-
const { pathname } = req.nextUrl;
|
|
119
|
-
const hasToken = !!req.cookies.get('accessToken')?.value;
|
|
120
|
-
const isAuthRoute = AUTH_ROUTES.some((r) => pathname.startsWith(r));
|
|
121
|
-
|
|
122
|
-
if (pathname === '/' && HOME_REDIRECT) {
|
|
123
|
-
return NextResponse.redirect(new URL(HOME_REDIRECT, req.url));
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (DEV_BYPASS) return NextResponse.next();
|
|
127
|
-
if (isAuthRoute) return NextResponse.next();
|
|
128
|
-
if (!hasToken) return NextResponse.redirect(new URL('/sign-in', req.url));
|
|
129
|
-
|
|
130
|
-
return NextResponse.next();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export const config = {
|
|
134
|
-
matcher: '/((?!api|_next|.*\\\\..*).*)',
|
|
135
|
-
};
|
|
136
|
-
`,
|
|
137
|
-
|
|
138
|
-
[`${arch.paths.api}/refreshSession.ts`]: `type RefreshResult =
|
|
139
|
-
| { ok: true; accessToken: string; refreshToken: string }
|
|
140
|
-
| { ok: false };
|
|
141
|
-
|
|
142
|
-
let inflight: Promise<RefreshResult> | null = null;
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* refreshToken 으로 새 accessToken / refreshToken 을 발급받는다.
|
|
146
|
-
*
|
|
147
|
-
* v1 placeholder — 백엔드 refresh API 명세가 확정되면 아래 TODO 부분을 채운다.
|
|
148
|
-
* 본문이 채워지면 BFF (/api/proxy) 와 withAuthRetry 가 자동으로 활용한다.
|
|
149
|
-
*
|
|
150
|
-
* 동시에 여러 요청이 401 을 만나도 inflight 모듈 변수로 코얼레싱돼서
|
|
151
|
-
* refresh 는 한 번만 발사된다.
|
|
152
|
-
*
|
|
153
|
-
* 참고 구현 예시:
|
|
154
|
-
*
|
|
155
|
-
* const res = await fetch(\`\${process.env.API_URL}/v1/auth/token/refresh\`, {
|
|
156
|
-
* method: 'POST',
|
|
157
|
-
* headers: { 'Content-Type': 'application/json' },
|
|
158
|
-
* body: JSON.stringify({ refreshToken }),
|
|
159
|
-
* });
|
|
160
|
-
* const body = await res.json();
|
|
161
|
-
* if (body.result === 'SUCCESS') {
|
|
162
|
-
* return {
|
|
163
|
-
* ok: true,
|
|
164
|
-
* accessToken: body.data.accessToken,
|
|
165
|
-
* refreshToken: body.data.refreshToken,
|
|
166
|
-
* };
|
|
167
|
-
* }
|
|
168
|
-
* return { ok: false };
|
|
169
|
-
*/
|
|
170
|
-
export async function refreshSession(
|
|
171
|
-
refreshToken: string,
|
|
172
|
-
): Promise<RefreshResult> {
|
|
173
|
-
if (inflight) return inflight;
|
|
174
|
-
|
|
175
|
-
inflight = (async (): Promise<RefreshResult> => {
|
|
176
|
-
try {
|
|
177
|
-
// TODO: 백엔드 refresh API 명세 확정 후 여기에 fetch 호출을 작성.
|
|
178
|
-
// 지금은 placeholder 라 항상 실패 → BFF 가 쿠키 삭제 + 401 응답.
|
|
179
|
-
void refreshToken;
|
|
180
|
-
return { ok: false };
|
|
181
|
-
} finally {
|
|
182
|
-
inflight = null;
|
|
183
|
-
}
|
|
184
|
-
})();
|
|
185
|
-
|
|
186
|
-
return inflight;
|
|
187
|
-
}
|
|
188
|
-
`,
|
|
189
|
-
|
|
190
|
-
[`${arch.paths.api}/withAuthRetry.ts`]: `import { cookies } from 'next/headers';
|
|
191
|
-
|
|
192
|
-
import { ApiError } from './error';
|
|
193
|
-
import { refreshSession } from './refreshSession';
|
|
194
|
-
|
|
195
|
-
const ACCESS_TOKEN_COOKIE = 'accessToken';
|
|
196
|
-
const REFRESH_TOKEN_COOKIE = 'refreshToken';
|
|
197
|
-
|
|
198
|
-
const COOKIE = {
|
|
199
|
-
httpOnly: true,
|
|
200
|
-
secure: process.env.COOKIE_SECURE === 'true',
|
|
201
|
-
sameSite: 'lax' as const,
|
|
202
|
-
path: '/',
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Route Handler 또는 Server Action 안에서 인증된 요청을 보낼 때 사용한다.
|
|
207
|
-
* 401 을 만나면 refreshSession 으로 토큰을 갱신하고 fn 을 한 번 더 실행한다.
|
|
208
|
-
*
|
|
209
|
-
* RSC (Server Component) 에서는 cookies().set() 이 막혀 있으므로 여기서 호출하면 안 된다.
|
|
210
|
-
* RSC 는 serverFetch 를 직접 호출하고, 401 은 prefetchQuery 가 swallow 한 뒤
|
|
211
|
-
* 클라이언트 refetch 가 BFF 경유로 자동 복구한다.
|
|
212
|
-
*
|
|
213
|
-
* 사용 예 (Server Action):
|
|
214
|
-
*
|
|
215
|
-
* 'use server';
|
|
216
|
-
* import { serverFetch } from '${arch.aliases.api}/serverFetch';
|
|
217
|
-
* import { withAuthRetry } from '${arch.aliases.api}/withAuthRetry';
|
|
218
|
-
*
|
|
219
|
-
* export async function toggleFavoriteAction(id: number) {
|
|
220
|
-
* return withAuthRetry(() =>
|
|
221
|
-
* serverFetch(\`/v1/products/\${id}/favorite\`, { method: 'POST' }),
|
|
222
|
-
* );
|
|
223
|
-
* }
|
|
224
|
-
*/
|
|
225
|
-
export async function withAuthRetry<T>(fn: () => Promise<T>): Promise<T> {
|
|
226
|
-
try {
|
|
227
|
-
return await fn();
|
|
228
|
-
} catch (e) {
|
|
229
|
-
if (!(e instanceof ApiError) || e.status !== 401) throw e;
|
|
230
|
-
|
|
231
|
-
const jar = await cookies();
|
|
232
|
-
const refreshToken = jar.get(REFRESH_TOKEN_COOKIE)?.value;
|
|
233
|
-
if (!refreshToken) throw e;
|
|
234
|
-
|
|
235
|
-
const r = await refreshSession(refreshToken);
|
|
236
|
-
if (!r.ok) throw new ApiError(401, 'UNAUTHORIZED', null);
|
|
237
|
-
|
|
238
|
-
jar.set(ACCESS_TOKEN_COOKIE, r.accessToken, COOKIE);
|
|
239
|
-
jar.set(REFRESH_TOKEN_COOKIE, r.refreshToken, COOKIE);
|
|
240
|
-
|
|
241
|
-
return await fn();
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
`,
|
|
245
|
-
|
|
246
|
-
'app/api/proxy/[...path]/route.ts': `import { cookies } from 'next/headers';
|
|
247
|
-
import { NextResponse, type NextRequest } from 'next/server';
|
|
248
|
-
|
|
249
|
-
import {
|
|
250
|
-
captureApiError,
|
|
251
|
-
logApiError,
|
|
252
|
-
} from '${arch.aliases.api}/observability';
|
|
253
|
-
import { refreshSession } from '${arch.aliases.api}/refreshSession';
|
|
254
|
-
|
|
255
|
-
const API_URL = process.env.API_URL ?? 'http://localhost:8080/api';
|
|
256
|
-
const ACCESS_TOKEN_COOKIE = 'accessToken';
|
|
257
|
-
const REFRESH_TOKEN_COOKIE = 'refreshToken';
|
|
258
|
-
const LOCALE_COOKIE = 'NEXT_LOCALE';
|
|
259
|
-
|
|
260
|
-
const COOKIE = {
|
|
261
|
-
httpOnly: true,
|
|
262
|
-
secure: process.env.COOKIE_SECURE === 'true',
|
|
263
|
-
sameSite: 'lax' as const,
|
|
264
|
-
path: '/',
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
const clearAuthCookies = (res: NextResponse) => {
|
|
268
|
-
res.cookies.set(ACCESS_TOKEN_COOKIE, '', { ...COOKIE, maxAge: 0 });
|
|
269
|
-
res.cookies.set(REFRESH_TOKEN_COOKIE, '', { ...COOKIE, maxAge: 0 });
|
|
270
|
-
return res;
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
const proxyRequest = async (
|
|
274
|
-
request: NextRequest,
|
|
275
|
-
ctx: { params: Promise<{ path: string[] }> },
|
|
276
|
-
method: string,
|
|
277
|
-
) => {
|
|
278
|
-
const { path } = await ctx.params;
|
|
279
|
-
const apiPath = path.join('/');
|
|
280
|
-
const url = new URL(\`\${API_URL}/\${apiPath}\`);
|
|
281
|
-
|
|
282
|
-
request.nextUrl.searchParams.forEach((value, key) => {
|
|
283
|
-
url.searchParams.set(key, value);
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
const cookieStore = await cookies();
|
|
287
|
-
const accessToken = cookieStore.get(ACCESS_TOKEN_COOKIE)?.value;
|
|
288
|
-
const refreshToken = cookieStore.get(REFRESH_TOKEN_COOKIE)?.value;
|
|
289
|
-
const locale =
|
|
290
|
-
cookieStore.get(LOCALE_COOKIE)?.value ??
|
|
291
|
-
request.headers.get('Accept-Language') ??
|
|
292
|
-
undefined;
|
|
293
|
-
|
|
294
|
-
const headers: Record<string, string> = {};
|
|
295
|
-
if (accessToken) headers.Authorization = \`Bearer \${accessToken}\`;
|
|
296
|
-
if (locale) headers['Accept-Language'] = locale;
|
|
297
|
-
|
|
298
|
-
let body: BodyInit | undefined;
|
|
299
|
-
if (method !== 'GET' && method !== 'HEAD') {
|
|
300
|
-
const contentType = request.headers.get('Content-Type');
|
|
301
|
-
if (contentType?.includes('multipart/form-data')) {
|
|
302
|
-
body = await request.formData();
|
|
303
|
-
} else {
|
|
304
|
-
headers['Content-Type'] = 'application/json';
|
|
305
|
-
body = await request.text();
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
let response: Response;
|
|
310
|
-
try {
|
|
311
|
-
response = await fetch(url.toString(), { method, headers, body });
|
|
312
|
-
} catch (error) {
|
|
313
|
-
console.error(\`[PROXY] \${method} \${url.toString()} —\`, error);
|
|
314
|
-
return NextResponse.json(
|
|
315
|
-
{
|
|
316
|
-
result: 'ERROR',
|
|
317
|
-
data: null,
|
|
318
|
-
error: {
|
|
319
|
-
code: 'NETWORK_ERROR',
|
|
320
|
-
message: 'Failed to reach upstream server.',
|
|
321
|
-
},
|
|
322
|
-
},
|
|
323
|
-
{ status: 502 },
|
|
324
|
-
);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
let data = await response.json();
|
|
328
|
-
|
|
329
|
-
// 401 → refresh 시도 (RT 가 있을 때만)
|
|
330
|
-
if (response.status === 401) {
|
|
331
|
-
if (!refreshToken) {
|
|
332
|
-
return clearAuthCookies(NextResponse.json(data, { status: 401 }));
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
const r = await refreshSession(refreshToken);
|
|
336
|
-
|
|
337
|
-
// refresh placeholder 가 본문 미구현이면 항상 ok:false
|
|
338
|
-
// → 쿠키 삭제 + 401 그대로 (clientFetch 가 /sign-in 으로 이동)
|
|
339
|
-
if (!r.ok) {
|
|
340
|
-
return clearAuthCookies(NextResponse.json(data, { status: 401 }));
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// 새 AT 로 재시도
|
|
344
|
-
headers.Authorization = \`Bearer \${r.accessToken}\`;
|
|
345
|
-
const retry = await fetch(url.toString(), { method, headers, body });
|
|
346
|
-
data = await retry.json();
|
|
347
|
-
|
|
348
|
-
const res = NextResponse.json(data, { status: retry.status });
|
|
349
|
-
res.cookies.set(ACCESS_TOKEN_COOKIE, r.accessToken, COOKIE);
|
|
350
|
-
res.cookies.set(REFRESH_TOKEN_COOKIE, r.refreshToken, COOKIE);
|
|
351
|
-
|
|
352
|
-
if (!retry.ok) {
|
|
353
|
-
logApiError('PROXY', {
|
|
354
|
-
url: url.toString(),
|
|
355
|
-
method,
|
|
356
|
-
status: retry.status,
|
|
357
|
-
requestBody: typeof body === 'string' ? body : undefined,
|
|
358
|
-
responseBody: data,
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
captureApiError({
|
|
362
|
-
url: url.toString(),
|
|
363
|
-
apiPath,
|
|
364
|
-
method,
|
|
365
|
-
status: retry.status,
|
|
366
|
-
responseBody: data,
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
return res;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if (!response.ok) {
|
|
374
|
-
logApiError('PROXY', {
|
|
375
|
-
url: url.toString(),
|
|
376
|
-
method,
|
|
377
|
-
status: response.status,
|
|
378
|
-
requestBody: typeof body === 'string' ? body : undefined,
|
|
379
|
-
responseBody: data,
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
captureApiError({
|
|
383
|
-
url: url.toString(),
|
|
384
|
-
apiPath,
|
|
385
|
-
method,
|
|
386
|
-
status: response.status,
|
|
387
|
-
responseBody: data,
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
return NextResponse.json(data, { status: response.status });
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
export const GET = (
|
|
395
|
-
req: NextRequest,
|
|
396
|
-
ctx: { params: Promise<{ path: string[] }> },
|
|
397
|
-
) => proxyRequest(req, ctx, 'GET');
|
|
398
|
-
|
|
399
|
-
export const POST = (
|
|
400
|
-
req: NextRequest,
|
|
401
|
-
ctx: { params: Promise<{ path: string[] }> },
|
|
402
|
-
) => proxyRequest(req, ctx, 'POST');
|
|
403
|
-
|
|
404
|
-
export const PUT = (
|
|
405
|
-
req: NextRequest,
|
|
406
|
-
ctx: { params: Promise<{ path: string[] }> },
|
|
407
|
-
) => proxyRequest(req, ctx, 'PUT');
|
|
408
|
-
|
|
409
|
-
export const PATCH = (
|
|
410
|
-
req: NextRequest,
|
|
411
|
-
ctx: { params: Promise<{ path: string[] }> },
|
|
412
|
-
) => proxyRequest(req, ctx, 'PATCH');
|
|
413
|
-
|
|
414
|
-
export const DELETE = (
|
|
415
|
-
req: NextRequest,
|
|
416
|
-
ctx: { params: Promise<{ path: string[] }> },
|
|
417
|
-
) => proxyRequest(req, ctx, 'DELETE');
|
|
418
|
-
`,
|
|
419
|
-
}),
|
|
420
|
-
};
|