@griddo/cx 1.75.178 → 1.75.180
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 +22 -14
- package/build/index.js +49 -0
- package/gatsby-browser.tsx +140 -0
- package/gatsby-config.ts +40 -0
- package/gatsby-node.ts +130 -0
- package/gatsby-ssr.tsx +63 -0
- package/index.js +4 -0
- package/package.json +119 -100
- package/scripts/build-reset.ts +9 -0
- package/scripts/griddo-exporter.ts +302 -0
- package/src/components/Head.tsx +207 -0
- package/src/components/template.tsx +90 -0
- package/src/components/types.ts +35 -0
- package/src/components/utils.ts +138 -0
- package/src/html.tsx +31 -0
- package/src/services/auth.ts +64 -0
- package/src/services/distributors.ts +153 -0
- package/src/services/domains.ts +27 -0
- package/src/services/navigation.ts +169 -0
- package/src/services/robots.ts +64 -0
- package/src/services/settings.ts +52 -0
- package/src/services/sites.ts +199 -0
- package/src/services/store-with-for-loop.ts +513 -0
- package/src/services/store-with-promise-all.ts +515 -0
- package/src/services/store.ts +513 -0
- package/src/types/api.ts +155 -0
- package/src/types/global.ts +72 -0
- package/src/types/navigation.ts +25 -0
- package/src/types/pages.ts +145 -0
- package/src/types/sites.ts +64 -0
- package/src/types/templates.ts +11 -0
- package/src/utils/api.ts +326 -0
- package/src/utils/cache.ts +114 -0
- package/src/utils/domains.ts +32 -0
- package/src/utils/folders.ts +147 -0
- package/src/utils/instance.ts +71 -0
- package/src/utils/pages.ts +534 -0
- package/src/utils/searches.ts +102 -0
- package/src/utils/shared.ts +131 -0
- package/src/utils/sites.ts +280 -0
- package/.babelrc +0 -7
- package/LICENSE +0 -1
- package/gatsby-browser.js +0 -115
- package/gatsby-config.js +0 -38
- package/gatsby-node.js +0 -273
- package/gatsby-ssr.js +0 -43
- package/scripts/griddo-exporter.js +0 -133
- package/src/api/index.js +0 -172
- package/src/components/template.js +0 -261
- package/src/html.js +0 -30
- package/src/images/readme.md +0 -1
- package/src/scripts/build-reset.js +0 -12
- package/src/services/auth.js +0 -44
- package/src/services/distributors.js +0 -85
- package/src/services/domains.js +0 -16
- package/src/services/navigation.js +0 -104
- package/src/services/robots.js +0 -37
- package/src/services/settings.js +0 -25
- package/src/services/sites.js +0 -110
- package/src/utils/cache.js +0 -70
- package/src/utils/component-lib-helpers.js +0 -56
- package/src/utils/dataLayer.js +0 -47
- package/src/utils/delay.js +0 -5
- package/src/utils/domains.js +0 -24
- package/src/utils/folders.js +0 -123
- package/src/utils/helpers.js +0 -144
- package/src/utils/index.js +0 -44
- package/src/utils/package.js +0 -20
- package/src/utils/pages.js +0 -236
- package/src/utils/searches.js +0 -62
- package/src/utils/sites.js +0 -207
- package/static/griddo-full-logo.png +0 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Only browser environment code
|
|
2
|
+
//
|
|
3
|
+
// Don't write node code (fs, path, etc..) in this folder because is imported by
|
|
4
|
+
// `template.tsx` in a browser environment and node doesn't exists.
|
|
5
|
+
// If yo do that the render process will be break in the SSR build process
|
|
6
|
+
//
|
|
7
|
+
// Browserify doesn't work with the mixture of typescript + webpack 5 + SSR
|
|
8
|
+
|
|
9
|
+
// Types
|
|
10
|
+
import type { Fields } from "@griddo/core";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Sanitize a string separated by commas.
|
|
14
|
+
*/
|
|
15
|
+
function cleanCommaSeparated(str: string) {
|
|
16
|
+
return str
|
|
17
|
+
.split(",")
|
|
18
|
+
.map((item) => item.trim())
|
|
19
|
+
.filter(Boolean)
|
|
20
|
+
.join(",");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Format Cloudinary or DAM URL
|
|
25
|
+
* @param image The image url
|
|
26
|
+
* @param width With of the image
|
|
27
|
+
* @param height Height of the image
|
|
28
|
+
* @param format Format of the image
|
|
29
|
+
* @returns A composed URL for the Cloudinary or DAM service
|
|
30
|
+
*/
|
|
31
|
+
function formatImage(
|
|
32
|
+
image: Fields.Image | string,
|
|
33
|
+
width: number,
|
|
34
|
+
height: number,
|
|
35
|
+
format = "jpg"
|
|
36
|
+
) {
|
|
37
|
+
const url = typeof image === "string" ? image : image?.url;
|
|
38
|
+
|
|
39
|
+
if (!url) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const isCloudinary = url.split("/")[2].includes("cloudinary.com");
|
|
44
|
+
|
|
45
|
+
return isCloudinary
|
|
46
|
+
? addCloudinaryParams(url, `c_fill,w_${width},h_${height}`)
|
|
47
|
+
: addGriddoDamParams(url, `f/${format}/w/${width}/h/${height}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Format Griddo DAM image url.
|
|
52
|
+
*/
|
|
53
|
+
function addGriddoDamParams(image: string, params: string) {
|
|
54
|
+
const urlParts = image.split("/");
|
|
55
|
+
const imagePath = urlParts.slice(0, -1).join("/");
|
|
56
|
+
const imageName = urlParts.slice(-1)[0];
|
|
57
|
+
|
|
58
|
+
return `${imagePath}/${params}/${imageName}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Take a cloudinary url and add query params.
|
|
63
|
+
*/
|
|
64
|
+
function addCloudinaryParams(image: string, params: string) {
|
|
65
|
+
const plainUrl = image.replace("https://", "");
|
|
66
|
+
const head = plainUrl.split("/").slice(0, 4).join("/");
|
|
67
|
+
const fullId = plainUrl.replace(head, "");
|
|
68
|
+
|
|
69
|
+
return `https://${head}/${params}${fullId}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* TODO: JSDoc
|
|
74
|
+
*/
|
|
75
|
+
function composeAnalytics(
|
|
76
|
+
page: {
|
|
77
|
+
pageContext: {
|
|
78
|
+
siteScript: string;
|
|
79
|
+
page: {
|
|
80
|
+
dimensions: {
|
|
81
|
+
// TODO: Type dimensions / remove any
|
|
82
|
+
values: any;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
88
|
+
generateAutomaticDimensions = (page: Record<string, unknown>) => {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
) {
|
|
92
|
+
const {
|
|
93
|
+
pageContext: {
|
|
94
|
+
siteScript: siteScriptBulk,
|
|
95
|
+
page: { dimensions },
|
|
96
|
+
},
|
|
97
|
+
} = page;
|
|
98
|
+
|
|
99
|
+
const analyticsScript = siteScriptBulk ? siteScriptBulk.trim() : "";
|
|
100
|
+
|
|
101
|
+
// Las dimensiones o DataLayer
|
|
102
|
+
const dynamicValuePrefix = "__SCRIPT:";
|
|
103
|
+
const dimensionValues = dimensions?.values || {};
|
|
104
|
+
const automaticDimensionValues = generateAutomaticDimensions
|
|
105
|
+
? generateAutomaticDimensions(page)
|
|
106
|
+
: {};
|
|
107
|
+
const allDimensionsValues = {
|
|
108
|
+
...dimensionValues,
|
|
109
|
+
...automaticDimensionValues,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const allDimensions = [];
|
|
113
|
+
|
|
114
|
+
for (const dimension of Object.keys(allDimensionsValues)) {
|
|
115
|
+
const dimensionValue = allDimensionsValues[dimension];
|
|
116
|
+
allDimensions.push(
|
|
117
|
+
`"${dimension}":${
|
|
118
|
+
dimensionValue?.startsWith(dynamicValuePrefix)
|
|
119
|
+
? `${dimensionValue.slice(
|
|
120
|
+
dynamicValuePrefix.length,
|
|
121
|
+
dimensionValue.endsWith(";") ? -1 : dimensionValue.length
|
|
122
|
+
)}`
|
|
123
|
+
: `"${dimensionValue}"`
|
|
124
|
+
}`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const analyticsDimensions = allDimensions.length
|
|
129
|
+
? `{${allDimensions.join(",")}}`
|
|
130
|
+
: null;
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
analyticsScript,
|
|
134
|
+
analyticsDimensions,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export { cleanCommaSeparated, composeAnalytics, formatImage };
|
package/src/html.tsx
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { HtmlProps } from "./components/types";
|
|
3
|
+
|
|
4
|
+
export default function HTML(props: HtmlProps) {
|
|
5
|
+
return (
|
|
6
|
+
<html {...props.htmlAttributes}>
|
|
7
|
+
<head>
|
|
8
|
+
<meta charSet="utf-8" />
|
|
9
|
+
<meta httpEquiv="x-ua-compatible" content="ie=edge" />
|
|
10
|
+
<meta
|
|
11
|
+
name="viewport"
|
|
12
|
+
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
|
13
|
+
/>
|
|
14
|
+
{props.headComponents}
|
|
15
|
+
</head>
|
|
16
|
+
<body {...props.bodyAttributes}>
|
|
17
|
+
{props.preBodyComponents}
|
|
18
|
+
<noscript key="noscript" id="gatsby-noscript">
|
|
19
|
+
This app works best with JavaScript enabled.
|
|
20
|
+
</noscript>
|
|
21
|
+
<div
|
|
22
|
+
key={"body"}
|
|
23
|
+
id="___gatsby"
|
|
24
|
+
dangerouslySetInnerHTML={{ __html: props.body }}
|
|
25
|
+
/>
|
|
26
|
+
<div id="modal" />
|
|
27
|
+
{props.postBodyComponents}
|
|
28
|
+
</body>
|
|
29
|
+
</html>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// External libraries
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Service for authentication in the Griddo Private API
|
|
7
|
+
*/
|
|
8
|
+
class AuthService {
|
|
9
|
+
user: string | undefined;
|
|
10
|
+
password: string | undefined;
|
|
11
|
+
baseUrl: string | undefined;
|
|
12
|
+
headers:
|
|
13
|
+
| { Authorization: string; "Cache-Control": string; lang?: string }
|
|
14
|
+
| undefined;
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
this.user = process.env.botEmail;
|
|
18
|
+
this.password = process.env.botPassword;
|
|
19
|
+
this.baseUrl = process.env.API_URL;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async login() {
|
|
23
|
+
try {
|
|
24
|
+
const response = await axios({
|
|
25
|
+
url: `${this.baseUrl}/login_check`,
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers: {
|
|
28
|
+
"Content-Type": "application/json",
|
|
29
|
+
},
|
|
30
|
+
data: {
|
|
31
|
+
username: this.user,
|
|
32
|
+
password: this.password,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (response.status === 200) {
|
|
37
|
+
const {
|
|
38
|
+
data: { token },
|
|
39
|
+
} = response;
|
|
40
|
+
this.headers = {
|
|
41
|
+
Authorization: "bearer " + token,
|
|
42
|
+
"Cache-Control": "no-store",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log("👋 Login\n");
|
|
47
|
+
} catch (e) {
|
|
48
|
+
console.error(
|
|
49
|
+
chalk.red(`
|
|
50
|
+
╭────────────────────────────────────────────────────────────╮
|
|
51
|
+
│ Access credentials failure │
|
|
52
|
+
│ Check that the login details are correct in your .env file │
|
|
53
|
+
╰────────────────────────────────────────────────────────────╯
|
|
54
|
+
`)
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const authService = new AuthService();
|
|
63
|
+
|
|
64
|
+
export { authService as AuthService };
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
import type { Core, Fields } from "@griddo/core";
|
|
3
|
+
import type { FetchDataProps } from "../types/global";
|
|
4
|
+
import type { APIPageObject } from "../types/pages";
|
|
5
|
+
|
|
6
|
+
// Utils
|
|
7
|
+
import { logBox } from "../utils/shared";
|
|
8
|
+
|
|
9
|
+
// Services
|
|
10
|
+
import { SitesService } from "./sites";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Service to work with distributors.
|
|
14
|
+
*/
|
|
15
|
+
class DistributorService {
|
|
16
|
+
/**
|
|
17
|
+
* Get the body data from a ReferenceField in auto or manual mode.
|
|
18
|
+
* @param data The ReferenceField props.
|
|
19
|
+
* @returns The props for one of the ReferenceField mode.
|
|
20
|
+
*/
|
|
21
|
+
static getBody(data: Fields.Reference<unknown>) {
|
|
22
|
+
const {
|
|
23
|
+
order,
|
|
24
|
+
source,
|
|
25
|
+
quantity,
|
|
26
|
+
mode,
|
|
27
|
+
fixed,
|
|
28
|
+
filter,
|
|
29
|
+
fullRelations = false,
|
|
30
|
+
allLanguages = false,
|
|
31
|
+
} = data;
|
|
32
|
+
|
|
33
|
+
return mode === "auto"
|
|
34
|
+
? {
|
|
35
|
+
mode,
|
|
36
|
+
order,
|
|
37
|
+
source,
|
|
38
|
+
quantity,
|
|
39
|
+
filter,
|
|
40
|
+
fullRelations,
|
|
41
|
+
allLanguages,
|
|
42
|
+
}
|
|
43
|
+
: {
|
|
44
|
+
mode,
|
|
45
|
+
fixed,
|
|
46
|
+
fullRelations,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Gets ContentType data from API
|
|
52
|
+
*
|
|
53
|
+
* @param props
|
|
54
|
+
* @param props.page The Page object
|
|
55
|
+
* @param props.component.data The ReferenceField, always in the data prop.
|
|
56
|
+
* @param props.cached Boolean that indicates cache use.
|
|
57
|
+
* @returns The Content Type data.
|
|
58
|
+
*/
|
|
59
|
+
static async fetchContentTypeData(props: FetchDataProps) {
|
|
60
|
+
const {
|
|
61
|
+
page,
|
|
62
|
+
component: { data },
|
|
63
|
+
cached,
|
|
64
|
+
} = props;
|
|
65
|
+
|
|
66
|
+
// Distrubutor with `hasDistributorDat: true` but without `data` prop
|
|
67
|
+
if (!data) {
|
|
68
|
+
logBox(
|
|
69
|
+
`Error: Page ${page.id} has \`hasDistributorData: true\` but it doesn't have a \`data\` prop`
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const body = this.getBody(data);
|
|
76
|
+
const response = await SitesService.getDistributorData(page, body, cached);
|
|
77
|
+
|
|
78
|
+
return response;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Compose the queriedItems prop from a distributor data of a page.
|
|
83
|
+
*
|
|
84
|
+
* @param props
|
|
85
|
+
* @param props.page The APIPage object
|
|
86
|
+
* @param props.cached Boolean that indicates cache use.
|
|
87
|
+
*/
|
|
88
|
+
static async getDistributorData({
|
|
89
|
+
page,
|
|
90
|
+
cached = false,
|
|
91
|
+
}: {
|
|
92
|
+
page: APIPageObject;
|
|
93
|
+
cached: boolean;
|
|
94
|
+
}) {
|
|
95
|
+
try {
|
|
96
|
+
const { template } = page;
|
|
97
|
+
const checkDistributors = async (
|
|
98
|
+
// No puede ser Core.Page['template'] porque a medida que va bajando en
|
|
99
|
+
// el árbol ya no es la estructura de un template.
|
|
100
|
+
templateChunk: Record<string, any>, // Core.Page['template'],
|
|
101
|
+
level = 1
|
|
102
|
+
) => {
|
|
103
|
+
// If it doesn't a "template strcuture"
|
|
104
|
+
if (!templateChunk || typeof templateChunk !== "object") return;
|
|
105
|
+
|
|
106
|
+
// If doesn't have `hasDistributorData: true`
|
|
107
|
+
if (
|
|
108
|
+
!JSON.stringify(templateChunk).includes('"hasDistributorData":true')
|
|
109
|
+
) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// For each prop in the "template"
|
|
114
|
+
for (const key in templateChunk) {
|
|
115
|
+
// Si la key es `queriedItems` saltamos al siguiente `key`
|
|
116
|
+
if (key === "queriedItems") continue;
|
|
117
|
+
|
|
118
|
+
const component = templateChunk[key];
|
|
119
|
+
|
|
120
|
+
// Si el elemento no existe o no es un objeto saltamos al siguiente `key`
|
|
121
|
+
if (!component || typeof component !== "object") continue;
|
|
122
|
+
|
|
123
|
+
// Si el elemento tiene la prop `hasDistributorData: true` hacemos el fetch
|
|
124
|
+
if (component.hasDistributorData) {
|
|
125
|
+
component.queriedItems = await this.fetchContentTypeData({
|
|
126
|
+
page,
|
|
127
|
+
component,
|
|
128
|
+
cached,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
await checkDistributors(component, level + 1);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const getDistributors = async (template: Core.Page["template"]) => {
|
|
137
|
+
await checkDistributors([template]); // ==> En array para que también revise la propia template como objeto.
|
|
138
|
+
|
|
139
|
+
return template;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const response = await getDistributors(template);
|
|
143
|
+
|
|
144
|
+
return response;
|
|
145
|
+
} catch (err) {
|
|
146
|
+
console.error(`Error en get distributor ${err}`);
|
|
147
|
+
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export { DistributorService };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
import type { Domains } from "../types/global";
|
|
3
|
+
|
|
4
|
+
// Utils
|
|
5
|
+
import { get } from "../utils/api";
|
|
6
|
+
|
|
7
|
+
// Envs
|
|
8
|
+
const API_URL = process.env.API_URL;
|
|
9
|
+
|
|
10
|
+
// Constants
|
|
11
|
+
const ENDPOINTS = {
|
|
12
|
+
GET_ALL: `${API_URL}/domains`,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get an array of domains availables.
|
|
17
|
+
*/
|
|
18
|
+
class DomainsService {
|
|
19
|
+
static async getAll() {
|
|
20
|
+
const { GET_ALL } = ENDPOINTS;
|
|
21
|
+
const response = await get<Domains>({ endpoint: GET_ALL });
|
|
22
|
+
|
|
23
|
+
return response;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { DomainsService };
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
import type { Footer, Header } from "../types/navigation";
|
|
3
|
+
import type { APIPageObject } from "../types/pages";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* TODO: JSDoc
|
|
7
|
+
*/
|
|
8
|
+
class NavigationService {
|
|
9
|
+
private _defaultHeaders: Record<string, Header>;
|
|
10
|
+
private _defaultFooters: Record<string, Footer>;
|
|
11
|
+
private _navigations: {
|
|
12
|
+
headers: Array<Header>;
|
|
13
|
+
footers: Array<Footer>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
this._navigations = {
|
|
18
|
+
footers: [],
|
|
19
|
+
headers: [],
|
|
20
|
+
};
|
|
21
|
+
this._defaultHeaders = {};
|
|
22
|
+
this._defaultFooters = {};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* TODO: JSDoc
|
|
27
|
+
*/
|
|
28
|
+
set navigations(navigations) {
|
|
29
|
+
this._navigations = navigations;
|
|
30
|
+
this._defaultFooters = this.getDefaultFooters();
|
|
31
|
+
this._defaultHeaders = this.getDefaultHeaders();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* TODO: JSDoc
|
|
36
|
+
*/
|
|
37
|
+
get navigations() {
|
|
38
|
+
return this._navigations;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* TODO: JSDoc
|
|
43
|
+
*/
|
|
44
|
+
getDefaultFooters() {
|
|
45
|
+
const safeFooters = [...this.navigations.footers];
|
|
46
|
+
const defaultFooters = safeFooters.filter(
|
|
47
|
+
(footer) => !!footer.setAsDefault
|
|
48
|
+
);
|
|
49
|
+
const defaultFootersByLang = defaultFooters.reduce((prev, footer) => {
|
|
50
|
+
const { language } = footer;
|
|
51
|
+
|
|
52
|
+
return { ...prev, [language]: footer };
|
|
53
|
+
}, {});
|
|
54
|
+
|
|
55
|
+
return defaultFootersByLang;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* TODO: JSDoc
|
|
60
|
+
*/
|
|
61
|
+
getDefaultHeaders() {
|
|
62
|
+
const safeHeaders = [...this.navigations.headers];
|
|
63
|
+
const defaultHeaders = safeHeaders.filter(
|
|
64
|
+
(header) => !!header.setAsDefault
|
|
65
|
+
);
|
|
66
|
+
const defaultHeadersByLang = defaultHeaders.reduce((prev, header) => {
|
|
67
|
+
const { language } = header;
|
|
68
|
+
|
|
69
|
+
return { ...prev, [language]: header };
|
|
70
|
+
}, {});
|
|
71
|
+
|
|
72
|
+
return defaultHeadersByLang;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* TODO: JSDoc
|
|
77
|
+
*/
|
|
78
|
+
getRightLanguage(
|
|
79
|
+
list: Array<{
|
|
80
|
+
language: number;
|
|
81
|
+
navigationLanguages: Array<{ navigationId: number }>;
|
|
82
|
+
id: number;
|
|
83
|
+
}>,
|
|
84
|
+
id: number,
|
|
85
|
+
language: number
|
|
86
|
+
) {
|
|
87
|
+
if (!list || !id) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const rightLanguageItem = list.find(
|
|
92
|
+
(item) =>
|
|
93
|
+
item.language === language &&
|
|
94
|
+
item.navigationLanguages?.find((version) => version.navigationId === id)
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const result = rightLanguageItem || list.find((item) => item.id === id);
|
|
98
|
+
|
|
99
|
+
return result ? { ...result } : null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* TODO: JSDoc
|
|
104
|
+
*/
|
|
105
|
+
getPageHeader(id: number, language: number) {
|
|
106
|
+
return this.getRightLanguage(this.navigations.headers, id, language);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* TODO: JSDoc
|
|
111
|
+
*/
|
|
112
|
+
getPageFooter(id: number, language: number) {
|
|
113
|
+
return this.getRightLanguage(this.navigations.footers, id, language);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* TODO: JSDoc
|
|
118
|
+
*/
|
|
119
|
+
getPageNavigations(page: APIPageObject) {
|
|
120
|
+
const {
|
|
121
|
+
header: pageHeader,
|
|
122
|
+
footer: pageFooter,
|
|
123
|
+
language,
|
|
124
|
+
template: { templateType },
|
|
125
|
+
templateConfig: { defaultHeader, defaultFooter, templates },
|
|
126
|
+
} = page;
|
|
127
|
+
|
|
128
|
+
// The navigations would be:
|
|
129
|
+
// - The one with the page or ...
|
|
130
|
+
// - The one defined for that template or ...
|
|
131
|
+
// - The one you have defined for that data package
|
|
132
|
+
const getValidNavigation = (values: Array<number | unknown>) => {
|
|
133
|
+
const fineNavigation = values.find((item) => typeof item === "number");
|
|
134
|
+
|
|
135
|
+
return typeof fineNavigation === "number" ? fineNavigation : null;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const headerID = getValidNavigation([
|
|
139
|
+
pageHeader,
|
|
140
|
+
templates?.[templateType]?.defaultHeader,
|
|
141
|
+
defaultHeader,
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
const footerID = getValidNavigation([
|
|
145
|
+
pageFooter,
|
|
146
|
+
templates?.[templateType]?.defaultFooter,
|
|
147
|
+
defaultFooter,
|
|
148
|
+
]);
|
|
149
|
+
|
|
150
|
+
const header = headerID
|
|
151
|
+
? this.getPageHeader(headerID, language)
|
|
152
|
+
: headerID === 0
|
|
153
|
+
? null
|
|
154
|
+
: this._defaultHeaders[language];
|
|
155
|
+
|
|
156
|
+
const footer = footerID
|
|
157
|
+
? this.getPageFooter(footerID, language)
|
|
158
|
+
: footerID === 0
|
|
159
|
+
? null
|
|
160
|
+
: this._defaultFooters[language];
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
header,
|
|
164
|
+
footer,
|
|
165
|
+
} as { header: Header; footer: Footer };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export { NavigationService };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
import type { Robots } from "../types/global";
|
|
3
|
+
|
|
4
|
+
// External libraries
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
|
|
8
|
+
// Utils
|
|
9
|
+
import { get } from "../utils/api";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* TODO: JSDoc
|
|
13
|
+
*/
|
|
14
|
+
class RobotsService {
|
|
15
|
+
robots: Robots;
|
|
16
|
+
baseURL: string | undefined;
|
|
17
|
+
settings: Record<string, unknown>;
|
|
18
|
+
endpoint: string;
|
|
19
|
+
|
|
20
|
+
constructor() {
|
|
21
|
+
this.robots = [];
|
|
22
|
+
this.baseURL = process.env.API_URL;
|
|
23
|
+
this.settings = {};
|
|
24
|
+
this.endpoint = `${this.baseURL}/domains/robots`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* TODO: JSDoc
|
|
29
|
+
*/
|
|
30
|
+
async loadRobots() {
|
|
31
|
+
try {
|
|
32
|
+
const apiRobots = await get<Robots>({
|
|
33
|
+
endpoint: this.endpoint,
|
|
34
|
+
});
|
|
35
|
+
this.robots =
|
|
36
|
+
apiRobots
|
|
37
|
+
?.filter((r) => !!r.path)
|
|
38
|
+
.map(({ path, content }) => ({
|
|
39
|
+
path,
|
|
40
|
+
content: content || "User-agent: *\n\r\n\rAllow: /",
|
|
41
|
+
})) || [];
|
|
42
|
+
} catch (e) {
|
|
43
|
+
console.warn(`${this.constructor.name}: ${(e as Error).message}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* TODO: JSDoc
|
|
49
|
+
*/
|
|
50
|
+
async writeFiles(basePath = "") {
|
|
51
|
+
for (const robot of this.robots) {
|
|
52
|
+
const basePathLocation = path.join(basePath, robot.path);
|
|
53
|
+
const fileLocation = path.join(basePathLocation, "robots.txt");
|
|
54
|
+
|
|
55
|
+
if (fs.existsSync(basePathLocation)) {
|
|
56
|
+
fs.writeFileSync(fileLocation, robot.content);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const robotsService = new RobotsService();
|
|
63
|
+
|
|
64
|
+
export { robotsService as RobotsService };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
import type { Settings } from "../types/global";
|
|
3
|
+
|
|
4
|
+
// Utils
|
|
5
|
+
import { get, post } from "../utils/api";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* TODO: JSDoc
|
|
9
|
+
*/
|
|
10
|
+
class SettingsService {
|
|
11
|
+
baseURL?: string;
|
|
12
|
+
settings: Settings;
|
|
13
|
+
ENDPOINTS: { settings: string; resetRender: string };
|
|
14
|
+
|
|
15
|
+
constructor() {
|
|
16
|
+
this.baseURL = process.env.API_URL;
|
|
17
|
+
this.settings = {
|
|
18
|
+
cloudinaryName: "",
|
|
19
|
+
useMetaTitle: false,
|
|
20
|
+
showBasicMetaRobots: false,
|
|
21
|
+
avoidHrefLangsOnCanonicals: false,
|
|
22
|
+
avoidSelfReferenceCanonicals: false,
|
|
23
|
+
avoidHrefLangXDefault: false,
|
|
24
|
+
avoidDebugMetas: false,
|
|
25
|
+
};
|
|
26
|
+
this.ENDPOINTS = {
|
|
27
|
+
settings: `${this.baseURL}/settings`,
|
|
28
|
+
resetRender: `${this.baseURL}/debug/reset-render`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get settings for a full domain render.
|
|
34
|
+
*/
|
|
35
|
+
async getAll() {
|
|
36
|
+
const { settings } = this.ENDPOINTS;
|
|
37
|
+
const response = await get<Settings>({ endpoint: settings });
|
|
38
|
+
this.settings = response;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* TODO: JSDoc
|
|
43
|
+
*/
|
|
44
|
+
async resetRender() {
|
|
45
|
+
const { resetRender } = this.ENDPOINTS;
|
|
46
|
+
await post({ endpoint: resetRender });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const settingsService = new SettingsService();
|
|
51
|
+
|
|
52
|
+
export { settingsService as SettingsService };
|