@localazy/strapi-plugin 1.1.3 → 1.4.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/dist/_chunks/{FetchIdentity-Dl2vx6zB.js → FetchIdentity-BpFOT163.js} +2 -2
- package/dist/_chunks/{FetchIdentity-C8-hFqEW.mjs → FetchIdentity-Cor4ooZv.mjs} +2 -2
- package/dist/_chunks/{LanguagesSelector-vu-Lj7L6.mjs → LanguagesSelector-BbrMKwQY.mjs} +3 -3
- package/dist/_chunks/{LanguagesSelector-DsW3aJ7p.js → LanguagesSelector-CC1ecWnc.js} +3 -3
- package/dist/_chunks/{LocalazyApp-BIll8u_G.js → LocalazyApp-D0JGRQeF.js} +87 -10
- package/dist/_chunks/{LocalazyApp-Dxygk1mM.mjs → LocalazyApp-DpFR8p10.mjs} +88 -11
- package/dist/_chunks/{LocalazyContentTransferSetup-BhEjlIgE.js → LocalazyContentTransferSetup-Cr7D-BUi.js} +5 -5
- package/dist/_chunks/{LocalazyContentTransferSetup-Cz88htly.mjs → LocalazyContentTransferSetup-Dael1Q4w.mjs} +5 -5
- package/dist/_chunks/{LocalazyGlobalSettings-B3Z8f4G8.js → LocalazyGlobalSettings-D3nG4zl7.js} +6 -6
- package/dist/_chunks/{LocalazyGlobalSettings-CgOADY93.mjs → LocalazyGlobalSettings-DzBI5M-K.mjs} +6 -6
- package/dist/_chunks/{LocalazyPanel-CQ8-K88T.js → LocalazyPanel-KQPLxTDz.js} +3 -3
- package/dist/_chunks/{LocalazyPanel-BjrSVZpI.mjs → LocalazyPanel-W6j8N1WC.mjs} +3 -3
- package/dist/_chunks/{LocalazyStatusColumn-CA34yhSE.js → LocalazyStatusColumn-B7Q_U2Lu.js} +2 -2
- package/dist/_chunks/{LocalazyStatusColumn-D0b2LCpg.mjs → LocalazyStatusColumn-CrhsCjgp.mjs} +2 -2
- package/dist/_chunks/{RequireLocalazyAuth-DpimzlRh.mjs → RequireLocalazyAuth-BwoGRfJq.mjs} +4 -4
- package/dist/_chunks/{RequireLocalazyAuth-BPsrUlFm.js → RequireLocalazyAuth-D3GeZPDh.js} +4 -4
- package/dist/_chunks/{entry-exclusion-service-nJswJPwF.js → entry-exclusion-service-Bwruvyce.js} +1 -1
- package/dist/_chunks/{entry-exclusion-service-DNJZg3U2.mjs → entry-exclusion-service-D7gaRHPE.mjs} +1 -1
- package/dist/_chunks/{has-model-changed-BnL6D-eR.js → has-model-changed-5IV9WI_-.js} +2 -2
- package/dist/_chunks/{has-model-changed-D6fjlIdc.mjs → has-model-changed-Dp2Vkwjw.mjs} +2 -2
- package/dist/_chunks/{i18n-CAsbU4pi.js → i18n-C_5tJvr0.js} +5 -1
- package/dist/_chunks/{i18n-QelOdIB2.mjs → i18n-o24yXf-j.mjs} +5 -1
- package/dist/_chunks/{index-CHjIMO9T.mjs → index-BjL9XpRh.mjs} +4 -3
- package/dist/_chunks/{index-CPBTdynk.mjs → index-C9hN3KbD.mjs} +8 -8
- package/dist/_chunks/{index-BK7lmZSN.js → index-CTkxoioB.js} +1 -0
- package/dist/_chunks/{index-BXPaiO0d.js → index-jAg6jjCu.js} +8 -8
- package/dist/_chunks/strapi-api-base-CAzGcwF6.mjs +23 -0
- package/dist/_chunks/strapi-api-base-IG3Pe5Sg.js +22 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/modules/@common/api/strapi-api-base.d.ts +19 -4
- package/dist/admin/src/modules/@common/utils/sort-by-model-name.d.ts +1 -1
- 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/server/index.js +510 -5
- package/dist/server/index.mjs +510 -6
- package/dist/server/src/controllers/debug-bundle-controller.d.ts +7 -0
- package/dist/server/src/controllers/index.d.ts +5 -0
- package/dist/server/src/core/index.d.ts +2 -0
- package/dist/server/src/db/model/activity-logs.d.ts +10 -0
- package/dist/server/src/index.d.ts +7 -0
- package/dist/server/src/routes/debug-bundle-routes.d.ts +9 -0
- package/dist/server/src/services/activity-logs-service.d.ts +3 -1
- package/dist/server/src/services/index.d.ts +2 -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 -2
- package/dist/_chunks/strapi-api-base-DkjMgMHl.js +0 -2425
- package/dist/_chunks/strapi-api-base-GgrMbYak.mjs +0 -2426
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.4.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
|
};
|
|
@@ -20620,7 +20623,6 @@ const devDependencies = {
|
|
|
20620
20623
|
"@types/react-dom": "^19.0.2",
|
|
20621
20624
|
"@typescript-eslint/eslint-plugin": "^8.21.0",
|
|
20622
20625
|
"@typescript-eslint/parser": "^8.21.0",
|
|
20623
|
-
axios: "^1.7.9",
|
|
20624
20626
|
chalk: "^5.4.1",
|
|
20625
20627
|
"conventional-changelog": "^5.1.0",
|
|
20626
20628
|
"conventional-changelog-cli": "^4.1.0",
|
|
@@ -24502,6 +24504,413 @@ const ActivityLogsController = ({ strapi: _strapi }) => ({
|
|
|
24502
24504
|
ctx.body = { success: true };
|
|
24503
24505
|
}
|
|
24504
24506
|
});
|
|
24507
|
+
const URL_SECRET_KEYS = ["token", "secret", "signature", "key", "apikey", "api_key"];
|
|
24508
|
+
const DEBUG_BUNDLE_ENV_ALLOWLIST = [
|
|
24509
|
+
"STRAPI_ADMIN_LOCALAZY_ENV",
|
|
24510
|
+
"STRAPI_ADMIN_BACKEND_URL",
|
|
24511
|
+
"NODE_ENV",
|
|
24512
|
+
"STRAPI_TELEMETRY_DISABLED"
|
|
24513
|
+
];
|
|
24514
|
+
const redactIdentity = (identity2) => {
|
|
24515
|
+
if (!identity2) return null;
|
|
24516
|
+
return {
|
|
24517
|
+
userId: identity2.user?.id ?? "",
|
|
24518
|
+
name: identity2.user?.name ?? "",
|
|
24519
|
+
scope: identity2.scope ?? "",
|
|
24520
|
+
hasAccessToken: Boolean(identity2.accessToken)
|
|
24521
|
+
};
|
|
24522
|
+
};
|
|
24523
|
+
const scrubUrlSecrets = (input) => {
|
|
24524
|
+
if (typeof input !== "string") return input;
|
|
24525
|
+
if (!/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(input) && !input.includes("?")) return input;
|
|
24526
|
+
try {
|
|
24527
|
+
const url2 = new URL(input);
|
|
24528
|
+
let mutated = false;
|
|
24529
|
+
for (const k2 of [...url2.searchParams.keys()]) {
|
|
24530
|
+
if (URL_SECRET_KEYS.includes(k2.toLowerCase())) {
|
|
24531
|
+
url2.searchParams.delete(k2);
|
|
24532
|
+
mutated = true;
|
|
24533
|
+
}
|
|
24534
|
+
}
|
|
24535
|
+
return mutated ? url2.toString() : input;
|
|
24536
|
+
} catch {
|
|
24537
|
+
return input;
|
|
24538
|
+
}
|
|
24539
|
+
};
|
|
24540
|
+
const pickEnvAllowlist = (env) => {
|
|
24541
|
+
const out = {};
|
|
24542
|
+
for (const key of DEBUG_BUNDLE_ENV_ALLOWLIST) {
|
|
24543
|
+
const raw = env[key];
|
|
24544
|
+
const present = raw !== void 0;
|
|
24545
|
+
out[key] = {
|
|
24546
|
+
present,
|
|
24547
|
+
value: present ? raw : null
|
|
24548
|
+
};
|
|
24549
|
+
}
|
|
24550
|
+
return out;
|
|
24551
|
+
};
|
|
24552
|
+
const serializeContentTypeSchema = (contentType) => {
|
|
24553
|
+
if (!contentType || typeof contentType !== "object") return null;
|
|
24554
|
+
const ct = contentType;
|
|
24555
|
+
return {
|
|
24556
|
+
kind: ct.kind,
|
|
24557
|
+
collectionName: ct.collectionName,
|
|
24558
|
+
info: ct.info,
|
|
24559
|
+
options: ct.options,
|
|
24560
|
+
pluginOptions: ct.pluginOptions,
|
|
24561
|
+
attributes: ct.attributes
|
|
24562
|
+
};
|
|
24563
|
+
};
|
|
24564
|
+
const DOCUMENT_INTERNAL_FIELDS = ["createdBy", "updatedBy"];
|
|
24565
|
+
const stripDocumentInternals = (doc) => {
|
|
24566
|
+
if (!doc || typeof doc !== "object") return doc;
|
|
24567
|
+
const clone = { ...doc };
|
|
24568
|
+
for (const field of DOCUMENT_INTERNAL_FIELDS) {
|
|
24569
|
+
delete clone[field];
|
|
24570
|
+
}
|
|
24571
|
+
return clone;
|
|
24572
|
+
};
|
|
24573
|
+
class SessionNotFoundError extends Error {
|
|
24574
|
+
constructor(sessionId) {
|
|
24575
|
+
super(`Session ${sessionId} not found`);
|
|
24576
|
+
this.name = "SessionNotFoundError";
|
|
24577
|
+
}
|
|
24578
|
+
}
|
|
24579
|
+
const DEBUG_BUNDLE_SCHEMA_VERSION = 1;
|
|
24580
|
+
const CAPS = {
|
|
24581
|
+
perEntryBytes: 1 * 1024 * 1024,
|
|
24582
|
+
// 1 MB
|
|
24583
|
+
totalEntries: 100,
|
|
24584
|
+
errorLogBytes: 2 * 1024 * 1024,
|
|
24585
|
+
// 2 MB
|
|
24586
|
+
consoleErrorsLines: 200,
|
|
24587
|
+
browserPayloadBytes: 64 * 1024
|
|
24588
|
+
// 64 KB
|
|
24589
|
+
};
|
|
24590
|
+
const selectAttemptedEntries = (attempted) => {
|
|
24591
|
+
const failedByKey = /* @__PURE__ */ new Map();
|
|
24592
|
+
const successByKey = /* @__PURE__ */ new Map();
|
|
24593
|
+
for (const record of attempted) {
|
|
24594
|
+
const key = `${record.uid}|${record.documentId}|${record.locale}`;
|
|
24595
|
+
const selected = {
|
|
24596
|
+
uid: record.uid,
|
|
24597
|
+
documentId: record.documentId,
|
|
24598
|
+
locale: record.locale,
|
|
24599
|
+
status: record.status,
|
|
24600
|
+
attemptedAt: record.attemptedAt
|
|
24601
|
+
};
|
|
24602
|
+
if (record.status === "failed") {
|
|
24603
|
+
successByKey.delete(key);
|
|
24604
|
+
if (!failedByKey.has(key)) failedByKey.set(key, selected);
|
|
24605
|
+
} else if (!failedByKey.has(key) && !successByKey.has(key)) {
|
|
24606
|
+
successByKey.set(key, selected);
|
|
24607
|
+
}
|
|
24608
|
+
}
|
|
24609
|
+
return [...failedByKey.values(), ...successByKey.values()];
|
|
24610
|
+
};
|
|
24611
|
+
const formatTimestampForFilename = (ms2) => new Date(ms2).toISOString().replace(/[:.]/g, "-");
|
|
24612
|
+
const safeUidForPath = (uid) => uid.replace(/::/g, "--").replace(/\./g, "-");
|
|
24613
|
+
const decodeBrowserPayload = (encoded) => {
|
|
24614
|
+
const markers = [];
|
|
24615
|
+
if (!encoded) return { payload: null, truncationMarkers: markers };
|
|
24616
|
+
try {
|
|
24617
|
+
const raw = Buffer.from(encoded, "base64").toString("utf8");
|
|
24618
|
+
const rawBytes = Buffer.byteLength(raw, "utf8");
|
|
24619
|
+
if (rawBytes > CAPS.browserPayloadBytes) {
|
|
24620
|
+
markers.push({ kind: "browser-payload-truncated", originalSizeBytes: rawBytes });
|
|
24621
|
+
return { payload: null, truncationMarkers: markers };
|
|
24622
|
+
}
|
|
24623
|
+
const parsed = JSON.parse(raw);
|
|
24624
|
+
if (parsed && Array.isArray(parsed.consoleErrors) && parsed.consoleErrors.length > CAPS.consoleErrorsLines) {
|
|
24625
|
+
markers.push({ kind: "console-errors-truncated", originalCount: parsed.consoleErrors.length });
|
|
24626
|
+
parsed.consoleErrors = parsed.consoleErrors.slice(0, CAPS.consoleErrorsLines);
|
|
24627
|
+
}
|
|
24628
|
+
return { payload: parsed, truncationMarkers: markers };
|
|
24629
|
+
} catch {
|
|
24630
|
+
return { payload: null, truncationMarkers: markers };
|
|
24631
|
+
}
|
|
24632
|
+
};
|
|
24633
|
+
const deepScrubUrls = (value) => {
|
|
24634
|
+
if (value == null) return value;
|
|
24635
|
+
if (typeof value === "string") return scrubUrlSecrets(value);
|
|
24636
|
+
if (Array.isArray(value)) return value.map(deepScrubUrls);
|
|
24637
|
+
if (typeof value === "object") {
|
|
24638
|
+
const out = {};
|
|
24639
|
+
for (const [k2, v2] of Object.entries(value)) {
|
|
24640
|
+
out[k2] = deepScrubUrls(v2);
|
|
24641
|
+
}
|
|
24642
|
+
return out;
|
|
24643
|
+
}
|
|
24644
|
+
return value;
|
|
24645
|
+
};
|
|
24646
|
+
const sanitizePluginSettings = (settings) => {
|
|
24647
|
+
const cloned = JSON.parse(JSON.stringify(settings ?? {}));
|
|
24648
|
+
if (cloned.webhookConfig?.url) {
|
|
24649
|
+
cloned.webhookConfig.url = scrubUrlSecrets(cloned.webhookConfig.url);
|
|
24650
|
+
}
|
|
24651
|
+
return cloned;
|
|
24652
|
+
};
|
|
24653
|
+
const sanitizeSession = (session) => {
|
|
24654
|
+
const cloned = JSON.parse(JSON.stringify(session));
|
|
24655
|
+
for (const entry of cloned.entries ?? []) {
|
|
24656
|
+
if (entry.data !== void 0) {
|
|
24657
|
+
entry.data = deepScrubUrls(entry.data);
|
|
24658
|
+
}
|
|
24659
|
+
}
|
|
24660
|
+
return cloned;
|
|
24661
|
+
};
|
|
24662
|
+
const buildErrorLog = (session) => {
|
|
24663
|
+
const lines = [];
|
|
24664
|
+
for (const entry of session.entries ?? []) {
|
|
24665
|
+
const ts = Number.isFinite(entry.timestamp) ? new Date(entry.timestamp).toISOString() : "unknown";
|
|
24666
|
+
lines.push(`${ts} ${entry.message}`);
|
|
24667
|
+
}
|
|
24668
|
+
if (session.status === "failed" && session.summary) {
|
|
24669
|
+
lines.push(`ERROR: ${session.summary}`);
|
|
24670
|
+
}
|
|
24671
|
+
const full = lines.join("\n");
|
|
24672
|
+
const fullBytes = Buffer.byteLength(full, "utf8");
|
|
24673
|
+
if (fullBytes <= CAPS.errorLogBytes) {
|
|
24674
|
+
return { text: full, markers: [] };
|
|
24675
|
+
}
|
|
24676
|
+
const truncated = Buffer.from(full, "utf8").subarray(fullBytes - CAPS.errorLogBytes).toString("utf8");
|
|
24677
|
+
return {
|
|
24678
|
+
text: `...[truncated, kept last ${CAPS.errorLogBytes} bytes]
|
|
24679
|
+
${truncated}`,
|
|
24680
|
+
markers: [{ kind: "error-log-truncated", originalSizeBytes: fullBytes }]
|
|
24681
|
+
};
|
|
24682
|
+
};
|
|
24683
|
+
const readHostPackages = async (strapi2) => {
|
|
24684
|
+
const appRoot = strapi2?.dirs?.app?.root ?? process.cwd();
|
|
24685
|
+
if (!appRoot) return { data: null, reason: "host-app-root-unknown" };
|
|
24686
|
+
try {
|
|
24687
|
+
const raw = await require$$1$4.promises.readFile(require$$1__default.default.join(appRoot, "package.json"), "utf8");
|
|
24688
|
+
const parsed = JSON.parse(raw);
|
|
24689
|
+
return {
|
|
24690
|
+
data: {
|
|
24691
|
+
name: parsed.name,
|
|
24692
|
+
version: parsed.version,
|
|
24693
|
+
dependencies: parsed.dependencies ?? {},
|
|
24694
|
+
devDependencies: parsed.devDependencies ?? {}
|
|
24695
|
+
}
|
|
24696
|
+
};
|
|
24697
|
+
} catch (err) {
|
|
24698
|
+
return { data: null, reason: err instanceof Error ? err.message : String(err) };
|
|
24699
|
+
}
|
|
24700
|
+
};
|
|
24701
|
+
const buildReadme = ({
|
|
24702
|
+
sessionShortId,
|
|
24703
|
+
sessionId,
|
|
24704
|
+
schemaVersion,
|
|
24705
|
+
hostPackagesSkipped
|
|
24706
|
+
}) => {
|
|
24707
|
+
const lines = [];
|
|
24708
|
+
lines.push(`# Localazy Strapi Plugin — Troubleshooting Bundle`);
|
|
24709
|
+
lines.push("");
|
|
24710
|
+
lines.push(`Generated: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
24711
|
+
lines.push(`Session id: ${sessionId}`);
|
|
24712
|
+
lines.push(`Schema version: ${schemaVersion}`);
|
|
24713
|
+
lines.push("");
|
|
24714
|
+
lines.push("## Contents");
|
|
24715
|
+
lines.push("");
|
|
24716
|
+
lines.push("- `meta.json` — schema version, session metadata.");
|
|
24717
|
+
lines.push("- `versions.json` — plugin version, Strapi version, Node version, platform/arch.");
|
|
24718
|
+
lines.push("- `env.json` — allow-listed environment variables only.");
|
|
24719
|
+
lines.push("- `browser.json` — user agent, language, viewport, console errors (from the admin UI).");
|
|
24720
|
+
lines.push("- `session.json` — full activity-log session record (URLs scrubbed of secret query keys).");
|
|
24721
|
+
lines.push("- `error.log` — chronological text log of session entries.");
|
|
24722
|
+
lines.push("- `plugin-settings.json` — full plugin settings (webhook URL secrets scrubbed).");
|
|
24723
|
+
lines.push("- `content-transfer-setup.json` — content-transfer setup record.");
|
|
24724
|
+
if (hostPackagesSkipped) {
|
|
24725
|
+
lines.push("- `host-packages.json` — NOT INCLUDED (could not be read; see `bundle-truncated.json`).");
|
|
24726
|
+
} else {
|
|
24727
|
+
lines.push("- `host-packages.json` — host project's package.json (deps + devDeps only).");
|
|
24728
|
+
}
|
|
24729
|
+
lines.push("- `content-types/` — schema.json per touched content-type UID.");
|
|
24730
|
+
lines.push("- `entries/<uid>/<documentId>--<locale>.json` — per-entry payload from `strapi.documents().findOne()`.");
|
|
24731
|
+
lines.push("- `bundle-truncated.json` — present only if any per-section cap was exceeded.");
|
|
24732
|
+
lines.push("");
|
|
24733
|
+
lines.push("## Reproducing locally");
|
|
24734
|
+
lines.push("");
|
|
24735
|
+
lines.push("1. `unzip` the bundle.");
|
|
24736
|
+
lines.push("2. Recreate the content types from `content-types/*.schema.json` in your Strapi project.");
|
|
24737
|
+
lines.push("3. Import the entries via the Document Service using `entries/<uid>/*.json`.");
|
|
24738
|
+
lines.push("4. Apply `plugin-settings.json` and `content-transfer-setup.json` via the plugin admin UI.");
|
|
24739
|
+
lines.push("5. Trigger the same upload/download/webhook with the same locale set.");
|
|
24740
|
+
lines.push("6. Compare the failure against `error.log` and `session.json`.");
|
|
24741
|
+
lines.push("");
|
|
24742
|
+
lines.push(`Bundle short id: ${sessionShortId}`);
|
|
24743
|
+
return lines.join("\n");
|
|
24744
|
+
};
|
|
24745
|
+
const buildDebugBundle = async ({
|
|
24746
|
+
strapi: strapi2,
|
|
24747
|
+
sessionId,
|
|
24748
|
+
browserPayloadEncoded
|
|
24749
|
+
}) => {
|
|
24750
|
+
const zip = new JSZip__default.default();
|
|
24751
|
+
const truncationMarkers = [];
|
|
24752
|
+
const pluginName = "localazy";
|
|
24753
|
+
const activityLogsService = strapi2.plugin(pluginName).service("ActivityLogsService");
|
|
24754
|
+
const pluginSettingsService = strapi2.plugin(pluginName).service("PluginSettingsService");
|
|
24755
|
+
const strapiService = strapi2.plugin(pluginName).service("StrapiService");
|
|
24756
|
+
const localazyUserService = strapi2.plugin(pluginName).service("LocalazyUserService");
|
|
24757
|
+
const session = await activityLogsService.getSession(sessionId);
|
|
24758
|
+
if (!session) {
|
|
24759
|
+
throw new SessionNotFoundError(sessionId);
|
|
24760
|
+
}
|
|
24761
|
+
const sessionShortId = session.id.slice(0, 8);
|
|
24762
|
+
const pluginSettingsRaw = await pluginSettingsService.getPluginSettings();
|
|
24763
|
+
const contentTransferSetup = await pluginSettingsService.getContentTransferSetup();
|
|
24764
|
+
const identity2 = await localazyUserService.getUser();
|
|
24765
|
+
const redactedIdentity = redactIdentity(identity2);
|
|
24766
|
+
const pluginVersion = await strapiService.getPluginVersion();
|
|
24767
|
+
const strapiVersion = strapiService.getStrapiVersion();
|
|
24768
|
+
zip.file(
|
|
24769
|
+
"meta.json",
|
|
24770
|
+
JSON.stringify(
|
|
24771
|
+
{
|
|
24772
|
+
schemaVersion: DEBUG_BUNDLE_SCHEMA_VERSION,
|
|
24773
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24774
|
+
sessionId: session.id,
|
|
24775
|
+
sessionShortId,
|
|
24776
|
+
eventType: session.eventType,
|
|
24777
|
+
status: session.status,
|
|
24778
|
+
startedAt: session.startedAt,
|
|
24779
|
+
finishedAt: session.finishedAt ?? null,
|
|
24780
|
+
initiatedBy: session.initiatedBy,
|
|
24781
|
+
identity: redactedIdentity
|
|
24782
|
+
},
|
|
24783
|
+
null,
|
|
24784
|
+
2
|
|
24785
|
+
)
|
|
24786
|
+
);
|
|
24787
|
+
zip.file(
|
|
24788
|
+
"versions.json",
|
|
24789
|
+
JSON.stringify(
|
|
24790
|
+
{
|
|
24791
|
+
plugin: { name: "@localazy/strapi-plugin", version: pluginVersion },
|
|
24792
|
+
strapi: { version: strapiVersion },
|
|
24793
|
+
node: process.version,
|
|
24794
|
+
platform: process.platform,
|
|
24795
|
+
arch: process.arch
|
|
24796
|
+
},
|
|
24797
|
+
null,
|
|
24798
|
+
2
|
|
24799
|
+
)
|
|
24800
|
+
);
|
|
24801
|
+
const envSnapshot = pickEnvAllowlist(process.env);
|
|
24802
|
+
zip.file(
|
|
24803
|
+
"env.json",
|
|
24804
|
+
JSON.stringify(
|
|
24805
|
+
{
|
|
24806
|
+
allowlist: [...DEBUG_BUNDLE_ENV_ALLOWLIST],
|
|
24807
|
+
values: envSnapshot
|
|
24808
|
+
},
|
|
24809
|
+
null,
|
|
24810
|
+
2
|
|
24811
|
+
)
|
|
24812
|
+
);
|
|
24813
|
+
const { payload: browserPayload, truncationMarkers: browserMarkers } = decodeBrowserPayload(browserPayloadEncoded);
|
|
24814
|
+
truncationMarkers.push(...browserMarkers);
|
|
24815
|
+
zip.file("browser.json", JSON.stringify(browserPayload ? browserPayload : { captured: false }, null, 2));
|
|
24816
|
+
zip.file("session.json", JSON.stringify(sanitizeSession(session), null, 2));
|
|
24817
|
+
const { text: errorLogText, markers: errorLogMarkers } = buildErrorLog(session);
|
|
24818
|
+
truncationMarkers.push(...errorLogMarkers);
|
|
24819
|
+
zip.file("error.log", errorLogText);
|
|
24820
|
+
zip.file("plugin-settings.json", JSON.stringify(sanitizePluginSettings(pluginSettingsRaw), null, 2));
|
|
24821
|
+
zip.file("content-transfer-setup.json", JSON.stringify(contentTransferSetup, null, 2));
|
|
24822
|
+
const hostPackages = await readHostPackages(strapi2);
|
|
24823
|
+
if (hostPackages.data) {
|
|
24824
|
+
zip.file("host-packages.json", JSON.stringify(hostPackages.data, null, 2));
|
|
24825
|
+
}
|
|
24826
|
+
if (session.attemptedEntries === void 0) {
|
|
24827
|
+
truncationMarkers.push({ kind: "attempted-entries-missing-legacy-session" });
|
|
24828
|
+
}
|
|
24829
|
+
const selected = selectAttemptedEntries(session.attemptedEntries ?? []);
|
|
24830
|
+
const kept = selected.slice(0, CAPS.totalEntries);
|
|
24831
|
+
const skipped = selected.slice(CAPS.totalEntries);
|
|
24832
|
+
if (skipped.length > 0) {
|
|
24833
|
+
truncationMarkers.push({
|
|
24834
|
+
kind: "entries-skipped",
|
|
24835
|
+
tuples: skipped.map(({ uid, documentId, locale }) => ({ uid, documentId, locale }))
|
|
24836
|
+
});
|
|
24837
|
+
}
|
|
24838
|
+
const uniqueUids = [...new Set(kept.map((t3) => t3.uid))];
|
|
24839
|
+
for (const uid of uniqueUids) {
|
|
24840
|
+
const schema = serializeContentTypeSchema(strapi2.contentTypes?.[uid]);
|
|
24841
|
+
if (schema) {
|
|
24842
|
+
zip.file(`content-types/${safeUidForPath(uid)}.schema.json`, JSON.stringify(schema, null, 2));
|
|
24843
|
+
}
|
|
24844
|
+
}
|
|
24845
|
+
for (const tuple of kept) {
|
|
24846
|
+
let documentJson = null;
|
|
24847
|
+
try {
|
|
24848
|
+
const doc = await strapi2.documents(tuple.uid).findOne({ documentId: tuple.documentId, locale: tuple.locale, populate: "*" });
|
|
24849
|
+
documentJson = doc ? stripDocumentInternals(doc) : { __not_found: true, attemptedAt: tuple.attemptedAt };
|
|
24850
|
+
} catch (err) {
|
|
24851
|
+
documentJson = { __error: err instanceof Error ? err.message : String(err) };
|
|
24852
|
+
}
|
|
24853
|
+
const serialised = JSON.stringify(documentJson, null, 2);
|
|
24854
|
+
const sizeBytes = Buffer.byteLength(serialised, "utf8");
|
|
24855
|
+
let body;
|
|
24856
|
+
if (sizeBytes > CAPS.perEntryBytes) {
|
|
24857
|
+
truncationMarkers.push({
|
|
24858
|
+
kind: "entry-too-large",
|
|
24859
|
+
uid: tuple.uid,
|
|
24860
|
+
documentId: tuple.documentId,
|
|
24861
|
+
locale: tuple.locale,
|
|
24862
|
+
originalSizeBytes: sizeBytes
|
|
24863
|
+
});
|
|
24864
|
+
body = JSON.stringify({ __truncated: true, originalSizeBytes: sizeBytes }, null, 2);
|
|
24865
|
+
} else {
|
|
24866
|
+
body = serialised;
|
|
24867
|
+
}
|
|
24868
|
+
const safeDocId = tuple.documentId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
24869
|
+
zip.file(`entries/${safeUidForPath(tuple.uid)}/${safeDocId}--${tuple.locale}.json`, body);
|
|
24870
|
+
}
|
|
24871
|
+
zip.file(
|
|
24872
|
+
"README.md",
|
|
24873
|
+
buildReadme({
|
|
24874
|
+
sessionShortId,
|
|
24875
|
+
sessionId: session.id,
|
|
24876
|
+
schemaVersion: DEBUG_BUNDLE_SCHEMA_VERSION,
|
|
24877
|
+
hostPackagesSkipped: !hostPackages.data
|
|
24878
|
+
})
|
|
24879
|
+
);
|
|
24880
|
+
if (truncationMarkers.length > 0 || !hostPackages.data) {
|
|
24881
|
+
const marker = {
|
|
24882
|
+
schemaVersion: DEBUG_BUNDLE_SCHEMA_VERSION,
|
|
24883
|
+
markers: truncationMarkers,
|
|
24884
|
+
hostPackagesSkipped: hostPackages.data ? null : hostPackages.reason ?? "unknown"
|
|
24885
|
+
};
|
|
24886
|
+
zip.file("bundle-truncated.json", JSON.stringify(marker, null, 2));
|
|
24887
|
+
}
|
|
24888
|
+
const zipBuffer = await zip.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
|
|
24889
|
+
const filename = `localazy-debug-bundle-${sessionShortId}-${formatTimestampForFilename(Date.now())}.zip`;
|
|
24890
|
+
return { zipBuffer, filename };
|
|
24891
|
+
};
|
|
24892
|
+
const DebugBundleController = ({ strapi: strapi2 }) => ({
|
|
24893
|
+
async getBundle(ctx) {
|
|
24894
|
+
const { sessionId } = ctx.params;
|
|
24895
|
+
const browserPayloadEncoded = typeof ctx.query.browser === "string" ? ctx.query.browser : null;
|
|
24896
|
+
try {
|
|
24897
|
+
const { zipBuffer, filename } = await buildDebugBundle({
|
|
24898
|
+
strapi: strapi2,
|
|
24899
|
+
sessionId,
|
|
24900
|
+
browserPayloadEncoded
|
|
24901
|
+
});
|
|
24902
|
+
ctx.type = "application/zip";
|
|
24903
|
+
ctx.set("Content-Disposition", `attachment; filename="${filename}"`);
|
|
24904
|
+
ctx.body = zipBuffer;
|
|
24905
|
+
} catch (err) {
|
|
24906
|
+
if (err instanceof SessionNotFoundError) {
|
|
24907
|
+
return ctx.notFound("Session not found");
|
|
24908
|
+
}
|
|
24909
|
+
strapi2.log.error(`[localazy] failed to build debug bundle: ${err instanceof Error ? err.message : err}`);
|
|
24910
|
+
throw err;
|
|
24911
|
+
}
|
|
24912
|
+
}
|
|
24913
|
+
});
|
|
24505
24914
|
const controllers = {
|
|
24506
24915
|
LocalazyUserController,
|
|
24507
24916
|
LocalazyAuthController,
|
|
@@ -24510,7 +24919,8 @@ const controllers = {
|
|
|
24510
24919
|
PluginSettingsController,
|
|
24511
24920
|
LocalazyTransferController,
|
|
24512
24921
|
EntryExclusionController,
|
|
24513
|
-
ActivityLogsController
|
|
24922
|
+
ActivityLogsController,
|
|
24923
|
+
DebugBundleController
|
|
24514
24924
|
};
|
|
24515
24925
|
const verifyWebhook = (config2, { strapi: strapi2 }) => {
|
|
24516
24926
|
return async (ctx, next) => {
|
|
@@ -24821,6 +25231,16 @@ const ActivityLogsRoutes = [
|
|
|
24821
25231
|
}
|
|
24822
25232
|
}
|
|
24823
25233
|
];
|
|
25234
|
+
const DebugBundleRoutes = [
|
|
25235
|
+
{
|
|
25236
|
+
method: "GET",
|
|
25237
|
+
path: "/activity-logs/:sessionId/bundle",
|
|
25238
|
+
handler: "DebugBundleController.getBundle",
|
|
25239
|
+
config: {
|
|
25240
|
+
policies: []
|
|
25241
|
+
}
|
|
25242
|
+
}
|
|
25243
|
+
];
|
|
24824
25244
|
const routes = {
|
|
24825
25245
|
"content-api": {
|
|
24826
25246
|
routes: [...StrapiRoutes]
|
|
@@ -24835,7 +25255,8 @@ const routes = {
|
|
|
24835
25255
|
...LocalazyTransferRoutes,
|
|
24836
25256
|
...LocalazyProjectRoutes,
|
|
24837
25257
|
...EntryExclusionRoutes,
|
|
24838
|
-
...ActivityLogsRoutes
|
|
25258
|
+
...ActivityLogsRoutes,
|
|
25259
|
+
...DebugBundleRoutes
|
|
24839
25260
|
]
|
|
24840
25261
|
},
|
|
24841
25262
|
LocalazyPublicTransferRoutes: {
|
|
@@ -25114,6 +25535,8 @@ const LocalazyTransferUploadService = ({ strapi: strapi2 }) => ({
|
|
|
25114
25535
|
const { setup: setup2 } = contentTransferSetup;
|
|
25115
25536
|
const collectionsNames = getCollectionsNames(setup2);
|
|
25116
25537
|
const models = await StrapiService2.getModels();
|
|
25538
|
+
const strapiDefaultLocaleCode = await StrapiI18nService2.getDefaultLocaleCode();
|
|
25539
|
+
const attemptedTuples = [];
|
|
25117
25540
|
let pickedFlattenStrapiContent = {};
|
|
25118
25541
|
for (const collectionName of collectionsNames) {
|
|
25119
25542
|
const currentModel = models.find((model) => model.collectionName === collectionName);
|
|
@@ -25176,6 +25599,15 @@ const LocalazyTransferUploadService = ({ strapi: strapi2 }) => ({
|
|
|
25176
25599
|
});
|
|
25177
25600
|
await activityLogsService.addEntry(sessionId, message);
|
|
25178
25601
|
strapi2.log.info(message);
|
|
25602
|
+
if (strapiDefaultLocaleCode) {
|
|
25603
|
+
for (const entry of entries) {
|
|
25604
|
+
attemptedTuples.push({
|
|
25605
|
+
uid: modelUid,
|
|
25606
|
+
documentId: entry.documentId,
|
|
25607
|
+
locale: strapiDefaultLocaleCode
|
|
25608
|
+
});
|
|
25609
|
+
}
|
|
25610
|
+
}
|
|
25179
25611
|
entries = omitDeep(entries, ["locale", "createdAt", "updatedAt", "publishedAt"]);
|
|
25180
25612
|
entries.forEach((entry) => {
|
|
25181
25613
|
const flatten = flattenObject({
|
|
@@ -25193,7 +25625,6 @@ const LocalazyTransferUploadService = ({ strapi: strapi2 }) => ({
|
|
|
25193
25625
|
...characterLimitsMetadata
|
|
25194
25626
|
};
|
|
25195
25627
|
}
|
|
25196
|
-
const strapiDefaultLocaleCode = await StrapiI18nService2.getDefaultLocaleCode();
|
|
25197
25628
|
const localazyLocaleCode = strapiDefaultLocaleCode ? isoStrapiToLocalazy(strapiDefaultLocaleCode) : config.default.LOCALAZY_DEFAULT_LOCALE;
|
|
25198
25629
|
const importFile = LocalazyUploadService2.createImportFileRepresentation(
|
|
25199
25630
|
localazyLocaleCode,
|
|
@@ -25215,6 +25646,10 @@ const LocalazyTransferUploadService = ({ strapi: strapi2 }) => ({
|
|
|
25215
25646
|
await activityLogsService.addEntry(sessionId, "Uploading collections to Localazy...");
|
|
25216
25647
|
try {
|
|
25217
25648
|
await LocalazyUploadService2.upload(importFile, uploadConfig);
|
|
25649
|
+
await activityLogsService.recordAttemptedEntries(
|
|
25650
|
+
sessionId,
|
|
25651
|
+
attemptedTuples.map((t3) => ({ ...t3, status: "success" }))
|
|
25652
|
+
);
|
|
25218
25653
|
} catch (error) {
|
|
25219
25654
|
success = false;
|
|
25220
25655
|
const errorMessage = `Upload to Localazy failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -25224,6 +25659,10 @@ const LocalazyTransferUploadService = ({ strapi: strapi2 }) => ({
|
|
|
25224
25659
|
message: errorMessage
|
|
25225
25660
|
});
|
|
25226
25661
|
await activityLogsService.addEntry(sessionId, errorMessage);
|
|
25662
|
+
await activityLogsService.recordAttemptedEntries(
|
|
25663
|
+
sessionId,
|
|
25664
|
+
attemptedTuples.map((t3) => ({ ...t3, status: "failed", errorMessage }))
|
|
25665
|
+
);
|
|
25227
25666
|
await activityLogsService.finishSession(sessionId, "failed", errorMessage);
|
|
25228
25667
|
console.timeEnd("upload");
|
|
25229
25668
|
return;
|
|
@@ -25948,6 +26387,12 @@ const LocalazyTransferDownloadService = ({ strapi: strapi2 }) => ({
|
|
|
25948
26387
|
message
|
|
25949
26388
|
});
|
|
25950
26389
|
await activityLogsService.addEntry(sessionId, message);
|
|
26390
|
+
await activityLogsService.recordAttemptedEntry(sessionId, {
|
|
26391
|
+
uid,
|
|
26392
|
+
documentId,
|
|
26393
|
+
locale: isoStrapi,
|
|
26394
|
+
status: "success"
|
|
26395
|
+
});
|
|
25951
26396
|
await markKeysProcessed(sourceKeys);
|
|
25952
26397
|
} catch (e2) {
|
|
25953
26398
|
success = false;
|
|
@@ -25964,6 +26409,13 @@ const LocalazyTransferDownloadService = ({ strapi: strapi2 }) => ({
|
|
|
25964
26409
|
sessionId,
|
|
25965
26410
|
`Failed to create ${uid} [${documentId}] in ${isoStrapi}: ${e2.message}`
|
|
25966
26411
|
);
|
|
26412
|
+
await activityLogsService.recordAttemptedEntry(sessionId, {
|
|
26413
|
+
uid,
|
|
26414
|
+
documentId,
|
|
26415
|
+
locale: isoStrapi,
|
|
26416
|
+
status: "failed",
|
|
26417
|
+
errorMessage: e2.message
|
|
26418
|
+
});
|
|
25967
26419
|
}
|
|
25968
26420
|
} else {
|
|
25969
26421
|
try {
|
|
@@ -25982,6 +26434,12 @@ const LocalazyTransferDownloadService = ({ strapi: strapi2 }) => ({
|
|
|
25982
26434
|
message
|
|
25983
26435
|
});
|
|
25984
26436
|
await activityLogsService.addEntry(sessionId, message);
|
|
26437
|
+
await activityLogsService.recordAttemptedEntry(sessionId, {
|
|
26438
|
+
uid,
|
|
26439
|
+
documentId,
|
|
26440
|
+
locale: isoStrapi,
|
|
26441
|
+
status: "success"
|
|
26442
|
+
});
|
|
25985
26443
|
await markKeysProcessed(sourceKeys);
|
|
25986
26444
|
} catch (e2) {
|
|
25987
26445
|
success = false;
|
|
@@ -25998,6 +26456,13 @@ const LocalazyTransferDownloadService = ({ strapi: strapi2 }) => ({
|
|
|
25998
26456
|
sessionId,
|
|
25999
26457
|
`Failed to update ${uid} [${documentId}] in ${isoStrapi}: ${e2.message}`
|
|
26000
26458
|
);
|
|
26459
|
+
await activityLogsService.recordAttemptedEntry(sessionId, {
|
|
26460
|
+
uid,
|
|
26461
|
+
documentId,
|
|
26462
|
+
locale: isoStrapi,
|
|
26463
|
+
status: "failed",
|
|
26464
|
+
errorMessage: e2.message
|
|
26465
|
+
});
|
|
26001
26466
|
}
|
|
26002
26467
|
}
|
|
26003
26468
|
} catch (e2) {
|
|
@@ -26010,6 +26475,13 @@ const LocalazyTransferDownloadService = ({ strapi: strapi2 }) => ({
|
|
|
26010
26475
|
sessionId,
|
|
26011
26476
|
`Error processing ${uid} [${documentId}] in ${isoStrapi}: ${e2.message}`
|
|
26012
26477
|
);
|
|
26478
|
+
await activityLogsService.recordAttemptedEntry(sessionId, {
|
|
26479
|
+
uid,
|
|
26480
|
+
documentId,
|
|
26481
|
+
locale: isoStrapi,
|
|
26482
|
+
status: "failed",
|
|
26483
|
+
errorMessage: e2.message
|
|
26484
|
+
});
|
|
26013
26485
|
}
|
|
26014
26486
|
}
|
|
26015
26487
|
}
|
|
@@ -26760,6 +27232,7 @@ const emptyActivityLogs = {
|
|
|
26760
27232
|
sessions: []
|
|
26761
27233
|
};
|
|
26762
27234
|
const ONE_YEAR_MS = 365 * 24 * 60 * 60 * 1e3;
|
|
27235
|
+
const ATTEMPTED_ENTRIES_CAP = 5e3;
|
|
26763
27236
|
let writeLock = Promise.resolve();
|
|
26764
27237
|
const withLock = async (fn) => {
|
|
26765
27238
|
let release;
|
|
@@ -26824,6 +27297,38 @@ const ActivityLogsService = ({ strapi: strapi2 }) => ({
|
|
|
26824
27297
|
await this.saveActivityLogs(logs);
|
|
26825
27298
|
});
|
|
26826
27299
|
},
|
|
27300
|
+
async recordAttemptedEntry(sessionId, entry) {
|
|
27301
|
+
return this.recordAttemptedEntries(sessionId, [entry]);
|
|
27302
|
+
},
|
|
27303
|
+
async recordAttemptedEntries(sessionId, entries) {
|
|
27304
|
+
if (!entries.length) {
|
|
27305
|
+
return;
|
|
27306
|
+
}
|
|
27307
|
+
return withLock(async () => {
|
|
27308
|
+
const logs = await this.getActivityLogs();
|
|
27309
|
+
const session = logs.sessions.find((s2) => s2.id === sessionId);
|
|
27310
|
+
if (!session) {
|
|
27311
|
+
return;
|
|
27312
|
+
}
|
|
27313
|
+
if (!session.attemptedEntries) {
|
|
27314
|
+
session.attemptedEntries = [];
|
|
27315
|
+
}
|
|
27316
|
+
const now = Date.now();
|
|
27317
|
+
const wasAtCap = session.attemptedEntries.length >= ATTEMPTED_ENTRIES_CAP;
|
|
27318
|
+
const remaining = Math.max(0, ATTEMPTED_ENTRIES_CAP - session.attemptedEntries.length);
|
|
27319
|
+
const accepted = entries.slice(0, remaining);
|
|
27320
|
+
const dropped = entries.length - accepted.length;
|
|
27321
|
+
for (const entry of accepted) {
|
|
27322
|
+
session.attemptedEntries.push({ ...entry, attemptedAt: now });
|
|
27323
|
+
}
|
|
27324
|
+
if (dropped > 0 && !wasAtCap) {
|
|
27325
|
+
strapi2.log.warn(
|
|
27326
|
+
`[localazy] attemptedEntries cap (${ATTEMPTED_ENTRIES_CAP}) reached for session ${sessionId}; ${dropped} record(s) dropped.`
|
|
27327
|
+
);
|
|
27328
|
+
}
|
|
27329
|
+
await this.saveActivityLogs(logs);
|
|
27330
|
+
});
|
|
27331
|
+
},
|
|
26827
27332
|
async finishSession(sessionId, status, summary) {
|
|
26828
27333
|
return withLock(async () => {
|
|
26829
27334
|
const logs = await this.getActivityLogs();
|