@websolutespa/bom-mixer-models 2.0.2 → 3.0.1
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/CHANGELOG.md +5 -10
- package/dist/exports/proxy.d.ts +12 -0
- package/dist/exports/proxy.d.ts.map +1 -0
- package/dist/exports/proxy.js +172 -0
- package/dist/exports/proxy.js.map +1 -0
- package/dist/exports/server.d.ts +184 -0
- package/dist/exports/server.d.ts.map +1 -0
- package/dist/exports/server.js +960 -0
- package/dist/exports/server.js.map +1 -0
- package/dist/index.d.ts +254 -424
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -1450
- package/dist/index.js.map +1 -0
- package/dist/lazy-JamSYYoh.d.ts +84 -0
- package/dist/lazy-JamSYYoh.d.ts.map +1 -0
- package/dist/page-BLbZbnWg.js +12 -0
- package/dist/page-BLbZbnWg.js.map +1 -0
- package/dist/route-k0W3AKyo.js +53 -0
- package/dist/route-k0W3AKyo.js.map +1 -0
- package/package.json +75 -27
- package/src/app/app.service.ts +1 -1
- package/src/captions/captions.handler.ts +1 -1
- package/src/category/category.service.ts +3 -3
- package/src/consent_preference/consent_preference.service.ts +4 -4
- package/src/consent_preference/consent_preference.ts +1 -1
- package/src/country/country.service.ts +4 -4
- package/src/exports/proxy.ts +2 -0
- package/src/exports/server.ts +22 -0
- package/src/index.ts +3 -20
- package/src/label/label.service.ts +3 -3
- package/src/lazy/lazy.service.ts +28 -0
- package/src/lazy/lazy.ts +0 -26
- package/src/locale/locale.service.ts +3 -13
- package/src/locale/locale.ts +9 -0
- package/src/market/market.service.ts +3 -3
- package/src/menu/menu.service.ts +3 -3
- package/src/page/page.service.ts +47 -7
- package/src/page/page.ts +9 -0
- package/src/province/province.service.ts +4 -4
- package/src/redirect/redirect.service.ts +4 -4
- package/src/region/region.service.ts +4 -4
- package/src/route/route-revalidate.handler.ts +54 -43
- package/src/route/route.interceptor.ts +18 -3
- package/src/route/route.service.ts +11 -49
- package/src/route/route.ts +64 -0
- package/src/session/session.service.ts +12 -4
- package/src/sitemap/sitemap.service.ts +5 -5
- package/dist/index.mjs +0 -1363
package/src/page/page.service.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { ICategorized, ICategory, IComponent, IEquatable, ILayout, IPage, IRoute, IRouteLink, PageProps, QueryParams, SchemaType
|
|
2
|
-
import { getStore } from '@websolutespa/bom-mixer-store';
|
|
1
|
+
import { asCategoryId, asServerProps, defaultLocale, defaultMarket, ICategorized, ICategory, IComponent, IEquatable, ILayout, IPage, IRoute, IRouteLink, PageProps, QueryParams, SchemaType } from '@websolutespa/bom-core';
|
|
2
|
+
import { getStore } from '@websolutespa/bom-mixer-store/server';
|
|
3
3
|
import { Redirect } from 'next';
|
|
4
4
|
import { getSegments } from '../category/category.service';
|
|
5
5
|
import { resolveLabel } from '../label/label.service';
|
|
6
6
|
import { getLayout } from '../layout/layout.service';
|
|
7
|
-
import { getDecoratedComponents } from '../lazy/lazy';
|
|
7
|
+
import { getDecoratedComponents } from '../lazy/lazy.service';
|
|
8
8
|
import { getBreadcrumbFromSegments, getRoutes } from '../route/route.service';
|
|
9
9
|
import { IModelStore } from '../store/store';
|
|
10
10
|
|
|
@@ -77,6 +77,43 @@ export async function getPage<T extends ICategorized = ICategorized>(schema: str
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
export async function getSchemaRoutes(schema: string, market: string, locale: string): Promise<IRoute[]> {
|
|
81
|
+
const routes: IRoute[] = await getRoutes({
|
|
82
|
+
where: {
|
|
83
|
+
market: {
|
|
84
|
+
equals: market,
|
|
85
|
+
},
|
|
86
|
+
locale: {
|
|
87
|
+
equals: locale,
|
|
88
|
+
},
|
|
89
|
+
schema: {
|
|
90
|
+
equals: schema,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
return routes;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function getNotFoundRoute(market: string, locale: string): Promise<IRoute | undefined> {
|
|
98
|
+
const routes: IRoute[] = await getRoutes({
|
|
99
|
+
where: {
|
|
100
|
+
market: {
|
|
101
|
+
equals: market,
|
|
102
|
+
},
|
|
103
|
+
locale: {
|
|
104
|
+
equals: locale,
|
|
105
|
+
},
|
|
106
|
+
schema: {
|
|
107
|
+
equals: 'notfound',
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
if (routes.length > 0) {
|
|
112
|
+
return routes[0];
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
80
117
|
export async function NotFound(market: string, locale: string): Promise<{ redirect: Redirect } | void> {
|
|
81
118
|
const routes: IRoute[] = await getRoutes({
|
|
82
119
|
where: {
|
|
@@ -94,7 +131,7 @@ export async function NotFound(market: string, locale: string): Promise<{ redire
|
|
|
94
131
|
if (routes.length > 0) {
|
|
95
132
|
return {
|
|
96
133
|
redirect: {
|
|
97
|
-
destination: routes[0]
|
|
134
|
+
destination: routes[0]!.id,
|
|
98
135
|
permanent: false,
|
|
99
136
|
},
|
|
100
137
|
};
|
|
@@ -106,7 +143,7 @@ export async function getPageRoutes(schema: string, id: IEquatable): Promise<IRo
|
|
|
106
143
|
const store = await getStore<IModelStore>();
|
|
107
144
|
let routes: IRoute[] = [];
|
|
108
145
|
try {
|
|
109
|
-
routes = await store.route
|
|
146
|
+
routes = await store.route!.findMany({
|
|
110
147
|
where: {
|
|
111
148
|
page: {
|
|
112
149
|
equals: id,
|
|
@@ -137,7 +174,7 @@ export async function getPageCategory<T extends ICategorized>(schema: string, pa
|
|
|
137
174
|
const category = pages.length ? pages[0] : null;
|
|
138
175
|
// console.log(page, market, locale, category);
|
|
139
176
|
if (category) {
|
|
140
|
-
const routes = await store.route
|
|
177
|
+
const routes = await store.route!.findMany({
|
|
141
178
|
where: {
|
|
142
179
|
schema: {
|
|
143
180
|
equals: schema,
|
|
@@ -206,7 +243,10 @@ export async function getErrorPageLayout(): Promise<{ layout: ILayout, page: IPa
|
|
|
206
243
|
|
|
207
244
|
export type PartialPageProps<T extends ICategorized, B> = Omit<PageProps<T>, 'page'> & Partial<Pick<PageProps<T>, 'page'>> & B;
|
|
208
245
|
|
|
209
|
-
export async function getPageProps<T extends ICategorized, B = any>(
|
|
246
|
+
export async function getPageProps<T extends ICategorized, B = any>(
|
|
247
|
+
props: PartialPageProps<T, B>,
|
|
248
|
+
extraComponents?: IComponent[]
|
|
249
|
+
) {
|
|
210
250
|
if (props.page) {
|
|
211
251
|
await getDecoratedComponents(props as PageProps<T> & B, extraComponents);
|
|
212
252
|
}
|
package/src/page/page.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
|
|
2
|
+
export function getPublicUrl() {
|
|
3
|
+
const publicUrl = process.env && process.env.NEXT_PUBLIC_URL ? process.env.NEXT_PUBLIC_URL : '';
|
|
4
|
+
return publicUrl;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function resolveHref(href: string = '') {
|
|
8
|
+
return href.startsWith('http') ? href : `${getPublicUrl()}${href}`;
|
|
9
|
+
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { IEquatable, INamedEntity, IPaginationResult, QueryParams } from '@websolutespa/bom-core';
|
|
2
|
-
import { getStore } from '@websolutespa/bom-mixer-store';
|
|
2
|
+
import { getStore } from '@websolutespa/bom-mixer-store/server';
|
|
3
3
|
import { IModelStore } from '../store/store';
|
|
4
4
|
|
|
5
5
|
export async function getProvinces(locale?: string): Promise<INamedEntity[]> {
|
|
6
6
|
const store = await getStore<IModelStore>();
|
|
7
|
-
const items = await store.i18n_province
|
|
7
|
+
const items = await store.i18n_province!.findMany({ locale });
|
|
8
8
|
return items;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export async function getProvince(id: IEquatable, params: QueryParams = {}): Promise<INamedEntity | undefined> {
|
|
12
12
|
const store = await getStore<IModelStore>();
|
|
13
|
-
const item = await store.i18n_province
|
|
13
|
+
const item = await store.i18n_province!.findOne(id, params);
|
|
14
14
|
return item;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export async function getProvincesPagination(params: QueryParams = {}): Promise<IPaginationResult<INamedEntity>> {
|
|
18
18
|
const store = await getStore<IModelStore>();
|
|
19
|
-
const pagination = await store.i18n_province
|
|
19
|
+
const pagination = await store.i18n_province!.findPaged<INamedEntity>(params);
|
|
20
20
|
return pagination;
|
|
21
21
|
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { IEquatable, ILayout, IPaginationResult, IRedirect, QueryParams } from '@websolutespa/bom-core';
|
|
2
|
-
import { getStore } from '@websolutespa/bom-mixer-store';
|
|
2
|
+
import { getStore } from '@websolutespa/bom-mixer-store/server';
|
|
3
3
|
import { IModelStore } from '../store/store';
|
|
4
4
|
|
|
5
5
|
export async function getRedirects(locale?: string): Promise<IRedirect[]> {
|
|
6
6
|
const store = await getStore<IModelStore>();
|
|
7
|
-
const items = await store.redirect
|
|
7
|
+
const items = await store.redirect!.findMany({ locale });
|
|
8
8
|
return items;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export async function getRedirect(id: IEquatable, params: QueryParams = {}): Promise<IRedirect | undefined> {
|
|
12
12
|
const store = await getStore<IModelStore>();
|
|
13
|
-
const item = await store.redirect
|
|
13
|
+
const item = await store.redirect!.findOne(id, params);
|
|
14
14
|
return item;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export async function getRedirectsPagination(params: QueryParams = {}): Promise<IPaginationResult<IRedirect>> {
|
|
18
18
|
const store = await getStore<IModelStore>();
|
|
19
|
-
const pagination = await store.redirect
|
|
19
|
+
const pagination = await store.redirect!.findPaged<IRedirect>(params);
|
|
20
20
|
return pagination;
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { IEquatable, INamedEntity, IPaginationResult, QueryParams } from '@websolutespa/bom-core';
|
|
2
|
-
import { getStore } from '@websolutespa/bom-mixer-store';
|
|
2
|
+
import { getStore } from '@websolutespa/bom-mixer-store/server';
|
|
3
3
|
import { IModelStore } from '../store/store';
|
|
4
4
|
|
|
5
5
|
export async function getRegions(locale?: string): Promise<INamedEntity[]> {
|
|
6
6
|
const store = await getStore<IModelStore>();
|
|
7
|
-
const items = await store.i18n_region
|
|
7
|
+
const items = await store.i18n_region!.findMany({ locale });
|
|
8
8
|
return items;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export async function getRegion(id: IEquatable, params: QueryParams = {}): Promise<INamedEntity | undefined> {
|
|
12
12
|
const store = await getStore<IModelStore>();
|
|
13
|
-
const item = await store.i18n_region
|
|
13
|
+
const item = await store.i18n_region!.findOne(id, params);
|
|
14
14
|
return item;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export async function getRegionsPagination(params: QueryParams = {}): Promise<IPaginationResult<INamedEntity>> {
|
|
18
18
|
const store = await getStore<IModelStore>();
|
|
19
|
-
const pagination = await store.i18n_region
|
|
19
|
+
const pagination = await store.i18n_region!.findPaged<INamedEntity>(params);
|
|
20
20
|
return pagination;
|
|
21
21
|
}
|
|
@@ -1,43 +1,54 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
1
|
+
|
|
2
|
+
import { Where } from '@websolutespa/bom-core';
|
|
3
|
+
import { apiHandler } from '@websolutespa/bom-mixer-store/server';
|
|
4
|
+
import { NextApiRequest, NextApiResponse } from 'next';
|
|
5
|
+
import { resolveRoute } from './route';
|
|
6
|
+
import { getRoutes } from './route.service';
|
|
7
|
+
|
|
8
|
+
export function routeRevalidateHandler() {
|
|
9
|
+
return apiHandler({
|
|
10
|
+
post: async (request: NextApiRequest, response: NextApiResponse) => {
|
|
11
|
+
// console.log(request.headers);
|
|
12
|
+
const authorization = request.headers.authorization;
|
|
13
|
+
const bearer = authorization && authorization.replace('Bearer ', '');
|
|
14
|
+
// Check for secret to confirm this is a valid request
|
|
15
|
+
if (bearer !== process.env.MIXER_SECRET) {
|
|
16
|
+
return response.status(401).json({ message: 'Invalid token' });
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
/* { "id": 1, "schema": "product", "href": "/product/xxxx" } */
|
|
20
|
+
const { href, schema, page } = request.body || {};
|
|
21
|
+
|
|
22
|
+
let where: Where;
|
|
23
|
+
if (href) {
|
|
24
|
+
where = { id: { equals: href } };
|
|
25
|
+
} else if (schema && page) {
|
|
26
|
+
where = { schema: { equals: schema }, page: { equals: page } };
|
|
27
|
+
} else {
|
|
28
|
+
return response.status(400).send('Missing href or schema and page');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const routes = await getRoutes({ where });
|
|
32
|
+
if (routes.length === 0) {
|
|
33
|
+
// console.log('route.notfound', href);
|
|
34
|
+
const notFoundTarget = href || `${schema}:${page}`;
|
|
35
|
+
return response.status(404).send(`routeRevalidateHandler.notFound ${notFoundTarget}`);
|
|
36
|
+
}
|
|
37
|
+
// console.log('routes', routes);
|
|
38
|
+
for (const route of routes) {
|
|
39
|
+
// console.log('route.found', route);
|
|
40
|
+
const resolvedRoute = resolveRoute(route);
|
|
41
|
+
// console.log('route.revalidating', resolvedRoute);
|
|
42
|
+
await response.revalidate(resolvedRoute);
|
|
43
|
+
|
|
44
|
+
// @TODO: CDN cache purge
|
|
45
|
+
}
|
|
46
|
+
return response.json({ revalidated: true });
|
|
47
|
+
} catch (error) {
|
|
48
|
+
// If there was an error, Next.js will continue
|
|
49
|
+
// to show the last successfully generated page
|
|
50
|
+
return response.status(500).send('Error revalidating');
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { IMarket, IRoute, asEquatable, defaultLocale, defaultMarket, getHost, isDevelopment } from '@websolutespa/bom-core';
|
|
2
2
|
import { StoreStrategy, localApiGet, localApiPost, storeApiGet, storeApiPost, storeStrategy } from '@websolutespa/bom-mixer-store';
|
|
3
3
|
import { NextFetchEvent, NextRequest, NextResponse } from 'next/server';
|
|
4
|
-
import { getPublicUrl, resolveHref } from '../page/page
|
|
5
|
-
import { resolveRoute } from './route
|
|
4
|
+
import { getPublicUrl, resolveHref } from '../page/page';
|
|
5
|
+
import { resolveRoute } from './route';
|
|
6
6
|
|
|
7
7
|
export async function detectLocale(request: NextRequest, defaultLocale?: string) {
|
|
8
8
|
const acceptLanguage = request.headers.get('accept-language')?.split(',')[0];
|
|
@@ -11,7 +11,10 @@ export async function detectLocale(request: NextRequest, defaultLocale?: string)
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export async function detectCountry(request: NextRequest, defaultMarket?: string) {
|
|
14
|
-
|
|
14
|
+
let ip = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip');
|
|
15
|
+
if (ip) {
|
|
16
|
+
ip = ip.split(',')[0];
|
|
17
|
+
}
|
|
15
18
|
const isValidIp = ip?.match(/^(\d+?)\.(\d+?)\.(\d+?)\.(\d+?)$/);
|
|
16
19
|
let detectedCountry = '';
|
|
17
20
|
try {
|
|
@@ -67,13 +70,18 @@ export async function routeAutoDetection(request: NextRequest, next: NextFetchEv
|
|
|
67
70
|
}
|
|
68
71
|
// console.log('routeInterceptor.routeAutoDetection', url.pathname);
|
|
69
72
|
const rootRoutes = await getRootRoutes();
|
|
73
|
+
// console.log('routeInterceptor.routeAutoDetection.rootRoutes', rootRoutes);
|
|
70
74
|
const defaultRoute = rootRoutes.find(x => x.id === '/');
|
|
75
|
+
// console.log('routeInterceptor.routeAutoDetection.defaultRoute', defaultRoute);
|
|
71
76
|
if (defaultRoute) {
|
|
72
77
|
return;
|
|
73
78
|
}
|
|
74
79
|
const markets = await getMarkets();
|
|
80
|
+
// console.log('routeInterceptor.routeAutoDetection.markets', markets);
|
|
75
81
|
const market = markets.find(x => x.isDefault) || markets.find(x => x.id === defaultMarket) || markets[0];
|
|
82
|
+
// console.log('routeInterceptor.routeAutoDetection.market', market);
|
|
76
83
|
const detectedCountry = await detectCountry(request, market.id);
|
|
84
|
+
// console.log('routeInterceptor.routeAutoDetection.detectedCountry', detectedCountry);
|
|
77
85
|
const detectedMarket = detectedCountry ? (
|
|
78
86
|
markets.filter(x => Array.isArray(x.countries)).find(x => {
|
|
79
87
|
const countries: string[] = (x.countries || []).map(x => typeof x === 'string' ? x : x.id as string);
|
|
@@ -81,14 +89,18 @@ export async function routeAutoDetection(request: NextRequest, next: NextFetchEv
|
|
|
81
89
|
}) ||
|
|
82
90
|
markets.find(x => !Array.isArray(x.countries))
|
|
83
91
|
) : undefined;
|
|
92
|
+
// console.log('routeInterceptor.routeAutoDetection.detectedMarket', detectedMarket);
|
|
84
93
|
let detectedMarketRoutes = rootRoutes.filter(x => x.market === detectedMarket?.id);
|
|
85
94
|
if (detectedMarketRoutes.length === 0) {
|
|
86
95
|
detectedMarketRoutes = rootRoutes.filter(x => x.market === market.id);
|
|
87
96
|
}
|
|
97
|
+
// console.log('routeInterceptor.routeAutoDetection.detectedMarketRoutes', detectedMarketRoutes);
|
|
88
98
|
const defaultMarketLocale = market.defaultLanguage ? (
|
|
89
99
|
typeof market.defaultLanguage === 'string' ? market.defaultLanguage : market.defaultLanguage.id
|
|
90
100
|
) as string : defaultLocale;
|
|
101
|
+
// console.log('routeInterceptor.routeAutoDetection.defaultMarketLocale', defaultMarketLocale);
|
|
91
102
|
const detectedLocale = await detectLocale(request, defaultMarketLocale);
|
|
103
|
+
// console.log('routeInterceptor.routeAutoDetection.detectedLocale', detectedLocale);
|
|
92
104
|
const detectedLocaleRoutes = detectedMarketRoutes.filter(x => x.locale === detectedLocale);
|
|
93
105
|
const detectedRoute =
|
|
94
106
|
detectedLocaleRoutes.length > 0 ?
|
|
@@ -98,6 +110,7 @@ export async function routeAutoDetection(request: NextRequest, next: NextFetchEv
|
|
|
98
110
|
const marketLocale = (market.defaultLanguage ? asEquatable(market.defaultLanguage as string) : defaultMarketLocale);
|
|
99
111
|
return x.locale === marketLocale;
|
|
100
112
|
})!;
|
|
113
|
+
// console.log('routeInterceptor.routeAutoDetection.detectedRoute', detectedRoute);
|
|
101
114
|
if (detectedRoute && detectedRoute.id !== url.pathname) {
|
|
102
115
|
console.log('routeInterceptor.routeAutoDetection', detectedLocale, detectedCountry, detectedRoute.id);
|
|
103
116
|
route = detectedRoute.id;
|
|
@@ -111,8 +124,10 @@ export async function routeInterceptor(request: NextRequest, next: NextFetchEven
|
|
|
111
124
|
let route: IRoute | undefined;
|
|
112
125
|
let url = request.nextUrl;
|
|
113
126
|
const hrefBeforeRedirect = getHrefBeforeRedirect(request);
|
|
127
|
+
// console.log('routeInterceptor._live', request.nextUrl.searchParams.get('_live'));
|
|
114
128
|
try {
|
|
115
129
|
const redirectUrl = await routeAutoDetection(request, next);
|
|
130
|
+
// console.log('routeInterceptor.routeAutoDetection', redirectUrl);
|
|
116
131
|
if (redirectUrl) {
|
|
117
132
|
const response = NextResponse.redirect(resolveHref(redirectUrl), isDevelopment ? 307 : 301);
|
|
118
133
|
response.cookies.set({
|
|
@@ -1,25 +1,27 @@
|
|
|
1
|
-
import { asCategoryId, defaultLocale, defaultMarket, findRouteOfCategory, getRootCategory, ICategory, IRoute, IRouteLink,
|
|
2
|
-
import { buildStrategy, BuildStrategy,
|
|
1
|
+
import { asCategoryId, defaultLocale, defaultMarket, findRouteOfCategory, getRootCategory, ICategory, IRoute, IRouteLink, QueryParams } from '@websolutespa/bom-core';
|
|
2
|
+
import { buildStrategy, BuildStrategy, StoreStrategy, storeStrategy } from '@websolutespa/bom-mixer-store';
|
|
3
|
+
import { getStore } from '@websolutespa/bom-mixer-store/server';
|
|
3
4
|
import { IModelStore } from '../store/store';
|
|
5
|
+
import { newRouteLink, routeToRouteLink } from './route';
|
|
4
6
|
|
|
5
7
|
export type StaticPath = { params: { [key: string]: string } };
|
|
6
8
|
|
|
7
9
|
export async function getRoutes(params: QueryParams = {}): Promise<IRoute[]> {
|
|
8
10
|
const store = await getStore<IModelStore>();
|
|
9
|
-
const routes = await store.route
|
|
11
|
+
const routes = await store.route!.findMany(params);
|
|
10
12
|
return routes;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
export async function getRoute(id: string): Promise<IRoute | undefined> {
|
|
14
16
|
const store = await getStore<IModelStore>();
|
|
15
|
-
const route = await store.route
|
|
17
|
+
const route = await store.route!.findOne(id);
|
|
16
18
|
// console.log('RouteService.getRoute', id, '->', route);
|
|
17
19
|
return route;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export async function getRoutesForSchemas(schemas: string[], market?: string, locale?: string): Promise<{ [key: string]: string; }> {
|
|
21
23
|
const store = await getStore<IModelStore>();
|
|
22
|
-
const routes = await store.route
|
|
24
|
+
const routes = await store.route!.findMany({
|
|
23
25
|
where: {
|
|
24
26
|
schema: {
|
|
25
27
|
in: schemas,
|
|
@@ -43,7 +45,7 @@ export async function getRoutesForSchemas(schemas: string[], market?: string, lo
|
|
|
43
45
|
|
|
44
46
|
export async function getRoutesForTemplates(templates: string[], market?: string, locale?: string): Promise<{ [key: string]: string; }> {
|
|
45
47
|
const store = await getStore<IModelStore>();
|
|
46
|
-
const routes = await store.route
|
|
48
|
+
const routes = await store.route!.findMany({
|
|
47
49
|
where: {
|
|
48
50
|
template: {
|
|
49
51
|
in: templates,
|
|
@@ -75,7 +77,7 @@ export async function getStaticPathsForSchema(
|
|
|
75
77
|
return [];
|
|
76
78
|
}
|
|
77
79
|
const store = await getStore<IModelStore>();
|
|
78
|
-
const routes = await store.route
|
|
80
|
+
const routes = await store.route!.findMany({
|
|
79
81
|
where: template ? ({
|
|
80
82
|
and: [{
|
|
81
83
|
template: {
|
|
@@ -151,7 +153,7 @@ export async function getBreadcrumbFromSegments(segments: ICategory[], route: IR
|
|
|
151
153
|
export async function getRouteLinkTree(market: string, locale: string): Promise<IRouteLink | undefined> {
|
|
152
154
|
const store = await getStore<IModelStore>();
|
|
153
155
|
// console.log('getRouteLinkTree.store', store);
|
|
154
|
-
const routes = await store.route
|
|
156
|
+
const routes = await store.route!.findMany({
|
|
155
157
|
where: {
|
|
156
158
|
market: {
|
|
157
159
|
equals: market,
|
|
@@ -162,7 +164,7 @@ export async function getRouteLinkTree(market: string, locale: string): Promise<
|
|
|
162
164
|
},
|
|
163
165
|
});
|
|
164
166
|
// console.log('getRouteLinkTree.routes', routes.map(x => x.id));
|
|
165
|
-
const categories = await store.category
|
|
167
|
+
const categories = await store.category!.findMany({ market, locale });
|
|
166
168
|
// console.log('getRouteLinkTree.categories', categories.map(x => x.id));
|
|
167
169
|
const rootCategory = getRootCategory(categories);
|
|
168
170
|
// console.log('getRouteLinkTree.rootCategory', rootCategory?.id);
|
|
@@ -205,43 +207,3 @@ export function getRouteLinkCategory(
|
|
|
205
207
|
routeLink.items = items;
|
|
206
208
|
return routeLink;
|
|
207
209
|
}
|
|
208
|
-
|
|
209
|
-
export function newRouteLink(category: ICategory, route: IRoute | undefined, locale: string): IRouteLink {
|
|
210
|
-
const href = (route && route.id) ? route.id.toString() : '/';
|
|
211
|
-
const id = route?.page || category.id;
|
|
212
|
-
const media = category.media || route?.media;
|
|
213
|
-
const schema = route?.template || route?.schema || category?.schema || 'unknown';
|
|
214
|
-
const title = localize(category.title || route?.title || 'untitled', locale);
|
|
215
|
-
const routeLink = {
|
|
216
|
-
category: category.id,
|
|
217
|
-
href,
|
|
218
|
-
id,
|
|
219
|
-
items: [],
|
|
220
|
-
media,
|
|
221
|
-
schema,
|
|
222
|
-
title,
|
|
223
|
-
};
|
|
224
|
-
return routeLink;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
export function routeToRouteLink(route: IRoute): IRouteLink {
|
|
228
|
-
const routeLink: IRouteLink = {
|
|
229
|
-
category: asCategoryId(route.category),
|
|
230
|
-
href: route.id,
|
|
231
|
-
id: route.page,
|
|
232
|
-
items: [],
|
|
233
|
-
media: route.media,
|
|
234
|
-
schema: route.schema,
|
|
235
|
-
title: route.title,
|
|
236
|
-
};
|
|
237
|
-
return routeLink;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export function resolveRoute(route: IRoute & { splat?: string }) {
|
|
241
|
-
// console.log('resolveRoute', route);
|
|
242
|
-
const routepath: string = route.template || route.schema;
|
|
243
|
-
// convert route to next.js pages folder schema
|
|
244
|
-
const resolvedPathname = `/${route.market}/${route.locale}/${routepath}/${route.page}${route.splat || ''}`;
|
|
245
|
-
// console.log('resolveRoute', route.schema, resolvedPathname);
|
|
246
|
-
return resolvedPathname;
|
|
247
|
-
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { asCategoryId, findRouteOfCategory, getRootCategory, ICategory, IRoute, IRouteLink, localize } from '@websolutespa/bom-core';
|
|
2
|
+
|
|
3
|
+
export type StaticPath = { params: { [key: string]: string } };
|
|
4
|
+
|
|
5
|
+
export function categoryToRouteLink(routes: IRoute[], categories: ICategory[], category: ICategory, locale: string): IRouteLink | undefined {
|
|
6
|
+
const route = findRouteOfCategory(routes, category.id);
|
|
7
|
+
if (!route) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
const rootCategory = getRootCategory(categories);
|
|
11
|
+
const childCategories = categories.filter(x => {
|
|
12
|
+
const parentId = asCategoryId(x.category);
|
|
13
|
+
return rootCategory && rootCategory.id === category.id ?
|
|
14
|
+
(x.id !== rootCategory.id && (parentId === category.id || !parentId)) :
|
|
15
|
+
parentId === category.id;
|
|
16
|
+
});
|
|
17
|
+
// console.log('childCategories', category.id, childCategories);
|
|
18
|
+
const routeLink = newRouteLink(category, route, locale);
|
|
19
|
+
// console.log('categoryToRouteLink', href, category.id, route);
|
|
20
|
+
routeLink.items = childCategories
|
|
21
|
+
.map(x => categoryToRouteLink(routes, categories, x, locale))
|
|
22
|
+
.filter(x => x !== undefined) as IRouteLink[];
|
|
23
|
+
return routeLink;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function newRouteLink(category: ICategory, route: IRoute | undefined, locale: string): IRouteLink {
|
|
27
|
+
const href = (route && route.id) ? route.id.toString() : '/';
|
|
28
|
+
const id = route?.page || category.id;
|
|
29
|
+
const media = category.media || route?.media;
|
|
30
|
+
const schema = route?.template || route?.schema || category?.schema || 'unknown';
|
|
31
|
+
const title = localize(category.title || route?.title || 'untitled', locale);
|
|
32
|
+
const routeLink = {
|
|
33
|
+
category: category.id,
|
|
34
|
+
href,
|
|
35
|
+
id,
|
|
36
|
+
items: [],
|
|
37
|
+
media,
|
|
38
|
+
schema,
|
|
39
|
+
title,
|
|
40
|
+
};
|
|
41
|
+
return routeLink;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function routeToRouteLink(route: IRoute): IRouteLink {
|
|
45
|
+
const routeLink: IRouteLink = {
|
|
46
|
+
category: asCategoryId(route.category),
|
|
47
|
+
href: route.id,
|
|
48
|
+
id: route.page,
|
|
49
|
+
items: [],
|
|
50
|
+
media: route.media,
|
|
51
|
+
schema: route.schema,
|
|
52
|
+
title: route.title,
|
|
53
|
+
};
|
|
54
|
+
return routeLink;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function resolveRoute(route: IRoute & { splat?: string }) {
|
|
58
|
+
// console.log('resolveRoute', route);
|
|
59
|
+
const routepath: string = route.template || route.schema;
|
|
60
|
+
// convert route to next.js pages folder schema
|
|
61
|
+
const resolvedPathname = `/${route.market}/${route.locale}/${routepath}/${route.page}${route.splat || ''}`;
|
|
62
|
+
// console.log('resolveRoute', route.schema, resolvedPathname);
|
|
63
|
+
return resolvedPathname;
|
|
64
|
+
}
|
|
@@ -4,21 +4,29 @@ import { getIronSession, IronSession, SessionOptions } from 'iron-session';
|
|
|
4
4
|
import { NextApiRequest, NextApiResponse } from 'next';
|
|
5
5
|
import { NextRequest, NextResponse } from 'next/server';
|
|
6
6
|
|
|
7
|
+
function getSessionSecret() {
|
|
8
|
+
const secret = process.env.MIXER_SECRET || '';
|
|
9
|
+
if (!secret) {
|
|
10
|
+
throw 'getSessionSecret MIXER_SECRET not defined';
|
|
11
|
+
}
|
|
12
|
+
return secret;
|
|
13
|
+
}
|
|
14
|
+
|
|
7
15
|
export async function getSession<T extends object = Record<string, any>>(
|
|
8
16
|
request: NextRequest | NextApiRequest | IncomingMessage & {
|
|
9
17
|
cookies: Partial<{ [key: string]: string }>;
|
|
10
18
|
},
|
|
11
19
|
response: NextResponse | NextApiResponse | ServerResponse<IncomingMessage>
|
|
12
20
|
): Promise<IronSession<T>> {
|
|
13
|
-
const
|
|
14
|
-
const
|
|
21
|
+
const cookieName = 'websolutespa-next-js';
|
|
22
|
+
const password = getSessionSecret();
|
|
15
23
|
const sessionOptions: SessionOptions = {
|
|
16
|
-
cookieName
|
|
24
|
+
cookieName,
|
|
25
|
+
password,
|
|
17
26
|
// secure: true should be used in production (HTTPS) but can't be used in development (HTTP)
|
|
18
27
|
cookieOptions: {
|
|
19
28
|
secure: !isDevelopment,
|
|
20
29
|
},
|
|
21
|
-
password: COOKIE_PASSWORD,
|
|
22
30
|
};
|
|
23
31
|
const session = await getIronSession<T>(request, response, sessionOptions);
|
|
24
32
|
return session;
|
|
@@ -24,7 +24,7 @@ export async function getSiteMapIndex(origin: string): Promise<string> {
|
|
|
24
24
|
// console.log(sitemapRoutes.shift(), sitemapRoutes.pop());
|
|
25
25
|
sitemaps.push({
|
|
26
26
|
id: `/${market.id}/${locale.id}/sitemap.xml`,
|
|
27
|
-
updatedAt: sitemapRoutes.length > 0 ? sitemapRoutes[sitemapRoutes.length - 1]
|
|
27
|
+
updatedAt: sitemapRoutes.length > 0 ? sitemapRoutes[sitemapRoutes.length - 1]!.updatedAt : undefined,
|
|
28
28
|
});
|
|
29
29
|
});
|
|
30
30
|
return `<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="${origin}/sitemap.xsl"?>
|
|
@@ -122,8 +122,8 @@ export async function getSiteMapXML(origin: string, marketId?: string, localeId?
|
|
|
122
122
|
let locales: ILocale[] = await getLocales();
|
|
123
123
|
markets = markets.filter(x => x.isActive !== false);
|
|
124
124
|
locales = locales.filter(x => x.isActive !== false);
|
|
125
|
-
const defaultMarket = markets.find(x => marketId ? x.id === marketId : x.isDefault) || markets[0]
|
|
126
|
-
const defaultLocale = locales.find(x => localeId ? x.id === localeId : x.isDefault) || locales[0]
|
|
125
|
+
const defaultMarket = markets.find(x => marketId ? x.id === marketId : x.isDefault) || markets[0]!;
|
|
126
|
+
const defaultLocale = locales.find(x => localeId ? x.id === localeId : x.isDefault) || locales[0]!;
|
|
127
127
|
const defaultMarketLocale = defaultMarket && defaultMarket.defaultLanguage ? defaultMarket.defaultLanguage : defaultLocale.id;
|
|
128
128
|
const routes: IRoute[] = await getRoutes();
|
|
129
129
|
const isCanonicalRoute: (route: IRoute) => boolean = (marketId && localeId) ? (route) => {
|
|
@@ -155,7 +155,7 @@ ${getSiteMapXMLUrls(origin, canonicalRoutes)}
|
|
|
155
155
|
export async function getSiteMapXSL(localeId?: string): Promise<string> {
|
|
156
156
|
let locales: ILocale[] = await getLocales();
|
|
157
157
|
locales = locales.filter(x => x.isActive !== false);
|
|
158
|
-
const
|
|
158
|
+
const locale = locales.find(x => localeId ? x.id === localeId : x.isDefault) || locales[0]!;
|
|
159
159
|
return /* html */`<?xml version="1.0" encoding="UTF-8"?>
|
|
160
160
|
<xsl:stylesheet version="2.0"
|
|
161
161
|
xmlns:html="http://www.w3.org/TR/REC-html40"
|
|
@@ -164,7 +164,7 @@ export async function getSiteMapXSL(localeId?: string): Promise<string> {
|
|
|
164
164
|
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
|
165
165
|
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
|
|
166
166
|
<xsl:template match="/">
|
|
167
|
-
<html lang="${
|
|
167
|
+
<html lang="${locale.id}" xmlns="http://www.w3.org/1999/xhtml">
|
|
168
168
|
<head>
|
|
169
169
|
<title>XML Sitemap</title>
|
|
170
170
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|