@localazy/strapi-plugin 1.3.0 → 1.6.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 +30 -0
- package/dist/_chunks/{FetchIdentity-BJGoZPpM.mjs → FetchIdentity-Bm7bc5bS.mjs} +36 -44
- package/dist/_chunks/{FetchIdentity-CtBRO-Cn.js → FetchIdentity-CcEjakiz.js} +25 -33
- package/dist/_chunks/{LanguagesSelector-DJxgCwgM.mjs → LanguagesSelector-B1rwN7-Y.mjs} +4 -3
- package/dist/_chunks/{LanguagesSelector-CNaKrS-m.js → LanguagesSelector-DejYCwBl.js} +4 -3
- package/dist/_chunks/{LocalazyApp-CJRpsTKr.mjs → LocalazyApp-C4kbsnQh.mjs} +148 -35
- package/dist/_chunks/{LocalazyApp-B1qCCEPe.js → LocalazyApp-jPO7Yr1S.js} +154 -41
- package/dist/_chunks/{LocalazyContentTransferSetup-CLAWn95U.mjs → LocalazyContentTransferSetup-C1wDD9hA.mjs} +58 -22
- package/dist/_chunks/{LocalazyContentTransferSetup-CCxiLHdG.js → LocalazyContentTransferSetup-DZh4ZdSi.js} +63 -27
- package/dist/_chunks/{LocalazyGlobalSettings-BJ5qfFfK.js → LocalazyGlobalSettings-D76it6rn.js} +57 -26
- package/dist/_chunks/{LocalazyGlobalSettings-Z-4Ykc_J.mjs → LocalazyGlobalSettings-DOW5rjLn.mjs} +56 -25
- package/dist/_chunks/{LocalazyPanel-CL_mzzfD.mjs → LocalazyPanel-C_Z4W49T.mjs} +12 -6
- package/dist/_chunks/{LocalazyPanel-DE3TtHOQ.js → LocalazyPanel-D2LQpqii.js} +14 -8
- package/dist/_chunks/{LocalazyStatusColumn-R5n6q_cx.mjs → LocalazyStatusColumn-C_ZCd_s7.mjs} +10 -4
- package/dist/_chunks/{LocalazyStatusColumn-BMLcklG-.js → LocalazyStatusColumn-Dy6N4_X6.js} +10 -4
- package/dist/_chunks/{RequireLocalazyAuth-WKKFdQlR.js → RequireLocalazyAuth-BVwF0bRC.js} +31 -4
- package/dist/_chunks/{RequireLocalazyAuth-Dzsu6UNS.mjs → RequireLocalazyAuth-DNhTZZ_6.mjs} +32 -5
- package/dist/_chunks/{entry-exclusion-service-DxqTlZFR.mjs → entry-exclusion-service-CF_yNCly.mjs} +1 -1
- package/dist/_chunks/{entry-exclusion-service-CggmAKxq.js → entry-exclusion-service-CH3ZoJQN.js} +1 -1
- package/dist/_chunks/{has-model-changed-FMGVcFHU.mjs → has-model-changed-CVJEuGxM.mjs} +2 -2
- package/dist/_chunks/{has-model-changed-BR53L6pA.js → has-model-changed-DdYbyv0v.js} +2 -2
- package/dist/_chunks/{i18n-QelOdIB2.mjs → i18n-76ox2llT.mjs} +9 -2
- package/dist/_chunks/{i18n-CAsbU4pi.js → i18n-C5ucROBh.js} +9 -2
- package/dist/_chunks/{index-BmxGKspv.js → index-CEGaWWh3.js} +33 -11
- package/dist/_chunks/{index-BK7lmZSN.js → index-CTkxoioB.js} +1 -0
- package/dist/_chunks/{index-toauQlIK.mjs → index-DP2ugzc_.mjs} +34 -12
- package/dist/_chunks/{index-CHjIMO9T.mjs → index-shryj0jT.mjs} +19 -18
- package/dist/_chunks/{strapi-api-base-CT9lknwW.js → strapi-api-base-CCUuu7EY.js} +1 -1
- package/dist/_chunks/{strapi-api-base-BflplVsH.mjs → strapi-api-base-IiMEtDoH.mjs} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/components/LocalazyStatusColumn.d.ts +1 -1
- package/dist/admin/src/constants/permissions.d.ts +29 -0
- package/dist/admin/src/modules/@common/components/LanguagesSelector.d.ts +1 -0
- package/dist/admin/src/modules/activity-logs/locale/en.d.ts +4 -0
- package/dist/admin/src/modules/activity-logs/services/debug-bundle-service.d.ts +10 -0
- package/dist/admin/src/modules/login/locale/en.d.ts +1 -0
- package/dist/admin/src/modules/plugin-settings/components/Tree.d.ts +1 -0
- package/dist/admin/src/modules/plugin-settings/components/TreeItem.d.ts +1 -0
- package/dist/admin/src/modules/plugin-settings/components/WebhookSetup.d.ts +4 -1
- package/dist/admin/src/modules/plugin-settings/locale/en.d.ts +2 -0
- package/dist/admin/src/modules/plugin-settings/services/plugin-settings-service.d.ts +4 -0
- package/dist/server/index.js +665 -38
- package/dist/server/index.mjs +665 -39
- package/dist/server/src/bootstrap.d.ts +1 -1
- package/dist/server/src/constants/permissions.d.ts +38 -0
- package/dist/server/src/controllers/debug-bundle-controller.d.ts +7 -0
- package/dist/server/src/controllers/index.d.ts +6 -0
- package/dist/server/src/controllers/plugin-settings-controller.d.ts +1 -0
- package/dist/server/src/core/index.d.ts +5 -1
- package/dist/server/src/db/model/activity-logs.d.ts +10 -0
- package/dist/server/src/index.d.ts +64 -8
- package/dist/server/src/routes/activity-logs-routes.d.ts +20 -3
- package/dist/server/src/routes/debug-bundle-routes.d.ts +14 -0
- package/dist/server/src/routes/entry-exclusion-routes.d.ts +20 -3
- package/dist/server/src/routes/index.d.ts +52 -6
- package/dist/server/src/routes/localazy-auth-routes.d.ts +6 -1
- package/dist/server/src/routes/localazy-project-routes.d.ts +20 -3
- package/dist/server/src/routes/localazy-transfer-routes.d.ts +6 -1
- package/dist/server/src/routes/localazy-user-routes.d.ts +20 -3
- package/dist/server/src/routes/plugin-settings-routes.d.ts +20 -3
- package/dist/server/src/routes/strapi-routes.d.ts +20 -3
- package/dist/server/src/services/activity-logs-service.d.ts +3 -1
- package/dist/server/src/services/index.d.ts +5 -1
- package/dist/server/src/services/localazy-transfer-download-service.d.ts +2 -1
- package/dist/server/src/services/plugin-settings-service.d.ts +4 -0
- package/dist/server/src/utils/build-debug-bundle.d.ts +68 -0
- package/dist/server/src/utils/sanitize-debug-bundle.d.ts +25 -0
- package/package.json +2 -1
package/dist/server/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const require$$0$2 = require("crypto");
|
|
|
10
10
|
const require$$0$3 = require("events");
|
|
11
11
|
const require$$2 = require("timers");
|
|
12
12
|
const require$$8 = require("ws");
|
|
13
|
+
const JSZip = require("jszip");
|
|
13
14
|
const _interopDefault = (e2) => e2 && e2.__esModule ? e2 : { default: e2 };
|
|
14
15
|
const require$$0__default$2 = /* @__PURE__ */ _interopDefault(require$$0$4);
|
|
15
16
|
const require$$1__default$4 = /* @__PURE__ */ _interopDefault(require$$1$4);
|
|
@@ -22,6 +23,7 @@ const require$$0__default = /* @__PURE__ */ _interopDefault(require$$0$2);
|
|
|
22
23
|
const require$$0__default$1 = /* @__PURE__ */ _interopDefault(require$$0$3);
|
|
23
24
|
const require$$2__default = /* @__PURE__ */ _interopDefault(require$$2);
|
|
24
25
|
const require$$8__default = /* @__PURE__ */ _interopDefault(require$$8);
|
|
26
|
+
const JSZip__default = /* @__PURE__ */ _interopDefault(JSZip);
|
|
25
27
|
var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
|
|
26
28
|
function getDefaultExportFromCjs(x2) {
|
|
27
29
|
return x2 && x2.__esModule && Object.prototype.hasOwnProperty.call(x2, "default") ? x2["default"] : x2;
|
|
@@ -20532,7 +20534,7 @@ var distExports = dist$1.exports;
|
|
|
20532
20534
|
const io = /* @__PURE__ */ getDefaultExportFromCjs(distExports);
|
|
20533
20535
|
const { Server: Server2, Namespace, Socket: Socket3 } = io;
|
|
20534
20536
|
const name = "@localazy/strapi-plugin";
|
|
20535
|
-
const version = "1.
|
|
20537
|
+
const version = "1.6.0";
|
|
20536
20538
|
const description = "The official Strapi Plugin by Localazy.";
|
|
20537
20539
|
const keywords = [
|
|
20538
20540
|
"strapi",
|
|
@@ -20602,6 +20604,7 @@ const scripts = {
|
|
|
20602
20604
|
prepare: "husky || true"
|
|
20603
20605
|
};
|
|
20604
20606
|
const dependencies = {
|
|
20607
|
+
jszip: "^3.10.1",
|
|
20605
20608
|
shelljs: "^0.8.5",
|
|
20606
20609
|
ws: "^8.18.0"
|
|
20607
20610
|
};
|
|
@@ -20715,7 +20718,60 @@ const _package$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePr
|
|
|
20715
20718
|
version
|
|
20716
20719
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
20717
20720
|
const PLUGIN_NAME = strapi$1.name;
|
|
20718
|
-
const
|
|
20721
|
+
const PERMISSION_UIDS = {
|
|
20722
|
+
READ: `plugin::${PLUGIN_NAME}.read`,
|
|
20723
|
+
TRANSFER: `plugin::${PLUGIN_NAME}.transfer`,
|
|
20724
|
+
SETTINGS_UPDATE: `plugin::${PLUGIN_NAME}.settings.update`
|
|
20725
|
+
};
|
|
20726
|
+
const LOCALAZY_RBAC_ACTIONS = [
|
|
20727
|
+
{
|
|
20728
|
+
section: "plugins",
|
|
20729
|
+
subCategory: "General",
|
|
20730
|
+
pluginName: PLUGIN_NAME,
|
|
20731
|
+
displayName: "Read",
|
|
20732
|
+
uid: "read"
|
|
20733
|
+
},
|
|
20734
|
+
{
|
|
20735
|
+
section: "plugins",
|
|
20736
|
+
subCategory: "General",
|
|
20737
|
+
pluginName: PLUGIN_NAME,
|
|
20738
|
+
displayName: "Transfer",
|
|
20739
|
+
uid: "transfer"
|
|
20740
|
+
},
|
|
20741
|
+
{
|
|
20742
|
+
section: "plugins",
|
|
20743
|
+
subCategory: "Settings",
|
|
20744
|
+
pluginName: PLUGIN_NAME,
|
|
20745
|
+
displayName: "Read settings",
|
|
20746
|
+
uid: "settings.read"
|
|
20747
|
+
},
|
|
20748
|
+
{
|
|
20749
|
+
section: "plugins",
|
|
20750
|
+
subCategory: "Settings",
|
|
20751
|
+
pluginName: PLUGIN_NAME,
|
|
20752
|
+
displayName: "Update settings",
|
|
20753
|
+
uid: "settings.update"
|
|
20754
|
+
}
|
|
20755
|
+
];
|
|
20756
|
+
const registerRbacActions = async (strapi2) => {
|
|
20757
|
+
try {
|
|
20758
|
+
const permissionService = strapi2.service("admin::permission");
|
|
20759
|
+
const actionProvider = permissionService?.actionProvider;
|
|
20760
|
+
if (!actionProvider?.registerMany) {
|
|
20761
|
+
strapi2.log.warn(
|
|
20762
|
+
`[${PLUGIN_NAME}] admin::permission.actionProvider unavailable — skipping RBAC action registration`
|
|
20763
|
+
);
|
|
20764
|
+
return;
|
|
20765
|
+
}
|
|
20766
|
+
await actionProvider.registerMany(LOCALAZY_RBAC_ACTIONS);
|
|
20767
|
+
} catch (error) {
|
|
20768
|
+
strapi2.log.error(
|
|
20769
|
+
`[${PLUGIN_NAME}] Failed to register RBAC actions — plugin permissions will not appear in role editor: ${error instanceof Error ? error.message : String(error)}`
|
|
20770
|
+
);
|
|
20771
|
+
}
|
|
20772
|
+
};
|
|
20773
|
+
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
20774
|
+
await registerRbacActions(strapi2);
|
|
20719
20775
|
if (strapi2.plugin(PLUGIN_NAME)?.config("enableSocketIO", true)) {
|
|
20720
20776
|
process.nextTick(async () => {
|
|
20721
20777
|
const io2 = new Server2(strapi2.server.httpServer, {
|
|
@@ -24298,6 +24354,9 @@ const PluginSettingsController = ({ strapi: _strapi }) => ({
|
|
|
24298
24354
|
async updatePluginSettings(ctx) {
|
|
24299
24355
|
ctx.body = await getPluginSettingsService().updatePluginSettings(ctx.request.body);
|
|
24300
24356
|
},
|
|
24357
|
+
async updatePluginSettingsUiPrefs(ctx) {
|
|
24358
|
+
ctx.body = await getPluginSettingsService().updatePluginSettingsUiPrefs(ctx.request.body);
|
|
24359
|
+
},
|
|
24301
24360
|
async getSyncCursor(ctx) {
|
|
24302
24361
|
ctx.body = await getPluginSettingsService().getSyncCursor();
|
|
24303
24362
|
}
|
|
@@ -24397,6 +24456,8 @@ const LocalazyTransferController = ({ strapi: strapi2 }) => ({
|
|
|
24397
24456
|
try {
|
|
24398
24457
|
const LocalazyTransferDownloadService2 = getLocalazyTransferDownloadService();
|
|
24399
24458
|
let fullSync = ctx.request.body?.fullSync === true;
|
|
24459
|
+
const rawUiLanguages = ctx.request.body?.uiLanguages;
|
|
24460
|
+
const requestedUiLanguages = Array.isArray(rawUiLanguages) && rawUiLanguages.every((code) => typeof code === "string") ? rawUiLanguages : void 0;
|
|
24400
24461
|
const requestInitiatorHelper = new RequestInitiatorHelper(strapi2);
|
|
24401
24462
|
if (requestInitiatorHelper.isInitiatedByLocalazyWebhook()) {
|
|
24402
24463
|
const pluginSettingsServiceHelper = new PluginSettingsServiceHelper();
|
|
@@ -24406,7 +24467,8 @@ const LocalazyTransferController = ({ strapi: strapi2 }) => ({
|
|
|
24406
24467
|
setTimeout(
|
|
24407
24468
|
() => LocalazyTransferDownloadService2.download({
|
|
24408
24469
|
notificationService: JobNotificationService$1,
|
|
24409
|
-
fullSync
|
|
24470
|
+
fullSync,
|
|
24471
|
+
requestedUiLanguages
|
|
24410
24472
|
}),
|
|
24411
24473
|
1e3
|
|
24412
24474
|
);
|
|
@@ -24501,6 +24563,413 @@ const ActivityLogsController = ({ strapi: _strapi }) => ({
|
|
|
24501
24563
|
ctx.body = { success: true };
|
|
24502
24564
|
}
|
|
24503
24565
|
});
|
|
24566
|
+
const URL_SECRET_KEYS = ["token", "secret", "signature", "key", "apikey", "api_key"];
|
|
24567
|
+
const DEBUG_BUNDLE_ENV_ALLOWLIST = [
|
|
24568
|
+
"STRAPI_ADMIN_LOCALAZY_ENV",
|
|
24569
|
+
"STRAPI_ADMIN_BACKEND_URL",
|
|
24570
|
+
"NODE_ENV",
|
|
24571
|
+
"STRAPI_TELEMETRY_DISABLED"
|
|
24572
|
+
];
|
|
24573
|
+
const redactIdentity = (identity2) => {
|
|
24574
|
+
if (!identity2) return null;
|
|
24575
|
+
return {
|
|
24576
|
+
userId: identity2.user?.id ?? "",
|
|
24577
|
+
name: identity2.user?.name ?? "",
|
|
24578
|
+
scope: identity2.scope ?? "",
|
|
24579
|
+
hasAccessToken: Boolean(identity2.accessToken)
|
|
24580
|
+
};
|
|
24581
|
+
};
|
|
24582
|
+
const scrubUrlSecrets = (input) => {
|
|
24583
|
+
if (typeof input !== "string") return input;
|
|
24584
|
+
if (!/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(input) && !input.includes("?")) return input;
|
|
24585
|
+
try {
|
|
24586
|
+
const url2 = new URL(input);
|
|
24587
|
+
let mutated = false;
|
|
24588
|
+
for (const k2 of [...url2.searchParams.keys()]) {
|
|
24589
|
+
if (URL_SECRET_KEYS.includes(k2.toLowerCase())) {
|
|
24590
|
+
url2.searchParams.delete(k2);
|
|
24591
|
+
mutated = true;
|
|
24592
|
+
}
|
|
24593
|
+
}
|
|
24594
|
+
return mutated ? url2.toString() : input;
|
|
24595
|
+
} catch {
|
|
24596
|
+
return input;
|
|
24597
|
+
}
|
|
24598
|
+
};
|
|
24599
|
+
const pickEnvAllowlist = (env) => {
|
|
24600
|
+
const out = {};
|
|
24601
|
+
for (const key of DEBUG_BUNDLE_ENV_ALLOWLIST) {
|
|
24602
|
+
const raw = env[key];
|
|
24603
|
+
const present = raw !== void 0;
|
|
24604
|
+
out[key] = {
|
|
24605
|
+
present,
|
|
24606
|
+
value: present ? raw : null
|
|
24607
|
+
};
|
|
24608
|
+
}
|
|
24609
|
+
return out;
|
|
24610
|
+
};
|
|
24611
|
+
const serializeContentTypeSchema = (contentType) => {
|
|
24612
|
+
if (!contentType || typeof contentType !== "object") return null;
|
|
24613
|
+
const ct = contentType;
|
|
24614
|
+
return {
|
|
24615
|
+
kind: ct.kind,
|
|
24616
|
+
collectionName: ct.collectionName,
|
|
24617
|
+
info: ct.info,
|
|
24618
|
+
options: ct.options,
|
|
24619
|
+
pluginOptions: ct.pluginOptions,
|
|
24620
|
+
attributes: ct.attributes
|
|
24621
|
+
};
|
|
24622
|
+
};
|
|
24623
|
+
const DOCUMENT_INTERNAL_FIELDS = ["createdBy", "updatedBy"];
|
|
24624
|
+
const stripDocumentInternals = (doc) => {
|
|
24625
|
+
if (!doc || typeof doc !== "object") return doc;
|
|
24626
|
+
const clone = { ...doc };
|
|
24627
|
+
for (const field of DOCUMENT_INTERNAL_FIELDS) {
|
|
24628
|
+
delete clone[field];
|
|
24629
|
+
}
|
|
24630
|
+
return clone;
|
|
24631
|
+
};
|
|
24632
|
+
class SessionNotFoundError extends Error {
|
|
24633
|
+
constructor(sessionId) {
|
|
24634
|
+
super(`Session ${sessionId} not found`);
|
|
24635
|
+
this.name = "SessionNotFoundError";
|
|
24636
|
+
}
|
|
24637
|
+
}
|
|
24638
|
+
const DEBUG_BUNDLE_SCHEMA_VERSION = 1;
|
|
24639
|
+
const CAPS = {
|
|
24640
|
+
perEntryBytes: 1 * 1024 * 1024,
|
|
24641
|
+
// 1 MB
|
|
24642
|
+
totalEntries: 100,
|
|
24643
|
+
errorLogBytes: 2 * 1024 * 1024,
|
|
24644
|
+
// 2 MB
|
|
24645
|
+
consoleErrorsLines: 200,
|
|
24646
|
+
browserPayloadBytes: 64 * 1024
|
|
24647
|
+
// 64 KB
|
|
24648
|
+
};
|
|
24649
|
+
const selectAttemptedEntries = (attempted) => {
|
|
24650
|
+
const failedByKey = /* @__PURE__ */ new Map();
|
|
24651
|
+
const successByKey = /* @__PURE__ */ new Map();
|
|
24652
|
+
for (const record of attempted) {
|
|
24653
|
+
const key = `${record.uid}|${record.documentId}|${record.locale}`;
|
|
24654
|
+
const selected = {
|
|
24655
|
+
uid: record.uid,
|
|
24656
|
+
documentId: record.documentId,
|
|
24657
|
+
locale: record.locale,
|
|
24658
|
+
status: record.status,
|
|
24659
|
+
attemptedAt: record.attemptedAt
|
|
24660
|
+
};
|
|
24661
|
+
if (record.status === "failed") {
|
|
24662
|
+
successByKey.delete(key);
|
|
24663
|
+
if (!failedByKey.has(key)) failedByKey.set(key, selected);
|
|
24664
|
+
} else if (!failedByKey.has(key) && !successByKey.has(key)) {
|
|
24665
|
+
successByKey.set(key, selected);
|
|
24666
|
+
}
|
|
24667
|
+
}
|
|
24668
|
+
return [...failedByKey.values(), ...successByKey.values()];
|
|
24669
|
+
};
|
|
24670
|
+
const formatTimestampForFilename = (ms2) => new Date(ms2).toISOString().replace(/[:.]/g, "-");
|
|
24671
|
+
const safeUidForPath = (uid) => uid.replace(/::/g, "--").replace(/\./g, "-");
|
|
24672
|
+
const decodeBrowserPayload = (encoded) => {
|
|
24673
|
+
const markers = [];
|
|
24674
|
+
if (!encoded) return { payload: null, truncationMarkers: markers };
|
|
24675
|
+
try {
|
|
24676
|
+
const raw = Buffer.from(encoded, "base64").toString("utf8");
|
|
24677
|
+
const rawBytes = Buffer.byteLength(raw, "utf8");
|
|
24678
|
+
if (rawBytes > CAPS.browserPayloadBytes) {
|
|
24679
|
+
markers.push({ kind: "browser-payload-truncated", originalSizeBytes: rawBytes });
|
|
24680
|
+
return { payload: null, truncationMarkers: markers };
|
|
24681
|
+
}
|
|
24682
|
+
const parsed = JSON.parse(raw);
|
|
24683
|
+
if (parsed && Array.isArray(parsed.consoleErrors) && parsed.consoleErrors.length > CAPS.consoleErrorsLines) {
|
|
24684
|
+
markers.push({ kind: "console-errors-truncated", originalCount: parsed.consoleErrors.length });
|
|
24685
|
+
parsed.consoleErrors = parsed.consoleErrors.slice(0, CAPS.consoleErrorsLines);
|
|
24686
|
+
}
|
|
24687
|
+
return { payload: parsed, truncationMarkers: markers };
|
|
24688
|
+
} catch {
|
|
24689
|
+
return { payload: null, truncationMarkers: markers };
|
|
24690
|
+
}
|
|
24691
|
+
};
|
|
24692
|
+
const deepScrubUrls = (value) => {
|
|
24693
|
+
if (value == null) return value;
|
|
24694
|
+
if (typeof value === "string") return scrubUrlSecrets(value);
|
|
24695
|
+
if (Array.isArray(value)) return value.map(deepScrubUrls);
|
|
24696
|
+
if (typeof value === "object") {
|
|
24697
|
+
const out = {};
|
|
24698
|
+
for (const [k2, v2] of Object.entries(value)) {
|
|
24699
|
+
out[k2] = deepScrubUrls(v2);
|
|
24700
|
+
}
|
|
24701
|
+
return out;
|
|
24702
|
+
}
|
|
24703
|
+
return value;
|
|
24704
|
+
};
|
|
24705
|
+
const sanitizePluginSettings = (settings) => {
|
|
24706
|
+
const cloned = JSON.parse(JSON.stringify(settings ?? {}));
|
|
24707
|
+
if (cloned.webhookConfig?.url) {
|
|
24708
|
+
cloned.webhookConfig.url = scrubUrlSecrets(cloned.webhookConfig.url);
|
|
24709
|
+
}
|
|
24710
|
+
return cloned;
|
|
24711
|
+
};
|
|
24712
|
+
const sanitizeSession = (session) => {
|
|
24713
|
+
const cloned = JSON.parse(JSON.stringify(session));
|
|
24714
|
+
for (const entry of cloned.entries ?? []) {
|
|
24715
|
+
if (entry.data !== void 0) {
|
|
24716
|
+
entry.data = deepScrubUrls(entry.data);
|
|
24717
|
+
}
|
|
24718
|
+
}
|
|
24719
|
+
return cloned;
|
|
24720
|
+
};
|
|
24721
|
+
const buildErrorLog = (session) => {
|
|
24722
|
+
const lines = [];
|
|
24723
|
+
for (const entry of session.entries ?? []) {
|
|
24724
|
+
const ts = Number.isFinite(entry.timestamp) ? new Date(entry.timestamp).toISOString() : "unknown";
|
|
24725
|
+
lines.push(`${ts} ${entry.message}`);
|
|
24726
|
+
}
|
|
24727
|
+
if (session.status === "failed" && session.summary) {
|
|
24728
|
+
lines.push(`ERROR: ${session.summary}`);
|
|
24729
|
+
}
|
|
24730
|
+
const full = lines.join("\n");
|
|
24731
|
+
const fullBytes = Buffer.byteLength(full, "utf8");
|
|
24732
|
+
if (fullBytes <= CAPS.errorLogBytes) {
|
|
24733
|
+
return { text: full, markers: [] };
|
|
24734
|
+
}
|
|
24735
|
+
const truncated = Buffer.from(full, "utf8").subarray(fullBytes - CAPS.errorLogBytes).toString("utf8");
|
|
24736
|
+
return {
|
|
24737
|
+
text: `...[truncated, kept last ${CAPS.errorLogBytes} bytes]
|
|
24738
|
+
${truncated}`,
|
|
24739
|
+
markers: [{ kind: "error-log-truncated", originalSizeBytes: fullBytes }]
|
|
24740
|
+
};
|
|
24741
|
+
};
|
|
24742
|
+
const readHostPackages = async (strapi2) => {
|
|
24743
|
+
const appRoot = strapi2?.dirs?.app?.root ?? process.cwd();
|
|
24744
|
+
if (!appRoot) return { data: null, reason: "host-app-root-unknown" };
|
|
24745
|
+
try {
|
|
24746
|
+
const raw = await require$$1$4.promises.readFile(require$$1__default.default.join(appRoot, "package.json"), "utf8");
|
|
24747
|
+
const parsed = JSON.parse(raw);
|
|
24748
|
+
return {
|
|
24749
|
+
data: {
|
|
24750
|
+
name: parsed.name,
|
|
24751
|
+
version: parsed.version,
|
|
24752
|
+
dependencies: parsed.dependencies ?? {},
|
|
24753
|
+
devDependencies: parsed.devDependencies ?? {}
|
|
24754
|
+
}
|
|
24755
|
+
};
|
|
24756
|
+
} catch (err) {
|
|
24757
|
+
return { data: null, reason: err instanceof Error ? err.message : String(err) };
|
|
24758
|
+
}
|
|
24759
|
+
};
|
|
24760
|
+
const buildReadme = ({
|
|
24761
|
+
sessionShortId,
|
|
24762
|
+
sessionId,
|
|
24763
|
+
schemaVersion,
|
|
24764
|
+
hostPackagesSkipped
|
|
24765
|
+
}) => {
|
|
24766
|
+
const lines = [];
|
|
24767
|
+
lines.push(`# Localazy Strapi Plugin — Troubleshooting Bundle`);
|
|
24768
|
+
lines.push("");
|
|
24769
|
+
lines.push(`Generated: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
24770
|
+
lines.push(`Session id: ${sessionId}`);
|
|
24771
|
+
lines.push(`Schema version: ${schemaVersion}`);
|
|
24772
|
+
lines.push("");
|
|
24773
|
+
lines.push("## Contents");
|
|
24774
|
+
lines.push("");
|
|
24775
|
+
lines.push("- `meta.json` — schema version, session metadata.");
|
|
24776
|
+
lines.push("- `versions.json` — plugin version, Strapi version, Node version, platform/arch.");
|
|
24777
|
+
lines.push("- `env.json` — allow-listed environment variables only.");
|
|
24778
|
+
lines.push("- `browser.json` — user agent, language, viewport, console errors (from the admin UI).");
|
|
24779
|
+
lines.push("- `session.json` — full activity-log session record (URLs scrubbed of secret query keys).");
|
|
24780
|
+
lines.push("- `error.log` — chronological text log of session entries.");
|
|
24781
|
+
lines.push("- `plugin-settings.json` — full plugin settings (webhook URL secrets scrubbed).");
|
|
24782
|
+
lines.push("- `content-transfer-setup.json` — content-transfer setup record.");
|
|
24783
|
+
if (hostPackagesSkipped) {
|
|
24784
|
+
lines.push("- `host-packages.json` — NOT INCLUDED (could not be read; see `bundle-truncated.json`).");
|
|
24785
|
+
} else {
|
|
24786
|
+
lines.push("- `host-packages.json` — host project's package.json (deps + devDeps only).");
|
|
24787
|
+
}
|
|
24788
|
+
lines.push("- `content-types/` — schema.json per touched content-type UID.");
|
|
24789
|
+
lines.push("- `entries/<uid>/<documentId>--<locale>.json` — per-entry payload from `strapi.documents().findOne()`.");
|
|
24790
|
+
lines.push("- `bundle-truncated.json` — present only if any per-section cap was exceeded.");
|
|
24791
|
+
lines.push("");
|
|
24792
|
+
lines.push("## Reproducing locally");
|
|
24793
|
+
lines.push("");
|
|
24794
|
+
lines.push("1. `unzip` the bundle.");
|
|
24795
|
+
lines.push("2. Recreate the content types from `content-types/*.schema.json` in your Strapi project.");
|
|
24796
|
+
lines.push("3. Import the entries via the Document Service using `entries/<uid>/*.json`.");
|
|
24797
|
+
lines.push("4. Apply `plugin-settings.json` and `content-transfer-setup.json` via the plugin admin UI.");
|
|
24798
|
+
lines.push("5. Trigger the same upload/download/webhook with the same locale set.");
|
|
24799
|
+
lines.push("6. Compare the failure against `error.log` and `session.json`.");
|
|
24800
|
+
lines.push("");
|
|
24801
|
+
lines.push(`Bundle short id: ${sessionShortId}`);
|
|
24802
|
+
return lines.join("\n");
|
|
24803
|
+
};
|
|
24804
|
+
const buildDebugBundle = async ({
|
|
24805
|
+
strapi: strapi2,
|
|
24806
|
+
sessionId,
|
|
24807
|
+
browserPayloadEncoded
|
|
24808
|
+
}) => {
|
|
24809
|
+
const zip = new JSZip__default.default();
|
|
24810
|
+
const truncationMarkers = [];
|
|
24811
|
+
const pluginName = "localazy";
|
|
24812
|
+
const activityLogsService = strapi2.plugin(pluginName).service("ActivityLogsService");
|
|
24813
|
+
const pluginSettingsService = strapi2.plugin(pluginName).service("PluginSettingsService");
|
|
24814
|
+
const strapiService = strapi2.plugin(pluginName).service("StrapiService");
|
|
24815
|
+
const localazyUserService = strapi2.plugin(pluginName).service("LocalazyUserService");
|
|
24816
|
+
const session = await activityLogsService.getSession(sessionId);
|
|
24817
|
+
if (!session) {
|
|
24818
|
+
throw new SessionNotFoundError(sessionId);
|
|
24819
|
+
}
|
|
24820
|
+
const sessionShortId = session.id.slice(0, 8);
|
|
24821
|
+
const pluginSettingsRaw = await pluginSettingsService.getPluginSettings();
|
|
24822
|
+
const contentTransferSetup = await pluginSettingsService.getContentTransferSetup();
|
|
24823
|
+
const identity2 = await localazyUserService.getUser();
|
|
24824
|
+
const redactedIdentity = redactIdentity(identity2);
|
|
24825
|
+
const pluginVersion = await strapiService.getPluginVersion();
|
|
24826
|
+
const strapiVersion = strapiService.getStrapiVersion();
|
|
24827
|
+
zip.file(
|
|
24828
|
+
"meta.json",
|
|
24829
|
+
JSON.stringify(
|
|
24830
|
+
{
|
|
24831
|
+
schemaVersion: DEBUG_BUNDLE_SCHEMA_VERSION,
|
|
24832
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24833
|
+
sessionId: session.id,
|
|
24834
|
+
sessionShortId,
|
|
24835
|
+
eventType: session.eventType,
|
|
24836
|
+
status: session.status,
|
|
24837
|
+
startedAt: session.startedAt,
|
|
24838
|
+
finishedAt: session.finishedAt ?? null,
|
|
24839
|
+
initiatedBy: session.initiatedBy,
|
|
24840
|
+
identity: redactedIdentity
|
|
24841
|
+
},
|
|
24842
|
+
null,
|
|
24843
|
+
2
|
|
24844
|
+
)
|
|
24845
|
+
);
|
|
24846
|
+
zip.file(
|
|
24847
|
+
"versions.json",
|
|
24848
|
+
JSON.stringify(
|
|
24849
|
+
{
|
|
24850
|
+
plugin: { name: "@localazy/strapi-plugin", version: pluginVersion },
|
|
24851
|
+
strapi: { version: strapiVersion },
|
|
24852
|
+
node: process.version,
|
|
24853
|
+
platform: process.platform,
|
|
24854
|
+
arch: process.arch
|
|
24855
|
+
},
|
|
24856
|
+
null,
|
|
24857
|
+
2
|
|
24858
|
+
)
|
|
24859
|
+
);
|
|
24860
|
+
const envSnapshot = pickEnvAllowlist(process.env);
|
|
24861
|
+
zip.file(
|
|
24862
|
+
"env.json",
|
|
24863
|
+
JSON.stringify(
|
|
24864
|
+
{
|
|
24865
|
+
allowlist: [...DEBUG_BUNDLE_ENV_ALLOWLIST],
|
|
24866
|
+
values: envSnapshot
|
|
24867
|
+
},
|
|
24868
|
+
null,
|
|
24869
|
+
2
|
|
24870
|
+
)
|
|
24871
|
+
);
|
|
24872
|
+
const { payload: browserPayload, truncationMarkers: browserMarkers } = decodeBrowserPayload(browserPayloadEncoded);
|
|
24873
|
+
truncationMarkers.push(...browserMarkers);
|
|
24874
|
+
zip.file("browser.json", JSON.stringify(browserPayload ? browserPayload : { captured: false }, null, 2));
|
|
24875
|
+
zip.file("session.json", JSON.stringify(sanitizeSession(session), null, 2));
|
|
24876
|
+
const { text: errorLogText, markers: errorLogMarkers } = buildErrorLog(session);
|
|
24877
|
+
truncationMarkers.push(...errorLogMarkers);
|
|
24878
|
+
zip.file("error.log", errorLogText);
|
|
24879
|
+
zip.file("plugin-settings.json", JSON.stringify(sanitizePluginSettings(pluginSettingsRaw), null, 2));
|
|
24880
|
+
zip.file("content-transfer-setup.json", JSON.stringify(contentTransferSetup, null, 2));
|
|
24881
|
+
const hostPackages = await readHostPackages(strapi2);
|
|
24882
|
+
if (hostPackages.data) {
|
|
24883
|
+
zip.file("host-packages.json", JSON.stringify(hostPackages.data, null, 2));
|
|
24884
|
+
}
|
|
24885
|
+
if (session.attemptedEntries === void 0) {
|
|
24886
|
+
truncationMarkers.push({ kind: "attempted-entries-missing-legacy-session" });
|
|
24887
|
+
}
|
|
24888
|
+
const selected = selectAttemptedEntries(session.attemptedEntries ?? []);
|
|
24889
|
+
const kept = selected.slice(0, CAPS.totalEntries);
|
|
24890
|
+
const skipped = selected.slice(CAPS.totalEntries);
|
|
24891
|
+
if (skipped.length > 0) {
|
|
24892
|
+
truncationMarkers.push({
|
|
24893
|
+
kind: "entries-skipped",
|
|
24894
|
+
tuples: skipped.map(({ uid, documentId, locale }) => ({ uid, documentId, locale }))
|
|
24895
|
+
});
|
|
24896
|
+
}
|
|
24897
|
+
const uniqueUids = [...new Set(kept.map((t3) => t3.uid))];
|
|
24898
|
+
for (const uid of uniqueUids) {
|
|
24899
|
+
const schema = serializeContentTypeSchema(strapi2.contentTypes?.[uid]);
|
|
24900
|
+
if (schema) {
|
|
24901
|
+
zip.file(`content-types/${safeUidForPath(uid)}.schema.json`, JSON.stringify(schema, null, 2));
|
|
24902
|
+
}
|
|
24903
|
+
}
|
|
24904
|
+
for (const tuple of kept) {
|
|
24905
|
+
let documentJson = null;
|
|
24906
|
+
try {
|
|
24907
|
+
const doc = await strapi2.documents(tuple.uid).findOne({ documentId: tuple.documentId, locale: tuple.locale, populate: "*" });
|
|
24908
|
+
documentJson = doc ? stripDocumentInternals(doc) : { __not_found: true, attemptedAt: tuple.attemptedAt };
|
|
24909
|
+
} catch (err) {
|
|
24910
|
+
documentJson = { __error: err instanceof Error ? err.message : String(err) };
|
|
24911
|
+
}
|
|
24912
|
+
const serialised = JSON.stringify(documentJson, null, 2);
|
|
24913
|
+
const sizeBytes = Buffer.byteLength(serialised, "utf8");
|
|
24914
|
+
let body;
|
|
24915
|
+
if (sizeBytes > CAPS.perEntryBytes) {
|
|
24916
|
+
truncationMarkers.push({
|
|
24917
|
+
kind: "entry-too-large",
|
|
24918
|
+
uid: tuple.uid,
|
|
24919
|
+
documentId: tuple.documentId,
|
|
24920
|
+
locale: tuple.locale,
|
|
24921
|
+
originalSizeBytes: sizeBytes
|
|
24922
|
+
});
|
|
24923
|
+
body = JSON.stringify({ __truncated: true, originalSizeBytes: sizeBytes }, null, 2);
|
|
24924
|
+
} else {
|
|
24925
|
+
body = serialised;
|
|
24926
|
+
}
|
|
24927
|
+
const safeDocId = tuple.documentId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
24928
|
+
zip.file(`entries/${safeUidForPath(tuple.uid)}/${safeDocId}--${tuple.locale}.json`, body);
|
|
24929
|
+
}
|
|
24930
|
+
zip.file(
|
|
24931
|
+
"README.md",
|
|
24932
|
+
buildReadme({
|
|
24933
|
+
sessionShortId,
|
|
24934
|
+
sessionId: session.id,
|
|
24935
|
+
schemaVersion: DEBUG_BUNDLE_SCHEMA_VERSION,
|
|
24936
|
+
hostPackagesSkipped: !hostPackages.data
|
|
24937
|
+
})
|
|
24938
|
+
);
|
|
24939
|
+
if (truncationMarkers.length > 0 || !hostPackages.data) {
|
|
24940
|
+
const marker = {
|
|
24941
|
+
schemaVersion: DEBUG_BUNDLE_SCHEMA_VERSION,
|
|
24942
|
+
markers: truncationMarkers,
|
|
24943
|
+
hostPackagesSkipped: hostPackages.data ? null : hostPackages.reason ?? "unknown"
|
|
24944
|
+
};
|
|
24945
|
+
zip.file("bundle-truncated.json", JSON.stringify(marker, null, 2));
|
|
24946
|
+
}
|
|
24947
|
+
const zipBuffer = await zip.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
|
|
24948
|
+
const filename = `localazy-debug-bundle-${sessionShortId}-${formatTimestampForFilename(Date.now())}.zip`;
|
|
24949
|
+
return { zipBuffer, filename };
|
|
24950
|
+
};
|
|
24951
|
+
const DebugBundleController = ({ strapi: strapi2 }) => ({
|
|
24952
|
+
async getBundle(ctx) {
|
|
24953
|
+
const { sessionId } = ctx.params;
|
|
24954
|
+
const browserPayloadEncoded = typeof ctx.query.browser === "string" ? ctx.query.browser : null;
|
|
24955
|
+
try {
|
|
24956
|
+
const { zipBuffer, filename } = await buildDebugBundle({
|
|
24957
|
+
strapi: strapi2,
|
|
24958
|
+
sessionId,
|
|
24959
|
+
browserPayloadEncoded
|
|
24960
|
+
});
|
|
24961
|
+
ctx.type = "application/zip";
|
|
24962
|
+
ctx.set("Content-Disposition", `attachment; filename="${filename}"`);
|
|
24963
|
+
ctx.body = zipBuffer;
|
|
24964
|
+
} catch (err) {
|
|
24965
|
+
if (err instanceof SessionNotFoundError) {
|
|
24966
|
+
return ctx.notFound("Session not found");
|
|
24967
|
+
}
|
|
24968
|
+
strapi2.log.error(`[localazy] failed to build debug bundle: ${err instanceof Error ? err.message : err}`);
|
|
24969
|
+
throw err;
|
|
24970
|
+
}
|
|
24971
|
+
}
|
|
24972
|
+
});
|
|
24504
24973
|
const controllers = {
|
|
24505
24974
|
LocalazyUserController,
|
|
24506
24975
|
LocalazyAuthController,
|
|
@@ -24509,7 +24978,8 @@ const controllers = {
|
|
|
24509
24978
|
PluginSettingsController,
|
|
24510
24979
|
LocalazyTransferController,
|
|
24511
24980
|
EntryExclusionController,
|
|
24512
|
-
ActivityLogsController
|
|
24981
|
+
ActivityLogsController,
|
|
24982
|
+
DebugBundleController
|
|
24513
24983
|
};
|
|
24514
24984
|
const verifyWebhook = (config2, { strapi: strapi2 }) => {
|
|
24515
24985
|
return async (ctx, next) => {
|
|
@@ -24567,13 +25037,17 @@ const middlewares = {
|
|
|
24567
25037
|
};
|
|
24568
25038
|
const policies = {};
|
|
24569
25039
|
const ROUTE_PREFIX$8 = "/user";
|
|
25040
|
+
const readPolicy$6 = [{ name: "admin::hasPermissions", config: { actions: [PERMISSION_UIDS.READ] } }];
|
|
25041
|
+
const settingsUpdatePolicy$4 = [
|
|
25042
|
+
{ name: "admin::hasPermissions", config: { actions: [PERMISSION_UIDS.SETTINGS_UPDATE] } }
|
|
25043
|
+
];
|
|
24570
25044
|
const LocalazyUserRoutes = [
|
|
24571
25045
|
{
|
|
24572
25046
|
method: "GET",
|
|
24573
25047
|
path: `${ROUTE_PREFIX$8}`,
|
|
24574
25048
|
handler: "LocalazyUserController.getUser",
|
|
24575
25049
|
config: {
|
|
24576
|
-
policies:
|
|
25050
|
+
policies: readPolicy$6
|
|
24577
25051
|
}
|
|
24578
25052
|
},
|
|
24579
25053
|
{
|
|
@@ -24581,7 +25055,7 @@ const LocalazyUserRoutes = [
|
|
|
24581
25055
|
path: `${ROUTE_PREFIX$8}`,
|
|
24582
25056
|
handler: "LocalazyUserController.updateUser",
|
|
24583
25057
|
config: {
|
|
24584
|
-
policies:
|
|
25058
|
+
policies: settingsUpdatePolicy$4
|
|
24585
25059
|
}
|
|
24586
25060
|
},
|
|
24587
25061
|
{
|
|
@@ -24589,18 +25063,21 @@ const LocalazyUserRoutes = [
|
|
|
24589
25063
|
path: `${ROUTE_PREFIX$8}`,
|
|
24590
25064
|
handler: "LocalazyUserController.deleteUser",
|
|
24591
25065
|
config: {
|
|
24592
|
-
policies:
|
|
25066
|
+
policies: settingsUpdatePolicy$4
|
|
24593
25067
|
}
|
|
24594
25068
|
}
|
|
24595
25069
|
];
|
|
24596
25070
|
const ROUTE_PREFIX$7 = "/auth";
|
|
25071
|
+
const settingsUpdatePolicy$3 = [
|
|
25072
|
+
{ name: "admin::hasPermissions", config: { actions: [PERMISSION_UIDS.SETTINGS_UPDATE] } }
|
|
25073
|
+
];
|
|
24597
25074
|
const LocalazyAuthRoutes = [
|
|
24598
25075
|
{
|
|
24599
25076
|
method: "GET",
|
|
24600
25077
|
path: `${ROUTE_PREFIX$7}/generate-keys`,
|
|
24601
25078
|
handler: "LocalazyAuthController.generateKeys",
|
|
24602
25079
|
config: {
|
|
24603
|
-
policies:
|
|
25080
|
+
policies: settingsUpdatePolicy$3
|
|
24604
25081
|
}
|
|
24605
25082
|
},
|
|
24606
25083
|
{
|
|
@@ -24608,18 +25085,26 @@ const LocalazyAuthRoutes = [
|
|
|
24608
25085
|
path: `${ROUTE_PREFIX$7}/continuous-poll`,
|
|
24609
25086
|
handler: "LocalazyAuthController.continuousPoll",
|
|
24610
25087
|
config: {
|
|
24611
|
-
policies:
|
|
25088
|
+
policies: settingsUpdatePolicy$3
|
|
24612
25089
|
}
|
|
24613
25090
|
}
|
|
24614
25091
|
];
|
|
24615
25092
|
const ROUTE_PREFIX$6 = "/plugin-settings";
|
|
25093
|
+
const readPolicy$5 = [{ name: "admin::hasPermissions", config: { actions: [PERMISSION_UIDS.READ] } }];
|
|
25094
|
+
const settingsUpdatePolicy$2 = [
|
|
25095
|
+
{ name: "admin::hasPermissions", config: { actions: [PERMISSION_UIDS.SETTINGS_UPDATE] } }
|
|
25096
|
+
];
|
|
24616
25097
|
const PluginSettingsRoutes = [
|
|
24617
25098
|
{
|
|
24618
25099
|
method: "GET",
|
|
24619
25100
|
path: `${ROUTE_PREFIX$6}/content-transfer-setup`,
|
|
24620
25101
|
handler: "PluginSettingsController.getContentTransferSetup",
|
|
24621
25102
|
config: {
|
|
24622
|
-
|
|
25103
|
+
// Upload/Download pages call this to detect "model changed" before
|
|
25104
|
+
// transferring. Gating on `settings.read` would lock transfer-only
|
|
25105
|
+
// roles out of the transfer pages, so the read is open to any plugin
|
|
25106
|
+
// user. The destructive PUT below stays on `settings.update`.
|
|
25107
|
+
policies: readPolicy$5
|
|
24623
25108
|
}
|
|
24624
25109
|
},
|
|
24625
25110
|
{
|
|
@@ -24627,7 +25112,7 @@ const PluginSettingsRoutes = [
|
|
|
24627
25112
|
path: `${ROUTE_PREFIX$6}/content-transfer-setup`,
|
|
24628
25113
|
handler: "PluginSettingsController.updateContentTransferSetup",
|
|
24629
25114
|
config: {
|
|
24630
|
-
policies:
|
|
25115
|
+
policies: settingsUpdatePolicy$2
|
|
24631
25116
|
}
|
|
24632
25117
|
},
|
|
24633
25118
|
{
|
|
@@ -24635,7 +25120,7 @@ const PluginSettingsRoutes = [
|
|
|
24635
25120
|
path: `${ROUTE_PREFIX$6}/plugin-settings`,
|
|
24636
25121
|
handler: "PluginSettingsController.getPluginSettings",
|
|
24637
25122
|
config: {
|
|
24638
|
-
policies:
|
|
25123
|
+
policies: readPolicy$5
|
|
24639
25124
|
}
|
|
24640
25125
|
},
|
|
24641
25126
|
{
|
|
@@ -24643,7 +25128,20 @@ const PluginSettingsRoutes = [
|
|
|
24643
25128
|
path: `${ROUTE_PREFIX$6}/plugin-settings`,
|
|
24644
25129
|
handler: "PluginSettingsController.updatePluginSettings",
|
|
24645
25130
|
config: {
|
|
24646
|
-
|
|
25131
|
+
// Persists destructive Global Settings (webhookConfig, download.*, upload.*).
|
|
25132
|
+
// UI-pref-only writes use `PUT /plugin-settings/ui-prefs` (read-gated) below.
|
|
25133
|
+
policies: settingsUpdatePolicy$2
|
|
25134
|
+
}
|
|
25135
|
+
},
|
|
25136
|
+
{
|
|
25137
|
+
method: "PUT",
|
|
25138
|
+
path: `${ROUTE_PREFIX$6}/ui-prefs`,
|
|
25139
|
+
handler: "PluginSettingsController.updatePluginSettingsUiPrefs",
|
|
25140
|
+
config: {
|
|
25141
|
+
// Per-user UI state (last visited route, sort prefs). Service-side filters
|
|
25142
|
+
// the body to a fixed allowlist so a `read`-only caller cannot reach the
|
|
25143
|
+
// destructive fields that share the same plugin-settings store.
|
|
25144
|
+
policies: readPolicy$5
|
|
24647
25145
|
}
|
|
24648
25146
|
},
|
|
24649
25147
|
{
|
|
@@ -24651,18 +25149,22 @@ const PluginSettingsRoutes = [
|
|
|
24651
25149
|
path: `${ROUTE_PREFIX$6}/sync-cursor`,
|
|
24652
25150
|
handler: "PluginSettingsController.getSyncCursor",
|
|
24653
25151
|
config: {
|
|
24654
|
-
policies:
|
|
25152
|
+
policies: readPolicy$5
|
|
24655
25153
|
}
|
|
24656
25154
|
}
|
|
24657
25155
|
];
|
|
24658
25156
|
const ROUTE_PREFIX$5 = "/strapi";
|
|
25157
|
+
const readPolicy$4 = [{ name: "admin::hasPermissions", config: { actions: [PERMISSION_UIDS.READ] } }];
|
|
25158
|
+
const settingsUpdatePolicy$1 = [
|
|
25159
|
+
{ name: "admin::hasPermissions", config: { actions: [PERMISSION_UIDS.SETTINGS_UPDATE] } }
|
|
25160
|
+
];
|
|
24659
25161
|
const StrapiRoutes = [
|
|
24660
25162
|
{
|
|
24661
25163
|
method: "GET",
|
|
24662
25164
|
path: `${ROUTE_PREFIX$5}/models`,
|
|
24663
25165
|
handler: "StrapiController.getModels",
|
|
24664
25166
|
config: {
|
|
24665
|
-
policies:
|
|
25167
|
+
policies: readPolicy$4
|
|
24666
25168
|
}
|
|
24667
25169
|
},
|
|
24668
25170
|
{
|
|
@@ -24670,7 +25172,7 @@ const StrapiRoutes = [
|
|
|
24670
25172
|
path: `${ROUTE_PREFIX$5}/localizable-models`,
|
|
24671
25173
|
handler: "StrapiController.getLocalizableModels",
|
|
24672
25174
|
config: {
|
|
24673
|
-
policies:
|
|
25175
|
+
policies: readPolicy$4
|
|
24674
25176
|
}
|
|
24675
25177
|
},
|
|
24676
25178
|
{
|
|
@@ -24678,7 +25180,7 @@ const StrapiRoutes = [
|
|
|
24678
25180
|
path: `${ROUTE_PREFIX$5}/lifecycle/localazy-webhooks`,
|
|
24679
25181
|
handler: "StrapiController.postLifecycleLocalazyWebhooks",
|
|
24680
25182
|
config: {
|
|
24681
|
-
policies:
|
|
25183
|
+
policies: settingsUpdatePolicy$1
|
|
24682
25184
|
}
|
|
24683
25185
|
},
|
|
24684
25186
|
{
|
|
@@ -24686,18 +25188,19 @@ const StrapiRoutes = [
|
|
|
24686
25188
|
path: `${ROUTE_PREFIX$5}/version`,
|
|
24687
25189
|
handler: "StrapiController.getPluginVersion",
|
|
24688
25190
|
config: {
|
|
24689
|
-
policies:
|
|
25191
|
+
policies: readPolicy$4
|
|
24690
25192
|
}
|
|
24691
25193
|
}
|
|
24692
25194
|
];
|
|
24693
25195
|
const ROUTE_PREFIX$4 = "/transfer";
|
|
25196
|
+
const transferPolicy$2 = [{ name: "admin::hasPermissions", config: { actions: [PERMISSION_UIDS.TRANSFER] } }];
|
|
24694
25197
|
const LocalazyTransferRoutes = [
|
|
24695
25198
|
{
|
|
24696
25199
|
method: "POST",
|
|
24697
25200
|
path: `${ROUTE_PREFIX$4}/upload`,
|
|
24698
25201
|
handler: "LocalazyTransferController.upload",
|
|
24699
25202
|
config: {
|
|
24700
|
-
policies:
|
|
25203
|
+
policies: transferPolicy$2
|
|
24701
25204
|
}
|
|
24702
25205
|
},
|
|
24703
25206
|
{
|
|
@@ -24705,18 +25208,22 @@ const LocalazyTransferRoutes = [
|
|
|
24705
25208
|
path: `${ROUTE_PREFIX$4}/download`,
|
|
24706
25209
|
handler: "LocalazyTransferController.download",
|
|
24707
25210
|
config: {
|
|
24708
|
-
policies:
|
|
25211
|
+
policies: transferPolicy$2
|
|
24709
25212
|
}
|
|
24710
25213
|
}
|
|
24711
25214
|
];
|
|
24712
25215
|
const ROUTE_PREFIX$3 = "/project";
|
|
25216
|
+
const readPolicy$3 = [{ name: "admin::hasPermissions", config: { actions: [PERMISSION_UIDS.READ] } }];
|
|
25217
|
+
const settingsUpdatePolicy = [
|
|
25218
|
+
{ name: "admin::hasPermissions", config: { actions: [PERMISSION_UIDS.SETTINGS_UPDATE] } }
|
|
25219
|
+
];
|
|
24713
25220
|
const LocalazyProjectRoutes = [
|
|
24714
25221
|
{
|
|
24715
25222
|
method: "GET",
|
|
24716
25223
|
path: `${ROUTE_PREFIX$3}`,
|
|
24717
25224
|
handler: "LocalazyProjectController.getConnectedProject",
|
|
24718
25225
|
config: {
|
|
24719
|
-
policies:
|
|
25226
|
+
policies: readPolicy$3
|
|
24720
25227
|
}
|
|
24721
25228
|
},
|
|
24722
25229
|
{
|
|
@@ -24724,7 +25231,7 @@ const LocalazyProjectRoutes = [
|
|
|
24724
25231
|
path: `${ROUTE_PREFIX$3}/webhooks`,
|
|
24725
25232
|
handler: "LocalazyProjectController.getWebhookStatus",
|
|
24726
25233
|
config: {
|
|
24727
|
-
policies:
|
|
25234
|
+
policies: readPolicy$3
|
|
24728
25235
|
}
|
|
24729
25236
|
},
|
|
24730
25237
|
{
|
|
@@ -24732,7 +25239,7 @@ const LocalazyProjectRoutes = [
|
|
|
24732
25239
|
path: `${ROUTE_PREFIX$3}/webhooks/setup`,
|
|
24733
25240
|
handler: "LocalazyProjectController.setupWebhook",
|
|
24734
25241
|
config: {
|
|
24735
|
-
policies:
|
|
25242
|
+
policies: settingsUpdatePolicy
|
|
24736
25243
|
}
|
|
24737
25244
|
},
|
|
24738
25245
|
{
|
|
@@ -24740,7 +25247,7 @@ const LocalazyProjectRoutes = [
|
|
|
24740
25247
|
path: `${ROUTE_PREFIX$3}/strapi-url`,
|
|
24741
25248
|
handler: "LocalazyProjectController.getStrapiUrl",
|
|
24742
25249
|
config: {
|
|
24743
|
-
policies:
|
|
25250
|
+
policies: readPolicy$3
|
|
24744
25251
|
}
|
|
24745
25252
|
}
|
|
24746
25253
|
];
|
|
@@ -24759,13 +25266,15 @@ const LocalazyPublicTransferRoutes = [
|
|
|
24759
25266
|
}
|
|
24760
25267
|
];
|
|
24761
25268
|
const ROUTE_PREFIX$1 = "/entry-exclusion";
|
|
25269
|
+
const readPolicy$2 = [{ name: "admin::hasPermissions", config: { actions: [PERMISSION_UIDS.READ] } }];
|
|
25270
|
+
const transferPolicy$1 = [{ name: "admin::hasPermissions", config: { actions: [PERMISSION_UIDS.TRANSFER] } }];
|
|
24762
25271
|
const EntryExclusionRoutes = [
|
|
24763
25272
|
{
|
|
24764
25273
|
method: "GET",
|
|
24765
25274
|
path: `${ROUTE_PREFIX$1}/:contentType/:documentId`,
|
|
24766
25275
|
handler: "EntryExclusionController.getEntryExclusion",
|
|
24767
25276
|
config: {
|
|
24768
|
-
policies:
|
|
25277
|
+
policies: readPolicy$2
|
|
24769
25278
|
}
|
|
24770
25279
|
},
|
|
24771
25280
|
{
|
|
@@ -24773,7 +25282,7 @@ const EntryExclusionRoutes = [
|
|
|
24773
25282
|
path: `${ROUTE_PREFIX$1}/:contentType/:documentId`,
|
|
24774
25283
|
handler: "EntryExclusionController.setEntryExclusion",
|
|
24775
25284
|
config: {
|
|
24776
|
-
policies:
|
|
25285
|
+
policies: transferPolicy$1
|
|
24777
25286
|
}
|
|
24778
25287
|
},
|
|
24779
25288
|
{
|
|
@@ -24781,18 +25290,20 @@ const EntryExclusionRoutes = [
|
|
|
24781
25290
|
path: `${ROUTE_PREFIX$1}/:contentType`,
|
|
24782
25291
|
handler: "EntryExclusionController.getContentTypeExclusions",
|
|
24783
25292
|
config: {
|
|
24784
|
-
policies:
|
|
25293
|
+
policies: readPolicy$2
|
|
24785
25294
|
}
|
|
24786
25295
|
}
|
|
24787
25296
|
];
|
|
24788
25297
|
const ROUTE_PREFIX = "/activity-logs";
|
|
25298
|
+
const readPolicy$1 = [{ name: "admin::hasPermissions", config: { actions: [PERMISSION_UIDS.READ] } }];
|
|
25299
|
+
const transferPolicy = [{ name: "admin::hasPermissions", config: { actions: [PERMISSION_UIDS.TRANSFER] } }];
|
|
24789
25300
|
const ActivityLogsRoutes = [
|
|
24790
25301
|
{
|
|
24791
25302
|
method: "GET",
|
|
24792
25303
|
path: `${ROUTE_PREFIX}`,
|
|
24793
25304
|
handler: "ActivityLogsController.getSessions",
|
|
24794
25305
|
config: {
|
|
24795
|
-
policies:
|
|
25306
|
+
policies: readPolicy$1
|
|
24796
25307
|
}
|
|
24797
25308
|
},
|
|
24798
25309
|
{
|
|
@@ -24800,7 +25311,7 @@ const ActivityLogsRoutes = [
|
|
|
24800
25311
|
path: `${ROUTE_PREFIX}/export`,
|
|
24801
25312
|
handler: "ActivityLogsController.exportSessions",
|
|
24802
25313
|
config: {
|
|
24803
|
-
policies:
|
|
25314
|
+
policies: readPolicy$1
|
|
24804
25315
|
}
|
|
24805
25316
|
},
|
|
24806
25317
|
{
|
|
@@ -24808,7 +25319,7 @@ const ActivityLogsRoutes = [
|
|
|
24808
25319
|
path: `${ROUTE_PREFIX}/:sessionId`,
|
|
24809
25320
|
handler: "ActivityLogsController.getSession",
|
|
24810
25321
|
config: {
|
|
24811
|
-
policies:
|
|
25322
|
+
policies: readPolicy$1
|
|
24812
25323
|
}
|
|
24813
25324
|
},
|
|
24814
25325
|
{
|
|
@@ -24816,13 +25327,28 @@ const ActivityLogsRoutes = [
|
|
|
24816
25327
|
path: `${ROUTE_PREFIX}`,
|
|
24817
25328
|
handler: "ActivityLogsController.clearSessions",
|
|
24818
25329
|
config: {
|
|
24819
|
-
policies:
|
|
25330
|
+
policies: transferPolicy
|
|
24820
25331
|
}
|
|
24821
25332
|
}
|
|
24822
25333
|
];
|
|
25334
|
+
const readPolicy = [{ name: "admin::hasPermissions", config: { actions: [PERMISSION_UIDS.READ] } }];
|
|
25335
|
+
const DebugBundleRoutes = [
|
|
25336
|
+
{
|
|
25337
|
+
method: "GET",
|
|
25338
|
+
path: "/activity-logs/:sessionId/bundle",
|
|
25339
|
+
handler: "DebugBundleController.getBundle",
|
|
25340
|
+
config: {
|
|
25341
|
+
policies: readPolicy
|
|
25342
|
+
}
|
|
25343
|
+
}
|
|
25344
|
+
];
|
|
25345
|
+
const withoutAdminPolicies = (routes2) => routes2.map((route) => ({
|
|
25346
|
+
...route,
|
|
25347
|
+
config: { ...route.config, policies: [] }
|
|
25348
|
+
}));
|
|
24823
25349
|
const routes = {
|
|
24824
25350
|
"content-api": {
|
|
24825
|
-
routes:
|
|
25351
|
+
routes: withoutAdminPolicies(StrapiRoutes)
|
|
24826
25352
|
},
|
|
24827
25353
|
admin: {
|
|
24828
25354
|
type: "admin",
|
|
@@ -24834,7 +25360,8 @@ const routes = {
|
|
|
24834
25360
|
...LocalazyTransferRoutes,
|
|
24835
25361
|
...LocalazyProjectRoutes,
|
|
24836
25362
|
...EntryExclusionRoutes,
|
|
24837
|
-
...ActivityLogsRoutes
|
|
25363
|
+
...ActivityLogsRoutes,
|
|
25364
|
+
...DebugBundleRoutes
|
|
24838
25365
|
]
|
|
24839
25366
|
},
|
|
24840
25367
|
LocalazyPublicTransferRoutes: {
|
|
@@ -25033,6 +25560,7 @@ const KEY$1 = "sync-cursor";
|
|
|
25033
25560
|
const emptySyncCursor = {
|
|
25034
25561
|
processedKeys: {}
|
|
25035
25562
|
};
|
|
25563
|
+
const UI_PREF_FIELDS = ["defaultRoute", "activityLogsSort"];
|
|
25036
25564
|
const PluginSettingsService = ({ strapi: strapi2 }) => ({
|
|
25037
25565
|
async getContentTransferSetup() {
|
|
25038
25566
|
const pluginStore = getStrapiStore(strapi2);
|
|
@@ -25069,6 +25597,20 @@ const PluginSettingsService = ({ strapi: strapi2 }) => ({
|
|
|
25069
25597
|
});
|
|
25070
25598
|
return newSettings;
|
|
25071
25599
|
},
|
|
25600
|
+
// Counterpart to `updatePluginSettings` for the `read`-gated UI-prefs route.
|
|
25601
|
+
// Drops any field outside `UI_PREF_FIELDS` server-side so a `read`-only caller
|
|
25602
|
+
// cannot reach `webhookConfig` / `download.*` / `upload.*` via the merge.
|
|
25603
|
+
async updatePluginSettingsUiPrefs(prefs) {
|
|
25604
|
+
const sanitized = {};
|
|
25605
|
+
if (prefs && typeof prefs === "object") {
|
|
25606
|
+
for (const field of UI_PREF_FIELDS) {
|
|
25607
|
+
if (field in prefs) {
|
|
25608
|
+
sanitized[field] = prefs[field];
|
|
25609
|
+
}
|
|
25610
|
+
}
|
|
25611
|
+
}
|
|
25612
|
+
return this.updatePluginSettings(sanitized);
|
|
25613
|
+
},
|
|
25072
25614
|
async getSyncCursor() {
|
|
25073
25615
|
const pluginStore = getStrapiStore(strapi2);
|
|
25074
25616
|
const cursor = await pluginStore.get({ key: KEY$1 });
|
|
@@ -25113,6 +25655,8 @@ const LocalazyTransferUploadService = ({ strapi: strapi2 }) => ({
|
|
|
25113
25655
|
const { setup: setup2 } = contentTransferSetup;
|
|
25114
25656
|
const collectionsNames = getCollectionsNames(setup2);
|
|
25115
25657
|
const models = await StrapiService2.getModels();
|
|
25658
|
+
const strapiDefaultLocaleCode = await StrapiI18nService2.getDefaultLocaleCode();
|
|
25659
|
+
const attemptedTuples = [];
|
|
25116
25660
|
let pickedFlattenStrapiContent = {};
|
|
25117
25661
|
for (const collectionName of collectionsNames) {
|
|
25118
25662
|
const currentModel = models.find((model) => model.collectionName === collectionName);
|
|
@@ -25175,6 +25719,15 @@ const LocalazyTransferUploadService = ({ strapi: strapi2 }) => ({
|
|
|
25175
25719
|
});
|
|
25176
25720
|
await activityLogsService.addEntry(sessionId, message);
|
|
25177
25721
|
strapi2.log.info(message);
|
|
25722
|
+
if (strapiDefaultLocaleCode) {
|
|
25723
|
+
for (const entry of entries) {
|
|
25724
|
+
attemptedTuples.push({
|
|
25725
|
+
uid: modelUid,
|
|
25726
|
+
documentId: entry.documentId,
|
|
25727
|
+
locale: strapiDefaultLocaleCode
|
|
25728
|
+
});
|
|
25729
|
+
}
|
|
25730
|
+
}
|
|
25178
25731
|
entries = omitDeep(entries, ["locale", "createdAt", "updatedAt", "publishedAt"]);
|
|
25179
25732
|
entries.forEach((entry) => {
|
|
25180
25733
|
const flatten = flattenObject({
|
|
@@ -25192,7 +25745,6 @@ const LocalazyTransferUploadService = ({ strapi: strapi2 }) => ({
|
|
|
25192
25745
|
...characterLimitsMetadata
|
|
25193
25746
|
};
|
|
25194
25747
|
}
|
|
25195
|
-
const strapiDefaultLocaleCode = await StrapiI18nService2.getDefaultLocaleCode();
|
|
25196
25748
|
const localazyLocaleCode = strapiDefaultLocaleCode ? isoStrapiToLocalazy(strapiDefaultLocaleCode) : config.default.LOCALAZY_DEFAULT_LOCALE;
|
|
25197
25749
|
const importFile = LocalazyUploadService2.createImportFileRepresentation(
|
|
25198
25750
|
localazyLocaleCode,
|
|
@@ -25214,6 +25766,10 @@ const LocalazyTransferUploadService = ({ strapi: strapi2 }) => ({
|
|
|
25214
25766
|
await activityLogsService.addEntry(sessionId, "Uploading collections to Localazy...");
|
|
25215
25767
|
try {
|
|
25216
25768
|
await LocalazyUploadService2.upload(importFile, uploadConfig);
|
|
25769
|
+
await activityLogsService.recordAttemptedEntries(
|
|
25770
|
+
sessionId,
|
|
25771
|
+
attemptedTuples.map((t3) => ({ ...t3, status: "success" }))
|
|
25772
|
+
);
|
|
25217
25773
|
} catch (error) {
|
|
25218
25774
|
success = false;
|
|
25219
25775
|
const errorMessage = `Upload to Localazy failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -25223,6 +25779,10 @@ const LocalazyTransferUploadService = ({ strapi: strapi2 }) => ({
|
|
|
25223
25779
|
message: errorMessage
|
|
25224
25780
|
});
|
|
25225
25781
|
await activityLogsService.addEntry(sessionId, errorMessage);
|
|
25782
|
+
await activityLogsService.recordAttemptedEntries(
|
|
25783
|
+
sessionId,
|
|
25784
|
+
attemptedTuples.map((t3) => ({ ...t3, status: "failed", errorMessage }))
|
|
25785
|
+
);
|
|
25226
25786
|
await activityLogsService.finishSession(sessionId, "failed", errorMessage);
|
|
25227
25787
|
console.timeEnd("upload");
|
|
25228
25788
|
return;
|
|
@@ -25627,7 +26187,7 @@ const shouldSetDownloadedProperty = (modelContentTransferSetup, parsedKeyRest) =
|
|
|
25627
26187
|
return "no";
|
|
25628
26188
|
}
|
|
25629
26189
|
};
|
|
25630
|
-
const getFilteredLanguagesCodesForDownload = async (languagesCodes) => {
|
|
26190
|
+
const getFilteredLanguagesCodesForDownload = async (languagesCodes, requestedUiLanguages) => {
|
|
25631
26191
|
const pluginSettingsServiceHelper = new PluginSettingsServiceHelper();
|
|
25632
26192
|
const requestInitiatorHelper = new RequestInitiatorHelper(strapi);
|
|
25633
26193
|
await pluginSettingsServiceHelper.setup();
|
|
@@ -25635,7 +26195,7 @@ const getFilteredLanguagesCodesForDownload = async (languagesCodes) => {
|
|
|
25635
26195
|
if (requestInitiatorHelper.isInitiatedByLocalazyWebhook()) {
|
|
25636
26196
|
localLanguagesCodes = pluginSettingsServiceHelper.getWebhookLanguagesCodes();
|
|
25637
26197
|
} else if (requestInitiatorHelper.isInitiatedByLocalazyPluginUI()) {
|
|
25638
|
-
localLanguagesCodes = pluginSettingsServiceHelper.getUiLanguagesCodes();
|
|
26198
|
+
localLanguagesCodes = requestedUiLanguages ?? pluginSettingsServiceHelper.getUiLanguagesCodes();
|
|
25639
26199
|
} else {
|
|
25640
26200
|
strapi.log.warn("Called by an unknown initiator.");
|
|
25641
26201
|
return languagesCodes;
|
|
@@ -25648,7 +26208,8 @@ const getFilteredLanguagesCodesForDownload = async (languagesCodes) => {
|
|
|
25648
26208
|
const LocalazyTransferDownloadService = ({ strapi: strapi2 }) => ({
|
|
25649
26209
|
async download({
|
|
25650
26210
|
notificationService,
|
|
25651
|
-
fullSync = false
|
|
26211
|
+
fullSync = false,
|
|
26212
|
+
requestedUiLanguages
|
|
25652
26213
|
}) {
|
|
25653
26214
|
console.time("download");
|
|
25654
26215
|
const syncMode = fullSync ? "Full sync" : "Incremental sync";
|
|
@@ -25728,7 +26289,7 @@ const LocalazyTransferDownloadService = ({ strapi: strapi2 }) => ({
|
|
|
25728
26289
|
return;
|
|
25729
26290
|
}
|
|
25730
26291
|
let languagesCodes = projectLanguagesWithoutSourceLanguage.map((language3) => language3.code);
|
|
25731
|
-
languagesCodes = await getFilteredLanguagesCodesForDownload(languagesCodes);
|
|
26292
|
+
languagesCodes = await getFilteredLanguagesCodesForDownload(languagesCodes, requestedUiLanguages);
|
|
25732
26293
|
const strapiUnsupportedLanguages = [];
|
|
25733
26294
|
const strapiLocales = await StrapiI18nService2.getLocales();
|
|
25734
26295
|
for (const isoLocalazy of languagesCodes) {
|
|
@@ -25947,6 +26508,12 @@ const LocalazyTransferDownloadService = ({ strapi: strapi2 }) => ({
|
|
|
25947
26508
|
message
|
|
25948
26509
|
});
|
|
25949
26510
|
await activityLogsService.addEntry(sessionId, message);
|
|
26511
|
+
await activityLogsService.recordAttemptedEntry(sessionId, {
|
|
26512
|
+
uid,
|
|
26513
|
+
documentId,
|
|
26514
|
+
locale: isoStrapi,
|
|
26515
|
+
status: "success"
|
|
26516
|
+
});
|
|
25950
26517
|
await markKeysProcessed(sourceKeys);
|
|
25951
26518
|
} catch (e2) {
|
|
25952
26519
|
success = false;
|
|
@@ -25963,6 +26530,13 @@ const LocalazyTransferDownloadService = ({ strapi: strapi2 }) => ({
|
|
|
25963
26530
|
sessionId,
|
|
25964
26531
|
`Failed to create ${uid} [${documentId}] in ${isoStrapi}: ${e2.message}`
|
|
25965
26532
|
);
|
|
26533
|
+
await activityLogsService.recordAttemptedEntry(sessionId, {
|
|
26534
|
+
uid,
|
|
26535
|
+
documentId,
|
|
26536
|
+
locale: isoStrapi,
|
|
26537
|
+
status: "failed",
|
|
26538
|
+
errorMessage: e2.message
|
|
26539
|
+
});
|
|
25966
26540
|
}
|
|
25967
26541
|
} else {
|
|
25968
26542
|
try {
|
|
@@ -25981,6 +26555,12 @@ const LocalazyTransferDownloadService = ({ strapi: strapi2 }) => ({
|
|
|
25981
26555
|
message
|
|
25982
26556
|
});
|
|
25983
26557
|
await activityLogsService.addEntry(sessionId, message);
|
|
26558
|
+
await activityLogsService.recordAttemptedEntry(sessionId, {
|
|
26559
|
+
uid,
|
|
26560
|
+
documentId,
|
|
26561
|
+
locale: isoStrapi,
|
|
26562
|
+
status: "success"
|
|
26563
|
+
});
|
|
25984
26564
|
await markKeysProcessed(sourceKeys);
|
|
25985
26565
|
} catch (e2) {
|
|
25986
26566
|
success = false;
|
|
@@ -25997,6 +26577,13 @@ const LocalazyTransferDownloadService = ({ strapi: strapi2 }) => ({
|
|
|
25997
26577
|
sessionId,
|
|
25998
26578
|
`Failed to update ${uid} [${documentId}] in ${isoStrapi}: ${e2.message}`
|
|
25999
26579
|
);
|
|
26580
|
+
await activityLogsService.recordAttemptedEntry(sessionId, {
|
|
26581
|
+
uid,
|
|
26582
|
+
documentId,
|
|
26583
|
+
locale: isoStrapi,
|
|
26584
|
+
status: "failed",
|
|
26585
|
+
errorMessage: e2.message
|
|
26586
|
+
});
|
|
26000
26587
|
}
|
|
26001
26588
|
}
|
|
26002
26589
|
} catch (e2) {
|
|
@@ -26009,6 +26596,13 @@ const LocalazyTransferDownloadService = ({ strapi: strapi2 }) => ({
|
|
|
26009
26596
|
sessionId,
|
|
26010
26597
|
`Error processing ${uid} [${documentId}] in ${isoStrapi}: ${e2.message}`
|
|
26011
26598
|
);
|
|
26599
|
+
await activityLogsService.recordAttemptedEntry(sessionId, {
|
|
26600
|
+
uid,
|
|
26601
|
+
documentId,
|
|
26602
|
+
locale: isoStrapi,
|
|
26603
|
+
status: "failed",
|
|
26604
|
+
errorMessage: e2.message
|
|
26605
|
+
});
|
|
26012
26606
|
}
|
|
26013
26607
|
}
|
|
26014
26608
|
}
|
|
@@ -26759,6 +27353,7 @@ const emptyActivityLogs = {
|
|
|
26759
27353
|
sessions: []
|
|
26760
27354
|
};
|
|
26761
27355
|
const ONE_YEAR_MS = 365 * 24 * 60 * 60 * 1e3;
|
|
27356
|
+
const ATTEMPTED_ENTRIES_CAP = 5e3;
|
|
26762
27357
|
let writeLock = Promise.resolve();
|
|
26763
27358
|
const withLock = async (fn) => {
|
|
26764
27359
|
let release;
|
|
@@ -26823,6 +27418,38 @@ const ActivityLogsService = ({ strapi: strapi2 }) => ({
|
|
|
26823
27418
|
await this.saveActivityLogs(logs);
|
|
26824
27419
|
});
|
|
26825
27420
|
},
|
|
27421
|
+
async recordAttemptedEntry(sessionId, entry) {
|
|
27422
|
+
return this.recordAttemptedEntries(sessionId, [entry]);
|
|
27423
|
+
},
|
|
27424
|
+
async recordAttemptedEntries(sessionId, entries) {
|
|
27425
|
+
if (!entries.length) {
|
|
27426
|
+
return;
|
|
27427
|
+
}
|
|
27428
|
+
return withLock(async () => {
|
|
27429
|
+
const logs = await this.getActivityLogs();
|
|
27430
|
+
const session = logs.sessions.find((s2) => s2.id === sessionId);
|
|
27431
|
+
if (!session) {
|
|
27432
|
+
return;
|
|
27433
|
+
}
|
|
27434
|
+
if (!session.attemptedEntries) {
|
|
27435
|
+
session.attemptedEntries = [];
|
|
27436
|
+
}
|
|
27437
|
+
const now = Date.now();
|
|
27438
|
+
const wasAtCap = session.attemptedEntries.length >= ATTEMPTED_ENTRIES_CAP;
|
|
27439
|
+
const remaining = Math.max(0, ATTEMPTED_ENTRIES_CAP - session.attemptedEntries.length);
|
|
27440
|
+
const accepted = entries.slice(0, remaining);
|
|
27441
|
+
const dropped = entries.length - accepted.length;
|
|
27442
|
+
for (const entry of accepted) {
|
|
27443
|
+
session.attemptedEntries.push({ ...entry, attemptedAt: now });
|
|
27444
|
+
}
|
|
27445
|
+
if (dropped > 0 && !wasAtCap) {
|
|
27446
|
+
strapi2.log.warn(
|
|
27447
|
+
`[localazy] attemptedEntries cap (${ATTEMPTED_ENTRIES_CAP}) reached for session ${sessionId}; ${dropped} record(s) dropped.`
|
|
27448
|
+
);
|
|
27449
|
+
}
|
|
27450
|
+
await this.saveActivityLogs(logs);
|
|
27451
|
+
});
|
|
27452
|
+
},
|
|
26826
27453
|
async finishSession(sessionId, status, summary) {
|
|
26827
27454
|
return withLock(async () => {
|
|
26828
27455
|
const logs = await this.getActivityLogs();
|