@raxonltd/raxon-core 1.1.8 → 1.1.13
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/core/feature/address/api/places.api.ts +20 -4
- package/core/server/places.proxy.ts +35 -0
- package/core/server/raxon.bootstrap.route.ts +39 -0
- package/core/server/raxon.server.ts +80 -0
- package/dist/core/feature/address/api/places.api.d.ts.map +1 -1
- package/dist/core/feature/address/api/places.api.js +18 -4
- package/dist/core/server/places.proxy.d.ts +10 -0
- package/dist/core/server/places.proxy.d.ts.map +1 -0
- package/dist/core/server/places.proxy.js +24 -0
- package/dist/core/server/raxon.bootstrap.route.d.ts +7 -0
- package/dist/core/server/raxon.bootstrap.route.d.ts.map +1 -0
- package/dist/core/server/raxon.bootstrap.route.js +27 -0
- package/dist/core/server/raxon.server.d.ts +24 -0
- package/dist/core/server/raxon.server.d.ts.map +1 -0
- package/dist/core/server/raxon.server.js +59 -0
- package/dist/middleware.d.ts +6 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +5 -0
- package/dist/server-bootstrap.d.ts +2 -0
- package/dist/server-bootstrap.d.ts.map +1 -0
- package/dist/server-bootstrap.js +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +11 -1
|
@@ -24,11 +24,19 @@ export async function fetchPlaceAutocomplete(
|
|
|
24
24
|
throw new Error('Google Maps API key not configured');
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
const
|
|
27
|
+
const isBrowser = typeof window !== 'undefined';
|
|
28
|
+
let url: URL;
|
|
29
|
+
|
|
30
|
+
if (isBrowser) {
|
|
31
|
+
url = new URL('/api/places/autocomplete', window.location.origin);
|
|
32
|
+
} else {
|
|
33
|
+
url = new URL('https://maps.googleapis.com/maps/api/place/autocomplete/json');
|
|
34
|
+
url.searchParams.set('key', apiKey);
|
|
35
|
+
}
|
|
36
|
+
|
|
28
37
|
url.searchParams.set('input', input);
|
|
29
38
|
url.searchParams.set('components', components);
|
|
30
39
|
url.searchParams.set('language', language);
|
|
31
|
-
url.searchParams.set('key', apiKey);
|
|
32
40
|
|
|
33
41
|
const response = await fetch(url.toString(), { signal });
|
|
34
42
|
|
|
@@ -54,11 +62,19 @@ export async function fetchPlaceDetails(
|
|
|
54
62
|
throw new Error('Google Maps API key not configured');
|
|
55
63
|
}
|
|
56
64
|
|
|
57
|
-
const
|
|
65
|
+
const isBrowser = typeof window !== 'undefined';
|
|
66
|
+
let url: URL;
|
|
67
|
+
|
|
68
|
+
if (isBrowser) {
|
|
69
|
+
url = new URL('/api/places/details', window.location.origin);
|
|
70
|
+
} else {
|
|
71
|
+
url = new URL('https://maps.googleapis.com/maps/api/place/details/json');
|
|
72
|
+
url.searchParams.set('key', apiKey);
|
|
73
|
+
}
|
|
74
|
+
|
|
58
75
|
url.searchParams.set('place_id', placeId);
|
|
59
76
|
url.searchParams.set('fields', 'geometry,address_components,formatted_address');
|
|
60
77
|
url.searchParams.set('language', language);
|
|
61
|
-
url.searchParams.set('key', apiKey);
|
|
62
78
|
|
|
63
79
|
const response = await fetch(url.toString(), { signal });
|
|
64
80
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import type { NextRequest } from 'next/server';
|
|
3
|
+
|
|
4
|
+
const GOOGLE_PLACES_URL = {
|
|
5
|
+
autocomplete: 'https://maps.googleapis.com/maps/api/place/autocomplete/json',
|
|
6
|
+
details: 'https://maps.googleapis.com/maps/api/place/details/json',
|
|
7
|
+
} as const;
|
|
8
|
+
|
|
9
|
+
export type GooglePlacesEndpoint = keyof typeof GOOGLE_PLACES_URL;
|
|
10
|
+
|
|
11
|
+
export async function proxyGooglePlaces(
|
|
12
|
+
request: NextRequest,
|
|
13
|
+
endpoint: GooglePlacesEndpoint,
|
|
14
|
+
googleMapsApiKey?: string,
|
|
15
|
+
) {
|
|
16
|
+
const googleUrl = new URL(GOOGLE_PLACES_URL[endpoint]);
|
|
17
|
+
|
|
18
|
+
request.nextUrl.searchParams.forEach((value, key) => {
|
|
19
|
+
googleUrl.searchParams.set(key, value);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const apiKey = googleMapsApiKey ?? process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY ?? '';
|
|
23
|
+
if (!googleUrl.searchParams.has('key') && apiKey) {
|
|
24
|
+
googleUrl.searchParams.set('key', apiKey);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const response = await fetch(googleUrl.toString());
|
|
29
|
+
const data = await response.json();
|
|
30
|
+
return NextResponse.json(data);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error('[raxonServer/places]', error);
|
|
33
|
+
return NextResponse.json({ error: 'Google Places API isteği başarısız' }, { status: 500 });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { unstable_cache } from 'next/cache';
|
|
3
|
+
import { fetchRaxonBootstrap } from '@/core/util/fetch.bootstrap';
|
|
4
|
+
import {
|
|
5
|
+
BOOTSTRAP_REVALIDATE_SECONDS,
|
|
6
|
+
bootstrapCacheHeaders,
|
|
7
|
+
} from '@/core/server/raxon.server';
|
|
8
|
+
|
|
9
|
+
async function fetchBootstrapData() {
|
|
10
|
+
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
|
|
11
|
+
const apiKey = process.env.NEXT_PUBLIC_API_KEY;
|
|
12
|
+
|
|
13
|
+
if (!apiUrl || !apiKey) {
|
|
14
|
+
throw new Error('[raxonServer] NEXT_PUBLIC_API_URL veya NEXT_PUBLIC_API_KEY tanımlı değil');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return fetchRaxonBootstrap(apiUrl, apiKey);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const getCachedBootstrap = unstable_cache(
|
|
21
|
+
fetchBootstrapData,
|
|
22
|
+
['raxon-bootstrap'],
|
|
23
|
+
{ revalidate: BOOTSTRAP_REVALIDATE_SECONDS },
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
/** API route kullanmak isteyenler için: export { GET, revalidate } from '@raxonltd/raxon-core/server/bootstrap' */
|
|
27
|
+
export const revalidate = BOOTSTRAP_REVALIDATE_SECONDS;
|
|
28
|
+
|
|
29
|
+
export async function GET() {
|
|
30
|
+
try {
|
|
31
|
+
const data = await getCachedBootstrap();
|
|
32
|
+
return NextResponse.json(data, {
|
|
33
|
+
headers: bootstrapCacheHeaders(revalidate),
|
|
34
|
+
});
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error('[raxonServer]', error);
|
|
37
|
+
return NextResponse.json({ error: 'Bootstrap verisi alınamadı' }, { status: 502 });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import type { NextRequest } from 'next/server';
|
|
3
|
+
import { fetchRaxonBootstrap } from '@/core/util/fetch.bootstrap';
|
|
4
|
+
import { proxyGooglePlaces } from '@/core/server/places.proxy';
|
|
5
|
+
|
|
6
|
+
const DEFAULT_REVALIDATE_SECONDS = 300;
|
|
7
|
+
|
|
8
|
+
const RAXON_SERVER_PATHS = {
|
|
9
|
+
bootstrap: '/api/bootstrap',
|
|
10
|
+
placesAutocomplete: '/api/places/autocomplete',
|
|
11
|
+
placesDetails: '/api/places/details',
|
|
12
|
+
} as const;
|
|
13
|
+
|
|
14
|
+
export interface RaxonServerOptions {
|
|
15
|
+
apiUrl?: string;
|
|
16
|
+
apiKey?: string;
|
|
17
|
+
googleMapsApiKey?: string;
|
|
18
|
+
revalidate?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function resolveCredentials(options: RaxonServerOptions = {}) {
|
|
22
|
+
const apiUrl = options.apiUrl ?? process.env.NEXT_PUBLIC_API_URL;
|
|
23
|
+
const apiKey = options.apiKey ?? process.env.NEXT_PUBLIC_API_KEY;
|
|
24
|
+
return { apiUrl, apiKey };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function fetchBootstrapData(options: RaxonServerOptions = {}) {
|
|
28
|
+
const { apiUrl, apiKey } = resolveCredentials(options);
|
|
29
|
+
|
|
30
|
+
if (!apiUrl || !apiKey) {
|
|
31
|
+
throw new Error('[raxonServer] NEXT_PUBLIC_API_URL veya NEXT_PUBLIC_API_KEY tanımlı değil');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return fetchRaxonBootstrap(apiUrl, apiKey);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function bootstrapCacheHeaders(revalidate = DEFAULT_REVALIDATE_SECONDS) {
|
|
38
|
+
return {
|
|
39
|
+
'Cache-Control': `public, s-maxage=${revalidate}, stale-while-revalidate=${revalidate * 2}`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Next.js middleware — bootstrap ve Google Places BFF isteklerini karşılar.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // proxy.ts — config Next.js tarafından statik parse edilmeli, import edilemez
|
|
48
|
+
* import { raxonServer } from '@raxonltd/raxon-core/server';
|
|
49
|
+
* export default raxonServer();
|
|
50
|
+
* export const config = { matcher: ['/api/bootstrap', '/api/places/:path'] };
|
|
51
|
+
*/
|
|
52
|
+
export function raxonServer(options: RaxonServerOptions = {}) {
|
|
53
|
+
const revalidate = options.revalidate ?? DEFAULT_REVALIDATE_SECONDS;
|
|
54
|
+
|
|
55
|
+
return async function raxonMiddleware(request: NextRequest) {
|
|
56
|
+
const { pathname } = request.nextUrl;
|
|
57
|
+
|
|
58
|
+
if (pathname === RAXON_SERVER_PATHS.bootstrap) {
|
|
59
|
+
try {
|
|
60
|
+
const data = await fetchBootstrapData(options);
|
|
61
|
+
return NextResponse.json(data, { headers: bootstrapCacheHeaders(revalidate) });
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error('[raxonServer/bootstrap]', error);
|
|
64
|
+
return NextResponse.json({ error: 'Bootstrap verisi alınamadı' }, { status: 502 });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (pathname === RAXON_SERVER_PATHS.placesAutocomplete) {
|
|
69
|
+
return proxyGooglePlaces(request, 'autocomplete', options.googleMapsApiKey);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (pathname === RAXON_SERVER_PATHS.placesDetails) {
|
|
73
|
+
return proxyGooglePlaces(request, 'details', options.googleMapsApiKey);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return NextResponse.next();
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { DEFAULT_REVALIDATE_SECONDS as BOOTSTRAP_REVALIDATE_SECONDS, bootstrapCacheHeaders };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"places.api.d.ts","sourceRoot":"","sources":["../../../../../core/feature/address/api/places.api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,2CAA2C,CAAC;AAMtG,UAAU,yBAAyB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,MAAM,EACb,EAAE,UAAyB,EAAE,QAAe,EAAE,MAAM,EAAE,GAAE,yBAA8B,GACrF,OAAO,CAAC,qBAAqB,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"places.api.d.ts","sourceRoot":"","sources":["../../../../../core/feature/address/api/places.api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,2CAA2C,CAAC;AAMtG,UAAU,yBAAyB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,MAAM,EACb,EAAE,UAAyB,EAAE,QAAe,EAAE,MAAM,EAAE,GAAE,yBAA8B,GACrF,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAiClC;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,EAAE,QAAe,EAAE,MAAM,EAAE,GAAE,oBAAyB,GACrD,OAAO,CAAC,kBAAkB,CAAC,CAiC7B"}
|
|
@@ -6,11 +6,18 @@ export async function fetchPlaceAutocomplete(input, { components = 'country:tr',
|
|
|
6
6
|
if (!apiKey) {
|
|
7
7
|
throw new Error('Google Maps API key not configured');
|
|
8
8
|
}
|
|
9
|
-
const
|
|
9
|
+
const isBrowser = typeof window !== 'undefined';
|
|
10
|
+
let url;
|
|
11
|
+
if (isBrowser) {
|
|
12
|
+
url = new URL('/api/places/autocomplete', window.location.origin);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
url = new URL('https://maps.googleapis.com/maps/api/place/autocomplete/json');
|
|
16
|
+
url.searchParams.set('key', apiKey);
|
|
17
|
+
}
|
|
10
18
|
url.searchParams.set('input', input);
|
|
11
19
|
url.searchParams.set('components', components);
|
|
12
20
|
url.searchParams.set('language', language);
|
|
13
|
-
url.searchParams.set('key', apiKey);
|
|
14
21
|
const response = await fetch(url.toString(), { signal });
|
|
15
22
|
if (!response.ok) {
|
|
16
23
|
throw new Error(`Google Places API error: ${response.status}`);
|
|
@@ -26,11 +33,18 @@ export async function fetchPlaceDetails(placeId, { language = 'tr', signal } = {
|
|
|
26
33
|
if (!apiKey) {
|
|
27
34
|
throw new Error('Google Maps API key not configured');
|
|
28
35
|
}
|
|
29
|
-
const
|
|
36
|
+
const isBrowser = typeof window !== 'undefined';
|
|
37
|
+
let url;
|
|
38
|
+
if (isBrowser) {
|
|
39
|
+
url = new URL('/api/places/details', window.location.origin);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
url = new URL('https://maps.googleapis.com/maps/api/place/details/json');
|
|
43
|
+
url.searchParams.set('key', apiKey);
|
|
44
|
+
}
|
|
30
45
|
url.searchParams.set('place_id', placeId);
|
|
31
46
|
url.searchParams.set('fields', 'geometry,address_components,formatted_address');
|
|
32
47
|
url.searchParams.set('language', language);
|
|
33
|
-
url.searchParams.set('key', apiKey);
|
|
34
48
|
const response = await fetch(url.toString(), { signal });
|
|
35
49
|
if (!response.ok) {
|
|
36
50
|
throw new Error(`Google Places API error: ${response.status}`);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import type { NextRequest } from 'next/server';
|
|
3
|
+
declare const GOOGLE_PLACES_URL: {
|
|
4
|
+
readonly autocomplete: "https://maps.googleapis.com/maps/api/place/autocomplete/json";
|
|
5
|
+
readonly details: "https://maps.googleapis.com/maps/api/place/details/json";
|
|
6
|
+
};
|
|
7
|
+
export type GooglePlacesEndpoint = keyof typeof GOOGLE_PLACES_URL;
|
|
8
|
+
export declare function proxyGooglePlaces(request: NextRequest, endpoint: GooglePlacesEndpoint, googleMapsApiKey?: string): Promise<NextResponse<any>>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=places.proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"places.proxy.d.ts","sourceRoot":"","sources":["../../../core/server/places.proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,QAAA,MAAM,iBAAiB;;;CAGb,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAAG,MAAM,OAAO,iBAAiB,CAAC;AAElE,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,oBAAoB,EAC9B,gBAAgB,CAAC,EAAE,MAAM,8BAqB1B"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
const GOOGLE_PLACES_URL = {
|
|
3
|
+
autocomplete: 'https://maps.googleapis.com/maps/api/place/autocomplete/json',
|
|
4
|
+
details: 'https://maps.googleapis.com/maps/api/place/details/json',
|
|
5
|
+
};
|
|
6
|
+
export async function proxyGooglePlaces(request, endpoint, googleMapsApiKey) {
|
|
7
|
+
const googleUrl = new URL(GOOGLE_PLACES_URL[endpoint]);
|
|
8
|
+
request.nextUrl.searchParams.forEach((value, key) => {
|
|
9
|
+
googleUrl.searchParams.set(key, value);
|
|
10
|
+
});
|
|
11
|
+
const apiKey = googleMapsApiKey ?? process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY ?? '';
|
|
12
|
+
if (!googleUrl.searchParams.has('key') && apiKey) {
|
|
13
|
+
googleUrl.searchParams.set('key', apiKey);
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const response = await fetch(googleUrl.toString());
|
|
17
|
+
const data = await response.json();
|
|
18
|
+
return NextResponse.json(data);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
console.error('[raxonServer/places]', error);
|
|
22
|
+
return NextResponse.json({ error: 'Google Places API isteği başarısız' }, { status: 500 });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
/** API route kullanmak isteyenler için: export { GET, revalidate } from '@raxonltd/raxon-core/server/bootstrap' */
|
|
3
|
+
export declare const revalidate = 300;
|
|
4
|
+
export declare function GET(): Promise<NextResponse<import("../..").RaxonBootstrapPayload> | NextResponse<{
|
|
5
|
+
error: string;
|
|
6
|
+
}>>;
|
|
7
|
+
//# sourceMappingURL=raxon.bootstrap.route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raxon.bootstrap.route.d.ts","sourceRoot":"","sources":["../../../core/server/raxon.bootstrap.route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAyB3C,mHAAmH;AACnH,eAAO,MAAM,UAAU,MAA+B,CAAC;AAEvD,wBAAsB,GAAG;;IAUxB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { unstable_cache } from 'next/cache';
|
|
3
|
+
import { fetchRaxonBootstrap } from '../util/fetch.bootstrap';
|
|
4
|
+
import { BOOTSTRAP_REVALIDATE_SECONDS, bootstrapCacheHeaders, } from '../server/raxon.server';
|
|
5
|
+
async function fetchBootstrapData() {
|
|
6
|
+
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
|
|
7
|
+
const apiKey = process.env.NEXT_PUBLIC_API_KEY;
|
|
8
|
+
if (!apiUrl || !apiKey) {
|
|
9
|
+
throw new Error('[raxonServer] NEXT_PUBLIC_API_URL veya NEXT_PUBLIC_API_KEY tanımlı değil');
|
|
10
|
+
}
|
|
11
|
+
return fetchRaxonBootstrap(apiUrl, apiKey);
|
|
12
|
+
}
|
|
13
|
+
const getCachedBootstrap = unstable_cache(fetchBootstrapData, ['raxon-bootstrap'], { revalidate: BOOTSTRAP_REVALIDATE_SECONDS });
|
|
14
|
+
/** API route kullanmak isteyenler için: export { GET, revalidate } from '@raxonltd/raxon-core/server/bootstrap' */
|
|
15
|
+
export const revalidate = BOOTSTRAP_REVALIDATE_SECONDS;
|
|
16
|
+
export async function GET() {
|
|
17
|
+
try {
|
|
18
|
+
const data = await getCachedBootstrap();
|
|
19
|
+
return NextResponse.json(data, {
|
|
20
|
+
headers: bootstrapCacheHeaders(revalidate),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
console.error('[raxonServer]', error);
|
|
25
|
+
return NextResponse.json({ error: 'Bootstrap verisi alınamadı' }, { status: 502 });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import type { NextRequest } from 'next/server';
|
|
3
|
+
declare const DEFAULT_REVALIDATE_SECONDS = 300;
|
|
4
|
+
export interface RaxonServerOptions {
|
|
5
|
+
apiUrl?: string;
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
googleMapsApiKey?: string;
|
|
8
|
+
revalidate?: number;
|
|
9
|
+
}
|
|
10
|
+
declare function bootstrapCacheHeaders(revalidate?: number): {
|
|
11
|
+
'Cache-Control': string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Next.js middleware — bootstrap ve Google Places BFF isteklerini karşılar.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // proxy.ts — config Next.js tarafından statik parse edilmeli, import edilemez
|
|
18
|
+
* import { raxonServer } from '@raxonltd/raxon-core/server';
|
|
19
|
+
* export default raxonServer();
|
|
20
|
+
* export const config = { matcher: ['/api/bootstrap', '/api/places/:path'] };
|
|
21
|
+
*/
|
|
22
|
+
export declare function raxonServer(options?: RaxonServerOptions): (request: NextRequest) => Promise<NextResponse<any>>;
|
|
23
|
+
export { DEFAULT_REVALIDATE_SECONDS as BOOTSTRAP_REVALIDATE_SECONDS, bootstrapCacheHeaders };
|
|
24
|
+
//# sourceMappingURL=raxon.server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raxon.server.d.ts","sourceRoot":"","sources":["../../../core/server/raxon.server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,QAAA,MAAM,0BAA0B,MAAM,CAAC;AAQvC,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAkBD,iBAAS,qBAAqB,CAAC,UAAU,SAA6B;;EAIrE;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,kBAAuB,IAGpB,SAAS,WAAW,gCAuB3D;AAED,OAAO,EAAE,0BAA0B,IAAI,4BAA4B,EAAE,qBAAqB,EAAE,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { fetchRaxonBootstrap } from '../util/fetch.bootstrap';
|
|
3
|
+
import { proxyGooglePlaces } from '../server/places.proxy';
|
|
4
|
+
const DEFAULT_REVALIDATE_SECONDS = 300;
|
|
5
|
+
const RAXON_SERVER_PATHS = {
|
|
6
|
+
bootstrap: '/api/bootstrap',
|
|
7
|
+
placesAutocomplete: '/api/places/autocomplete',
|
|
8
|
+
placesDetails: '/api/places/details',
|
|
9
|
+
};
|
|
10
|
+
function resolveCredentials(options = {}) {
|
|
11
|
+
const apiUrl = options.apiUrl ?? process.env.NEXT_PUBLIC_API_URL;
|
|
12
|
+
const apiKey = options.apiKey ?? process.env.NEXT_PUBLIC_API_KEY;
|
|
13
|
+
return { apiUrl, apiKey };
|
|
14
|
+
}
|
|
15
|
+
async function fetchBootstrapData(options = {}) {
|
|
16
|
+
const { apiUrl, apiKey } = resolveCredentials(options);
|
|
17
|
+
if (!apiUrl || !apiKey) {
|
|
18
|
+
throw new Error('[raxonServer] NEXT_PUBLIC_API_URL veya NEXT_PUBLIC_API_KEY tanımlı değil');
|
|
19
|
+
}
|
|
20
|
+
return fetchRaxonBootstrap(apiUrl, apiKey);
|
|
21
|
+
}
|
|
22
|
+
function bootstrapCacheHeaders(revalidate = DEFAULT_REVALIDATE_SECONDS) {
|
|
23
|
+
return {
|
|
24
|
+
'Cache-Control': `public, s-maxage=${revalidate}, stale-while-revalidate=${revalidate * 2}`,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Next.js middleware — bootstrap ve Google Places BFF isteklerini karşılar.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* // proxy.ts — config Next.js tarafından statik parse edilmeli, import edilemez
|
|
32
|
+
* import { raxonServer } from '@raxonltd/raxon-core/server';
|
|
33
|
+
* export default raxonServer();
|
|
34
|
+
* export const config = { matcher: ['/api/bootstrap', '/api/places/:path'] };
|
|
35
|
+
*/
|
|
36
|
+
export function raxonServer(options = {}) {
|
|
37
|
+
const revalidate = options.revalidate ?? DEFAULT_REVALIDATE_SECONDS;
|
|
38
|
+
return async function raxonMiddleware(request) {
|
|
39
|
+
const { pathname } = request.nextUrl;
|
|
40
|
+
if (pathname === RAXON_SERVER_PATHS.bootstrap) {
|
|
41
|
+
try {
|
|
42
|
+
const data = await fetchBootstrapData(options);
|
|
43
|
+
return NextResponse.json(data, { headers: bootstrapCacheHeaders(revalidate) });
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error('[raxonServer/bootstrap]', error);
|
|
47
|
+
return NextResponse.json({ error: 'Bootstrap verisi alınamadı' }, { status: 502 });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (pathname === RAXON_SERVER_PATHS.placesAutocomplete) {
|
|
51
|
+
return proxyGooglePlaces(request, 'autocomplete', options.googleMapsApiKey);
|
|
52
|
+
}
|
|
53
|
+
if (pathname === RAXON_SERVER_PATHS.placesDetails) {
|
|
54
|
+
return proxyGooglePlaces(request, 'details', options.googleMapsApiKey);
|
|
55
|
+
}
|
|
56
|
+
return NextResponse.next();
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export { DEFAULT_REVALIDATE_SECONDS as BOOTSTRAP_REVALIDATE_SECONDS, bootstrapCacheHeaders };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../middleware.ts"],"names":[],"mappings":";AAEA,wBAA6B;AAE7B,eAAO,MAAM,MAAM;;CAElB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-bootstrap.d.ts","sourceRoot":"","sources":["../server-bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { GET, revalidate } from './core/server/raxon.bootstrap.route';
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { raxonServer } from './core/server/raxon.server';
|