@localazy/strapi-plugin 0.1.12-rc.6 → 0.1.13-rc.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 +0 -6
- package/admin/src/index.js +26 -1
- package/admin/src/modules/@common/components/LanguagesSelector/index.js +69 -0
- package/admin/src/modules/@common/locale/en.js +1 -0
- package/admin/src/modules/localazy-download/locale/en.js +3 -0
- package/admin/src/modules/plugin-settings/components/ContentTransferSetup/index.js +1 -2
- package/admin/src/modules/plugin-settings/components/GlobalSettings/index.js +269 -0
- package/admin/src/modules/plugin-settings/locale/en.js +38 -3
- package/admin/src/modules/plugin-settings/services/strapi-users-service.js +26 -0
- package/admin/src/pages/Download/index.js +49 -2
- package/admin/src/pages/PluginSettings/index.js +27 -11
- package/admin/src/utils/createAxiosInstance.js +2 -1
- package/package.json +2 -2
- package/server/bootstrap.js +50 -9
- package/server/controllers/localazy-transfer-controller.js +42 -4
- package/server/controllers/strapi-controller.js +3 -0
- package/server/lifecycles/deep-populate-hook.js +11 -0
- package/server/lifecycles/deprecate-event-entry-in-localazy-hook.js +69 -0
- package/server/lifecycles/upload-event-entry-to-localazy-hook.js +31 -0
- package/server/middlewares/index.js +5 -1
- package/server/middlewares/verify-webhook.js +77 -0
- package/server/routes/index.js +2 -0
- package/server/routes/localazy-public-transfer.js +20 -0
- package/server/routes/strapi.js +8 -0
- package/server/services/helpers/plugin-settings-service-helper.js +52 -0
- package/server/services/localazy-pubapi-service.js +6 -0
- package/server/services/plugin-settings-service.js +7 -1
- package/server/services/strapi-i18n-service.js +1 -1
- package/server/utils/get-picked-flatten-keys-for-hook-entry.js +270 -0
- package/server/utils/request-initiator-helper.js +36 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { isoStrapiToLocalazy } = require("./iso-locales-utils");
|
|
4
|
+
const config = require("../config").default;
|
|
5
|
+
const omitDeep = require("./omit-deep");
|
|
6
|
+
const flattenObject = require("./flatten-object");
|
|
7
|
+
const pickEntries = require("./pick-entries");
|
|
8
|
+
const {
|
|
9
|
+
findSetupModelByCollectionName,
|
|
10
|
+
getPickPaths
|
|
11
|
+
} = require("./transfer-setup-utils");
|
|
12
|
+
const PluginSettingsServiceHelper = require('../services/helpers/plugin-settings-service-helper');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* @param {*} contentTransferSetup
|
|
17
|
+
* @param {*} eventModel
|
|
18
|
+
* @returns `undefined` if such model is not model eligible for transfer or `boolean` setup
|
|
19
|
+
*/
|
|
20
|
+
const shouldProcessEntry = (contentTransferSetup, eventModel) => {
|
|
21
|
+
let key;
|
|
22
|
+
const modelContentTransferSetup = contentTransferSetup.setup
|
|
23
|
+
.find((model) => {
|
|
24
|
+
key = Object.keys(model)[0];
|
|
25
|
+
return key === eventModel;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// might be undefined as it's called on any Content-Type
|
|
29
|
+
if (!modelContentTransferSetup) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
return !!modelContentTransferSetup[key].__model__;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const getEventEntries = async (event) => {
|
|
36
|
+
switch (event.action) {
|
|
37
|
+
case 'afterCreate':
|
|
38
|
+
case 'afterUpdate': {
|
|
39
|
+
const entry = [await strapi.entityService.findOne(
|
|
40
|
+
event.model.uid,
|
|
41
|
+
event.result.id,
|
|
42
|
+
{
|
|
43
|
+
populate: "deep",
|
|
44
|
+
})];
|
|
45
|
+
return entry;
|
|
46
|
+
}
|
|
47
|
+
case "beforeDelete": {
|
|
48
|
+
const entry = [await strapi.entityService.findOne(
|
|
49
|
+
event.model.uid,
|
|
50
|
+
event.params.where.id,
|
|
51
|
+
{
|
|
52
|
+
populate: "deep",
|
|
53
|
+
})];
|
|
54
|
+
return entry;
|
|
55
|
+
}
|
|
56
|
+
case "beforeDeleteMany": {
|
|
57
|
+
/**
|
|
58
|
+
* Strapi sets the locale to the default one, so passing the ids won't help (won't fetch the entries in the correct locale)
|
|
59
|
+
* And therefore because of Strapi being Strapi, we need to get one id from the filter and fetch it's language
|
|
60
|
+
* And then pass the locale parameter to the findMany method
|
|
61
|
+
*/
|
|
62
|
+
// const entry = await strapi.entityService.findOne(
|
|
63
|
+
// event.model.uid,
|
|
64
|
+
// event.params.where.$and[0].id.$in[0],
|
|
65
|
+
// {
|
|
66
|
+
// populate: "deep",
|
|
67
|
+
// });
|
|
68
|
+
// const locale = entry.locale;
|
|
69
|
+
// const entries = await strapi.entityService.findMany(
|
|
70
|
+
// event.model.uid,
|
|
71
|
+
// {
|
|
72
|
+
// filters: {
|
|
73
|
+
// id: event.params.where.$and[0].id.$in,
|
|
74
|
+
// },
|
|
75
|
+
// locale,
|
|
76
|
+
// populate: "deep",
|
|
77
|
+
// });
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* If deletion of other than default locale is requested, `entries` will be an empty array
|
|
81
|
+
* If deletion of default locale is requested, `entries` will contain requested entries, and we can proceed with the deprecation process
|
|
82
|
+
*/
|
|
83
|
+
const entries = await strapi.entityService.findMany(
|
|
84
|
+
event.model.uid,
|
|
85
|
+
{
|
|
86
|
+
filters: event.params.where,
|
|
87
|
+
populate: "deep",
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (entries.length === 0) {
|
|
91
|
+
strapi.log.info(`Localazy Plugin: No entries in source language for ${event.action} found; the operation won't proceed`);
|
|
92
|
+
}
|
|
93
|
+
return entries;
|
|
94
|
+
}
|
|
95
|
+
default: {
|
|
96
|
+
throw new Error("Unhandled event action");
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const shouldSkipAction = async (event) => {
|
|
102
|
+
const pluginSettingsServiceHelper = new PluginSettingsServiceHelper(strapi);
|
|
103
|
+
await pluginSettingsServiceHelper.setup();
|
|
104
|
+
|
|
105
|
+
switch (event.action) {
|
|
106
|
+
case 'afterCreate': {
|
|
107
|
+
if (!pluginSettingsServiceHelper.shouldAllowAutomatedCreatedTrigger()) {
|
|
108
|
+
strapi.log.info(`Localazy Plugin: Skipping ${event.action} hook because automated created trigger is disabled.`)
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
case 'afterUpdate': {
|
|
114
|
+
if (!pluginSettingsServiceHelper.shouldAllowAutomatedUpdatedTrigger()) {
|
|
115
|
+
strapi.log.info(`Localazy Plugin: Skipping ${event.action} hook because automated update trigger is disabled.`)
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
case "beforeDelete":
|
|
121
|
+
case "beforeDeleteMany": {
|
|
122
|
+
if (!pluginSettingsServiceHelper.shouldAllowDeprecateOnDeletion()) {
|
|
123
|
+
strapi.log.info(`Localazy Plugin: Skipping ${event.action} hook because deprecate on deletion is disabled.`)
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
default: {
|
|
129
|
+
throw new Error("Unhandled event action");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
module.exports = async (event) => {
|
|
135
|
+
// Docs: https://docs.strapi.io/developer-docs/latest/development/backend-customization/models.html#hook-event-object
|
|
136
|
+
// this should respect the content transfer setup
|
|
137
|
+
// there read it and compare with the model property
|
|
138
|
+
const LocalazyUserService = strapi
|
|
139
|
+
.plugin("localazy")
|
|
140
|
+
.service("localazyUserService");
|
|
141
|
+
const LocalazyPubApiService = strapi
|
|
142
|
+
.plugin("localazy")
|
|
143
|
+
.service("localazyPubApiService");
|
|
144
|
+
const StrapiI18nService = strapi
|
|
145
|
+
.plugin("localazy")
|
|
146
|
+
.service("strapiI18nService");
|
|
147
|
+
const contentTransferSetup = await strapi
|
|
148
|
+
.plugin("localazy")
|
|
149
|
+
.service("pluginSettingsService")
|
|
150
|
+
.getContentTransferSetup();
|
|
151
|
+
|
|
152
|
+
const eventAction = event.action;
|
|
153
|
+
strapi.log.info(`${eventAction} triggered`);
|
|
154
|
+
|
|
155
|
+
// Content Transfer Setup not available yet; break execution
|
|
156
|
+
if (!contentTransferSetup.has_setup) {
|
|
157
|
+
strapi.log.warn("Localazy Plugin: Content Transfer Setup is not set up; the operation won't proceed");
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// entry model not supposed to be processed; break execution
|
|
162
|
+
const eventModel = event.model.tableName;
|
|
163
|
+
const shouldProcess = shouldProcessEntry(contentTransferSetup, eventModel);
|
|
164
|
+
if (typeof shouldProcess === 'undefined') {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (!shouldProcess) {
|
|
168
|
+
strapi.log.info("Localazy Plugin: The entry collection transfer is disabled");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// if project not connected; break execution
|
|
173
|
+
const user = await LocalazyUserService.getUser();
|
|
174
|
+
const { accessToken } = user;
|
|
175
|
+
if (!accessToken) {
|
|
176
|
+
strapi.log.error("Localazy Plugin: Localazy user is not logged in; the operation won't proceed");
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (await shouldSkipAction(event)) {
|
|
181
|
+
strapi.log.info("Localazy Plugin: Automated action skipped");
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const project = await LocalazyPubApiService.getProject(
|
|
186
|
+
user.project.id,
|
|
187
|
+
false,
|
|
188
|
+
true
|
|
189
|
+
);
|
|
190
|
+
const projectLanguages = project.languages;
|
|
191
|
+
const projectSourceLanguageId = project.sourceLanguage;
|
|
192
|
+
const sourceLanguage = projectLanguages.find(
|
|
193
|
+
(language) => language.id === projectSourceLanguageId
|
|
194
|
+
);
|
|
195
|
+
const projectSourceLanguageCode = sourceLanguage.code;
|
|
196
|
+
|
|
197
|
+
// Strapi default locale not compatible with project source language
|
|
198
|
+
const strapiLocales = await StrapiI18nService.getLocales();
|
|
199
|
+
const defaultLocale = strapiLocales.find((locale) => locale.isDefault);
|
|
200
|
+
|
|
201
|
+
const strapiDefaultLocale = defaultLocale
|
|
202
|
+
? isoStrapiToLocalazy(defaultLocale.code)
|
|
203
|
+
: config.LOCALAZY_DEFAULT_LOCALE;
|
|
204
|
+
if (strapiDefaultLocale !== projectSourceLanguageCode) {
|
|
205
|
+
strapi.log.warn("Localazy Plugin: Source locales do not match; the operation won't proceed");
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Prepare the data structure and upload it to Localazy
|
|
211
|
+
*/
|
|
212
|
+
const modelUid = event.model.uid;
|
|
213
|
+
const entries = await getEventEntries(event);
|
|
214
|
+
|
|
215
|
+
if (entries.length === 0) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ? TODO: will always be valid?
|
|
220
|
+
const eventEntryLocale = entries[0].locale;
|
|
221
|
+
|
|
222
|
+
// event entry not in source language; break execution
|
|
223
|
+
if (isoStrapiToLocalazy(eventEntryLocale) !== projectSourceLanguageCode) {
|
|
224
|
+
strapi.log.info("Localazy Plugin: Entry language is not in Localazy source language; the operation won't proceed");
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const reducedEntries = omitDeep(entries, [
|
|
229
|
+
// "__component",
|
|
230
|
+
"locale",
|
|
231
|
+
"localizations",
|
|
232
|
+
"createdAt",
|
|
233
|
+
"createdBy",
|
|
234
|
+
"updatedAt",
|
|
235
|
+
"updatedBy",
|
|
236
|
+
"publishedAt",
|
|
237
|
+
]);
|
|
238
|
+
|
|
239
|
+
const transferSetupModel = findSetupModelByCollectionName(
|
|
240
|
+
contentTransferSetup.setup,
|
|
241
|
+
eventModel
|
|
242
|
+
);
|
|
243
|
+
const currentTransferSetupModel = transferSetupModel[eventModel];
|
|
244
|
+
const pickPaths = getPickPaths(currentTransferSetupModel);
|
|
245
|
+
if (!pickPaths.length) {
|
|
246
|
+
strapi.log.warn(`No fields for collection ${eventModel} transfer are enabled.`);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const pickPathsWithUid = pickPaths.map(
|
|
250
|
+
(pickPath) => `${modelUid}.${pickPath}`
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
// flatten Strapi content
|
|
254
|
+
let flatten = {};
|
|
255
|
+
for (const reducedEntry of reducedEntries) {
|
|
256
|
+
flatten = {
|
|
257
|
+
...flatten,
|
|
258
|
+
...flattenObject({
|
|
259
|
+
[modelUid]: reducedEntry,
|
|
260
|
+
}),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
// get only enabled fields; "__component" will be filtered out inside of the function
|
|
264
|
+
const pickedFlatten = pickEntries(flatten, pickPathsWithUid, currentTransferSetupModel);
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
pickedFlatten,
|
|
268
|
+
eventEntryLocale,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
class RequestInitiatorHelper {
|
|
4
|
+
constructor(strapi) {
|
|
5
|
+
this.strapi = strapi;
|
|
6
|
+
this.ctx = strapi.requestContext.get();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
isInitiatedByLocalazyWebhook() {
|
|
10
|
+
if (typeof this.ctx !== 'undefined') {
|
|
11
|
+
const ctxHeaders = this.ctx.headers;
|
|
12
|
+
const xLocalazyHmac = ctxHeaders["x-localazy-hmac"];
|
|
13
|
+
const xLocalazyTimestamp = ctxHeaders["x-localazy-timestamp"];
|
|
14
|
+
if (!!xLocalazyHmac && !!xLocalazyTimestamp) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
isInitiatedByLocalazyPluginUI() {
|
|
22
|
+
if (typeof this.ctx !== 'undefined') {
|
|
23
|
+
const ctxHeaders = this.ctx.headers;
|
|
24
|
+
const xLocalazyInitiatedBy = ctxHeaders["x-localazy-initiated-by"];
|
|
25
|
+
if (!xLocalazyInitiatedBy) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
return xLocalazyInitiatedBy === "strapi-plugin-localazy";
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = RequestInitiatorHelper;
|