@localazy/strapi-plugin 1.3.0 → 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-CtBRO-Cn.js → FetchIdentity-BpFOT163.js} +2 -2
- package/dist/_chunks/{FetchIdentity-BJGoZPpM.mjs → FetchIdentity-Cor4ooZv.mjs} +2 -2
- package/dist/_chunks/{LanguagesSelector-DJxgCwgM.mjs → LanguagesSelector-BbrMKwQY.mjs} +3 -3
- package/dist/_chunks/{LanguagesSelector-CNaKrS-m.js → LanguagesSelector-CC1ecWnc.js} +3 -3
- package/dist/_chunks/{LocalazyApp-B1qCCEPe.js → LocalazyApp-D0JGRQeF.js} +85 -8
- package/dist/_chunks/{LocalazyApp-CJRpsTKr.mjs → LocalazyApp-DpFR8p10.mjs} +86 -9
- package/dist/_chunks/{LocalazyContentTransferSetup-CCxiLHdG.js → LocalazyContentTransferSetup-Cr7D-BUi.js} +5 -5
- package/dist/_chunks/{LocalazyContentTransferSetup-CLAWn95U.mjs → LocalazyContentTransferSetup-Dael1Q4w.mjs} +5 -5
- package/dist/_chunks/{LocalazyGlobalSettings-BJ5qfFfK.js → LocalazyGlobalSettings-D3nG4zl7.js} +6 -6
- package/dist/_chunks/{LocalazyGlobalSettings-Z-4Ykc_J.mjs → LocalazyGlobalSettings-DzBI5M-K.mjs} +6 -6
- package/dist/_chunks/{LocalazyPanel-DE3TtHOQ.js → LocalazyPanel-KQPLxTDz.js} +3 -3
- package/dist/_chunks/{LocalazyPanel-CL_mzzfD.mjs → LocalazyPanel-W6j8N1WC.mjs} +3 -3
- package/dist/_chunks/{LocalazyStatusColumn-BMLcklG-.js → LocalazyStatusColumn-B7Q_U2Lu.js} +2 -2
- package/dist/_chunks/{LocalazyStatusColumn-R5n6q_cx.mjs → LocalazyStatusColumn-CrhsCjgp.mjs} +2 -2
- package/dist/_chunks/{RequireLocalazyAuth-Dzsu6UNS.mjs → RequireLocalazyAuth-BwoGRfJq.mjs} +4 -4
- package/dist/_chunks/{RequireLocalazyAuth-WKKFdQlR.js → RequireLocalazyAuth-D3GeZPDh.js} +4 -4
- package/dist/_chunks/{entry-exclusion-service-CggmAKxq.js → entry-exclusion-service-Bwruvyce.js} +1 -1
- package/dist/_chunks/{entry-exclusion-service-DxqTlZFR.mjs → entry-exclusion-service-D7gaRHPE.mjs} +1 -1
- package/dist/_chunks/{has-model-changed-BR53L6pA.js → has-model-changed-5IV9WI_-.js} +2 -2
- package/dist/_chunks/{has-model-changed-FMGVcFHU.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-toauQlIK.mjs → index-C9hN3KbD.mjs} +8 -8
- package/dist/_chunks/{index-BK7lmZSN.js → index-CTkxoioB.js} +1 -0
- package/dist/_chunks/{index-BmxGKspv.js → index-jAg6jjCu.js} +8 -8
- package/dist/_chunks/{strapi-api-base-BflplVsH.mjs → strapi-api-base-CAzGcwF6.mjs} +1 -1
- package/dist/_chunks/{strapi-api-base-CT9lknwW.js → strapi-api-base-IG3Pe5Sg.js} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +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 -4
- package/dist/server/index.mjs +510 -5
- 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 -1
package/dist/server/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import require$$0$4 from "http";
|
|
2
|
-
import require$$1$4 from "fs";
|
|
2
|
+
import require$$1$4, { promises } from "fs";
|
|
3
3
|
import require$$1$1 from "zlib";
|
|
4
4
|
import require$$1 from "path";
|
|
5
5
|
import require$$4 from "stream";
|
|
@@ -9,6 +9,7 @@ import require$$0$2 from "crypto";
|
|
|
9
9
|
import require$$0$3 from "events";
|
|
10
10
|
import require$$2 from "timers";
|
|
11
11
|
import require$$8, { WebSocketServer } from "ws";
|
|
12
|
+
import JSZip from "jszip";
|
|
12
13
|
var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
|
|
13
14
|
function getDefaultExportFromCjs(x2) {
|
|
14
15
|
return x2 && x2.__esModule && Object.prototype.hasOwnProperty.call(x2, "default") ? x2["default"] : x2;
|
|
@@ -20519,7 +20520,7 @@ var distExports = dist$1.exports;
|
|
|
20519
20520
|
const io = /* @__PURE__ */ getDefaultExportFromCjs(distExports);
|
|
20520
20521
|
const { Server: Server2, Namespace, Socket: Socket3 } = io;
|
|
20521
20522
|
const name = "@localazy/strapi-plugin";
|
|
20522
|
-
const version = "1.
|
|
20523
|
+
const version = "1.4.0";
|
|
20523
20524
|
const description = "The official Strapi Plugin by Localazy.";
|
|
20524
20525
|
const keywords = [
|
|
20525
20526
|
"strapi",
|
|
@@ -20589,6 +20590,7 @@ const scripts = {
|
|
|
20589
20590
|
prepare: "husky || true"
|
|
20590
20591
|
};
|
|
20591
20592
|
const dependencies = {
|
|
20593
|
+
jszip: "^3.10.1",
|
|
20592
20594
|
shelljs: "^0.8.5",
|
|
20593
20595
|
ws: "^8.18.0"
|
|
20594
20596
|
};
|
|
@@ -24488,6 +24490,413 @@ const ActivityLogsController = ({ strapi: _strapi }) => ({
|
|
|
24488
24490
|
ctx.body = { success: true };
|
|
24489
24491
|
}
|
|
24490
24492
|
});
|
|
24493
|
+
const URL_SECRET_KEYS = ["token", "secret", "signature", "key", "apikey", "api_key"];
|
|
24494
|
+
const DEBUG_BUNDLE_ENV_ALLOWLIST = [
|
|
24495
|
+
"STRAPI_ADMIN_LOCALAZY_ENV",
|
|
24496
|
+
"STRAPI_ADMIN_BACKEND_URL",
|
|
24497
|
+
"NODE_ENV",
|
|
24498
|
+
"STRAPI_TELEMETRY_DISABLED"
|
|
24499
|
+
];
|
|
24500
|
+
const redactIdentity = (identity2) => {
|
|
24501
|
+
if (!identity2) return null;
|
|
24502
|
+
return {
|
|
24503
|
+
userId: identity2.user?.id ?? "",
|
|
24504
|
+
name: identity2.user?.name ?? "",
|
|
24505
|
+
scope: identity2.scope ?? "",
|
|
24506
|
+
hasAccessToken: Boolean(identity2.accessToken)
|
|
24507
|
+
};
|
|
24508
|
+
};
|
|
24509
|
+
const scrubUrlSecrets = (input) => {
|
|
24510
|
+
if (typeof input !== "string") return input;
|
|
24511
|
+
if (!/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(input) && !input.includes("?")) return input;
|
|
24512
|
+
try {
|
|
24513
|
+
const url2 = new URL(input);
|
|
24514
|
+
let mutated = false;
|
|
24515
|
+
for (const k2 of [...url2.searchParams.keys()]) {
|
|
24516
|
+
if (URL_SECRET_KEYS.includes(k2.toLowerCase())) {
|
|
24517
|
+
url2.searchParams.delete(k2);
|
|
24518
|
+
mutated = true;
|
|
24519
|
+
}
|
|
24520
|
+
}
|
|
24521
|
+
return mutated ? url2.toString() : input;
|
|
24522
|
+
} catch {
|
|
24523
|
+
return input;
|
|
24524
|
+
}
|
|
24525
|
+
};
|
|
24526
|
+
const pickEnvAllowlist = (env) => {
|
|
24527
|
+
const out = {};
|
|
24528
|
+
for (const key of DEBUG_BUNDLE_ENV_ALLOWLIST) {
|
|
24529
|
+
const raw = env[key];
|
|
24530
|
+
const present = raw !== void 0;
|
|
24531
|
+
out[key] = {
|
|
24532
|
+
present,
|
|
24533
|
+
value: present ? raw : null
|
|
24534
|
+
};
|
|
24535
|
+
}
|
|
24536
|
+
return out;
|
|
24537
|
+
};
|
|
24538
|
+
const serializeContentTypeSchema = (contentType) => {
|
|
24539
|
+
if (!contentType || typeof contentType !== "object") return null;
|
|
24540
|
+
const ct = contentType;
|
|
24541
|
+
return {
|
|
24542
|
+
kind: ct.kind,
|
|
24543
|
+
collectionName: ct.collectionName,
|
|
24544
|
+
info: ct.info,
|
|
24545
|
+
options: ct.options,
|
|
24546
|
+
pluginOptions: ct.pluginOptions,
|
|
24547
|
+
attributes: ct.attributes
|
|
24548
|
+
};
|
|
24549
|
+
};
|
|
24550
|
+
const DOCUMENT_INTERNAL_FIELDS = ["createdBy", "updatedBy"];
|
|
24551
|
+
const stripDocumentInternals = (doc) => {
|
|
24552
|
+
if (!doc || typeof doc !== "object") return doc;
|
|
24553
|
+
const clone = { ...doc };
|
|
24554
|
+
for (const field of DOCUMENT_INTERNAL_FIELDS) {
|
|
24555
|
+
delete clone[field];
|
|
24556
|
+
}
|
|
24557
|
+
return clone;
|
|
24558
|
+
};
|
|
24559
|
+
class SessionNotFoundError extends Error {
|
|
24560
|
+
constructor(sessionId) {
|
|
24561
|
+
super(`Session ${sessionId} not found`);
|
|
24562
|
+
this.name = "SessionNotFoundError";
|
|
24563
|
+
}
|
|
24564
|
+
}
|
|
24565
|
+
const DEBUG_BUNDLE_SCHEMA_VERSION = 1;
|
|
24566
|
+
const CAPS = {
|
|
24567
|
+
perEntryBytes: 1 * 1024 * 1024,
|
|
24568
|
+
// 1 MB
|
|
24569
|
+
totalEntries: 100,
|
|
24570
|
+
errorLogBytes: 2 * 1024 * 1024,
|
|
24571
|
+
// 2 MB
|
|
24572
|
+
consoleErrorsLines: 200,
|
|
24573
|
+
browserPayloadBytes: 64 * 1024
|
|
24574
|
+
// 64 KB
|
|
24575
|
+
};
|
|
24576
|
+
const selectAttemptedEntries = (attempted) => {
|
|
24577
|
+
const failedByKey = /* @__PURE__ */ new Map();
|
|
24578
|
+
const successByKey = /* @__PURE__ */ new Map();
|
|
24579
|
+
for (const record of attempted) {
|
|
24580
|
+
const key = `${record.uid}|${record.documentId}|${record.locale}`;
|
|
24581
|
+
const selected = {
|
|
24582
|
+
uid: record.uid,
|
|
24583
|
+
documentId: record.documentId,
|
|
24584
|
+
locale: record.locale,
|
|
24585
|
+
status: record.status,
|
|
24586
|
+
attemptedAt: record.attemptedAt
|
|
24587
|
+
};
|
|
24588
|
+
if (record.status === "failed") {
|
|
24589
|
+
successByKey.delete(key);
|
|
24590
|
+
if (!failedByKey.has(key)) failedByKey.set(key, selected);
|
|
24591
|
+
} else if (!failedByKey.has(key) && !successByKey.has(key)) {
|
|
24592
|
+
successByKey.set(key, selected);
|
|
24593
|
+
}
|
|
24594
|
+
}
|
|
24595
|
+
return [...failedByKey.values(), ...successByKey.values()];
|
|
24596
|
+
};
|
|
24597
|
+
const formatTimestampForFilename = (ms2) => new Date(ms2).toISOString().replace(/[:.]/g, "-");
|
|
24598
|
+
const safeUidForPath = (uid) => uid.replace(/::/g, "--").replace(/\./g, "-");
|
|
24599
|
+
const decodeBrowserPayload = (encoded) => {
|
|
24600
|
+
const markers = [];
|
|
24601
|
+
if (!encoded) return { payload: null, truncationMarkers: markers };
|
|
24602
|
+
try {
|
|
24603
|
+
const raw = Buffer.from(encoded, "base64").toString("utf8");
|
|
24604
|
+
const rawBytes = Buffer.byteLength(raw, "utf8");
|
|
24605
|
+
if (rawBytes > CAPS.browserPayloadBytes) {
|
|
24606
|
+
markers.push({ kind: "browser-payload-truncated", originalSizeBytes: rawBytes });
|
|
24607
|
+
return { payload: null, truncationMarkers: markers };
|
|
24608
|
+
}
|
|
24609
|
+
const parsed = JSON.parse(raw);
|
|
24610
|
+
if (parsed && Array.isArray(parsed.consoleErrors) && parsed.consoleErrors.length > CAPS.consoleErrorsLines) {
|
|
24611
|
+
markers.push({ kind: "console-errors-truncated", originalCount: parsed.consoleErrors.length });
|
|
24612
|
+
parsed.consoleErrors = parsed.consoleErrors.slice(0, CAPS.consoleErrorsLines);
|
|
24613
|
+
}
|
|
24614
|
+
return { payload: parsed, truncationMarkers: markers };
|
|
24615
|
+
} catch {
|
|
24616
|
+
return { payload: null, truncationMarkers: markers };
|
|
24617
|
+
}
|
|
24618
|
+
};
|
|
24619
|
+
const deepScrubUrls = (value) => {
|
|
24620
|
+
if (value == null) return value;
|
|
24621
|
+
if (typeof value === "string") return scrubUrlSecrets(value);
|
|
24622
|
+
if (Array.isArray(value)) return value.map(deepScrubUrls);
|
|
24623
|
+
if (typeof value === "object") {
|
|
24624
|
+
const out = {};
|
|
24625
|
+
for (const [k2, v2] of Object.entries(value)) {
|
|
24626
|
+
out[k2] = deepScrubUrls(v2);
|
|
24627
|
+
}
|
|
24628
|
+
return out;
|
|
24629
|
+
}
|
|
24630
|
+
return value;
|
|
24631
|
+
};
|
|
24632
|
+
const sanitizePluginSettings = (settings) => {
|
|
24633
|
+
const cloned = JSON.parse(JSON.stringify(settings ?? {}));
|
|
24634
|
+
if (cloned.webhookConfig?.url) {
|
|
24635
|
+
cloned.webhookConfig.url = scrubUrlSecrets(cloned.webhookConfig.url);
|
|
24636
|
+
}
|
|
24637
|
+
return cloned;
|
|
24638
|
+
};
|
|
24639
|
+
const sanitizeSession = (session) => {
|
|
24640
|
+
const cloned = JSON.parse(JSON.stringify(session));
|
|
24641
|
+
for (const entry of cloned.entries ?? []) {
|
|
24642
|
+
if (entry.data !== void 0) {
|
|
24643
|
+
entry.data = deepScrubUrls(entry.data);
|
|
24644
|
+
}
|
|
24645
|
+
}
|
|
24646
|
+
return cloned;
|
|
24647
|
+
};
|
|
24648
|
+
const buildErrorLog = (session) => {
|
|
24649
|
+
const lines = [];
|
|
24650
|
+
for (const entry of session.entries ?? []) {
|
|
24651
|
+
const ts = Number.isFinite(entry.timestamp) ? new Date(entry.timestamp).toISOString() : "unknown";
|
|
24652
|
+
lines.push(`${ts} ${entry.message}`);
|
|
24653
|
+
}
|
|
24654
|
+
if (session.status === "failed" && session.summary) {
|
|
24655
|
+
lines.push(`ERROR: ${session.summary}`);
|
|
24656
|
+
}
|
|
24657
|
+
const full = lines.join("\n");
|
|
24658
|
+
const fullBytes = Buffer.byteLength(full, "utf8");
|
|
24659
|
+
if (fullBytes <= CAPS.errorLogBytes) {
|
|
24660
|
+
return { text: full, markers: [] };
|
|
24661
|
+
}
|
|
24662
|
+
const truncated = Buffer.from(full, "utf8").subarray(fullBytes - CAPS.errorLogBytes).toString("utf8");
|
|
24663
|
+
return {
|
|
24664
|
+
text: `...[truncated, kept last ${CAPS.errorLogBytes} bytes]
|
|
24665
|
+
${truncated}`,
|
|
24666
|
+
markers: [{ kind: "error-log-truncated", originalSizeBytes: fullBytes }]
|
|
24667
|
+
};
|
|
24668
|
+
};
|
|
24669
|
+
const readHostPackages = async (strapi2) => {
|
|
24670
|
+
const appRoot = strapi2?.dirs?.app?.root ?? process.cwd();
|
|
24671
|
+
if (!appRoot) return { data: null, reason: "host-app-root-unknown" };
|
|
24672
|
+
try {
|
|
24673
|
+
const raw = await promises.readFile(require$$1.join(appRoot, "package.json"), "utf8");
|
|
24674
|
+
const parsed = JSON.parse(raw);
|
|
24675
|
+
return {
|
|
24676
|
+
data: {
|
|
24677
|
+
name: parsed.name,
|
|
24678
|
+
version: parsed.version,
|
|
24679
|
+
dependencies: parsed.dependencies ?? {},
|
|
24680
|
+
devDependencies: parsed.devDependencies ?? {}
|
|
24681
|
+
}
|
|
24682
|
+
};
|
|
24683
|
+
} catch (err) {
|
|
24684
|
+
return { data: null, reason: err instanceof Error ? err.message : String(err) };
|
|
24685
|
+
}
|
|
24686
|
+
};
|
|
24687
|
+
const buildReadme = ({
|
|
24688
|
+
sessionShortId,
|
|
24689
|
+
sessionId,
|
|
24690
|
+
schemaVersion,
|
|
24691
|
+
hostPackagesSkipped
|
|
24692
|
+
}) => {
|
|
24693
|
+
const lines = [];
|
|
24694
|
+
lines.push(`# Localazy Strapi Plugin — Troubleshooting Bundle`);
|
|
24695
|
+
lines.push("");
|
|
24696
|
+
lines.push(`Generated: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
24697
|
+
lines.push(`Session id: ${sessionId}`);
|
|
24698
|
+
lines.push(`Schema version: ${schemaVersion}`);
|
|
24699
|
+
lines.push("");
|
|
24700
|
+
lines.push("## Contents");
|
|
24701
|
+
lines.push("");
|
|
24702
|
+
lines.push("- `meta.json` — schema version, session metadata.");
|
|
24703
|
+
lines.push("- `versions.json` — plugin version, Strapi version, Node version, platform/arch.");
|
|
24704
|
+
lines.push("- `env.json` — allow-listed environment variables only.");
|
|
24705
|
+
lines.push("- `browser.json` — user agent, language, viewport, console errors (from the admin UI).");
|
|
24706
|
+
lines.push("- `session.json` — full activity-log session record (URLs scrubbed of secret query keys).");
|
|
24707
|
+
lines.push("- `error.log` — chronological text log of session entries.");
|
|
24708
|
+
lines.push("- `plugin-settings.json` — full plugin settings (webhook URL secrets scrubbed).");
|
|
24709
|
+
lines.push("- `content-transfer-setup.json` — content-transfer setup record.");
|
|
24710
|
+
if (hostPackagesSkipped) {
|
|
24711
|
+
lines.push("- `host-packages.json` — NOT INCLUDED (could not be read; see `bundle-truncated.json`).");
|
|
24712
|
+
} else {
|
|
24713
|
+
lines.push("- `host-packages.json` — host project's package.json (deps + devDeps only).");
|
|
24714
|
+
}
|
|
24715
|
+
lines.push("- `content-types/` — schema.json per touched content-type UID.");
|
|
24716
|
+
lines.push("- `entries/<uid>/<documentId>--<locale>.json` — per-entry payload from `strapi.documents().findOne()`.");
|
|
24717
|
+
lines.push("- `bundle-truncated.json` — present only if any per-section cap was exceeded.");
|
|
24718
|
+
lines.push("");
|
|
24719
|
+
lines.push("## Reproducing locally");
|
|
24720
|
+
lines.push("");
|
|
24721
|
+
lines.push("1. `unzip` the bundle.");
|
|
24722
|
+
lines.push("2. Recreate the content types from `content-types/*.schema.json` in your Strapi project.");
|
|
24723
|
+
lines.push("3. Import the entries via the Document Service using `entries/<uid>/*.json`.");
|
|
24724
|
+
lines.push("4. Apply `plugin-settings.json` and `content-transfer-setup.json` via the plugin admin UI.");
|
|
24725
|
+
lines.push("5. Trigger the same upload/download/webhook with the same locale set.");
|
|
24726
|
+
lines.push("6. Compare the failure against `error.log` and `session.json`.");
|
|
24727
|
+
lines.push("");
|
|
24728
|
+
lines.push(`Bundle short id: ${sessionShortId}`);
|
|
24729
|
+
return lines.join("\n");
|
|
24730
|
+
};
|
|
24731
|
+
const buildDebugBundle = async ({
|
|
24732
|
+
strapi: strapi2,
|
|
24733
|
+
sessionId,
|
|
24734
|
+
browserPayloadEncoded
|
|
24735
|
+
}) => {
|
|
24736
|
+
const zip = new JSZip();
|
|
24737
|
+
const truncationMarkers = [];
|
|
24738
|
+
const pluginName = "localazy";
|
|
24739
|
+
const activityLogsService = strapi2.plugin(pluginName).service("ActivityLogsService");
|
|
24740
|
+
const pluginSettingsService = strapi2.plugin(pluginName).service("PluginSettingsService");
|
|
24741
|
+
const strapiService = strapi2.plugin(pluginName).service("StrapiService");
|
|
24742
|
+
const localazyUserService = strapi2.plugin(pluginName).service("LocalazyUserService");
|
|
24743
|
+
const session = await activityLogsService.getSession(sessionId);
|
|
24744
|
+
if (!session) {
|
|
24745
|
+
throw new SessionNotFoundError(sessionId);
|
|
24746
|
+
}
|
|
24747
|
+
const sessionShortId = session.id.slice(0, 8);
|
|
24748
|
+
const pluginSettingsRaw = await pluginSettingsService.getPluginSettings();
|
|
24749
|
+
const contentTransferSetup = await pluginSettingsService.getContentTransferSetup();
|
|
24750
|
+
const identity2 = await localazyUserService.getUser();
|
|
24751
|
+
const redactedIdentity = redactIdentity(identity2);
|
|
24752
|
+
const pluginVersion = await strapiService.getPluginVersion();
|
|
24753
|
+
const strapiVersion = strapiService.getStrapiVersion();
|
|
24754
|
+
zip.file(
|
|
24755
|
+
"meta.json",
|
|
24756
|
+
JSON.stringify(
|
|
24757
|
+
{
|
|
24758
|
+
schemaVersion: DEBUG_BUNDLE_SCHEMA_VERSION,
|
|
24759
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24760
|
+
sessionId: session.id,
|
|
24761
|
+
sessionShortId,
|
|
24762
|
+
eventType: session.eventType,
|
|
24763
|
+
status: session.status,
|
|
24764
|
+
startedAt: session.startedAt,
|
|
24765
|
+
finishedAt: session.finishedAt ?? null,
|
|
24766
|
+
initiatedBy: session.initiatedBy,
|
|
24767
|
+
identity: redactedIdentity
|
|
24768
|
+
},
|
|
24769
|
+
null,
|
|
24770
|
+
2
|
|
24771
|
+
)
|
|
24772
|
+
);
|
|
24773
|
+
zip.file(
|
|
24774
|
+
"versions.json",
|
|
24775
|
+
JSON.stringify(
|
|
24776
|
+
{
|
|
24777
|
+
plugin: { name: "@localazy/strapi-plugin", version: pluginVersion },
|
|
24778
|
+
strapi: { version: strapiVersion },
|
|
24779
|
+
node: process.version,
|
|
24780
|
+
platform: process.platform,
|
|
24781
|
+
arch: process.arch
|
|
24782
|
+
},
|
|
24783
|
+
null,
|
|
24784
|
+
2
|
|
24785
|
+
)
|
|
24786
|
+
);
|
|
24787
|
+
const envSnapshot = pickEnvAllowlist(process.env);
|
|
24788
|
+
zip.file(
|
|
24789
|
+
"env.json",
|
|
24790
|
+
JSON.stringify(
|
|
24791
|
+
{
|
|
24792
|
+
allowlist: [...DEBUG_BUNDLE_ENV_ALLOWLIST],
|
|
24793
|
+
values: envSnapshot
|
|
24794
|
+
},
|
|
24795
|
+
null,
|
|
24796
|
+
2
|
|
24797
|
+
)
|
|
24798
|
+
);
|
|
24799
|
+
const { payload: browserPayload, truncationMarkers: browserMarkers } = decodeBrowserPayload(browserPayloadEncoded);
|
|
24800
|
+
truncationMarkers.push(...browserMarkers);
|
|
24801
|
+
zip.file("browser.json", JSON.stringify(browserPayload ? browserPayload : { captured: false }, null, 2));
|
|
24802
|
+
zip.file("session.json", JSON.stringify(sanitizeSession(session), null, 2));
|
|
24803
|
+
const { text: errorLogText, markers: errorLogMarkers } = buildErrorLog(session);
|
|
24804
|
+
truncationMarkers.push(...errorLogMarkers);
|
|
24805
|
+
zip.file("error.log", errorLogText);
|
|
24806
|
+
zip.file("plugin-settings.json", JSON.stringify(sanitizePluginSettings(pluginSettingsRaw), null, 2));
|
|
24807
|
+
zip.file("content-transfer-setup.json", JSON.stringify(contentTransferSetup, null, 2));
|
|
24808
|
+
const hostPackages = await readHostPackages(strapi2);
|
|
24809
|
+
if (hostPackages.data) {
|
|
24810
|
+
zip.file("host-packages.json", JSON.stringify(hostPackages.data, null, 2));
|
|
24811
|
+
}
|
|
24812
|
+
if (session.attemptedEntries === void 0) {
|
|
24813
|
+
truncationMarkers.push({ kind: "attempted-entries-missing-legacy-session" });
|
|
24814
|
+
}
|
|
24815
|
+
const selected = selectAttemptedEntries(session.attemptedEntries ?? []);
|
|
24816
|
+
const kept = selected.slice(0, CAPS.totalEntries);
|
|
24817
|
+
const skipped = selected.slice(CAPS.totalEntries);
|
|
24818
|
+
if (skipped.length > 0) {
|
|
24819
|
+
truncationMarkers.push({
|
|
24820
|
+
kind: "entries-skipped",
|
|
24821
|
+
tuples: skipped.map(({ uid, documentId, locale }) => ({ uid, documentId, locale }))
|
|
24822
|
+
});
|
|
24823
|
+
}
|
|
24824
|
+
const uniqueUids = [...new Set(kept.map((t3) => t3.uid))];
|
|
24825
|
+
for (const uid of uniqueUids) {
|
|
24826
|
+
const schema = serializeContentTypeSchema(strapi2.contentTypes?.[uid]);
|
|
24827
|
+
if (schema) {
|
|
24828
|
+
zip.file(`content-types/${safeUidForPath(uid)}.schema.json`, JSON.stringify(schema, null, 2));
|
|
24829
|
+
}
|
|
24830
|
+
}
|
|
24831
|
+
for (const tuple of kept) {
|
|
24832
|
+
let documentJson = null;
|
|
24833
|
+
try {
|
|
24834
|
+
const doc = await strapi2.documents(tuple.uid).findOne({ documentId: tuple.documentId, locale: tuple.locale, populate: "*" });
|
|
24835
|
+
documentJson = doc ? stripDocumentInternals(doc) : { __not_found: true, attemptedAt: tuple.attemptedAt };
|
|
24836
|
+
} catch (err) {
|
|
24837
|
+
documentJson = { __error: err instanceof Error ? err.message : String(err) };
|
|
24838
|
+
}
|
|
24839
|
+
const serialised = JSON.stringify(documentJson, null, 2);
|
|
24840
|
+
const sizeBytes = Buffer.byteLength(serialised, "utf8");
|
|
24841
|
+
let body;
|
|
24842
|
+
if (sizeBytes > CAPS.perEntryBytes) {
|
|
24843
|
+
truncationMarkers.push({
|
|
24844
|
+
kind: "entry-too-large",
|
|
24845
|
+
uid: tuple.uid,
|
|
24846
|
+
documentId: tuple.documentId,
|
|
24847
|
+
locale: tuple.locale,
|
|
24848
|
+
originalSizeBytes: sizeBytes
|
|
24849
|
+
});
|
|
24850
|
+
body = JSON.stringify({ __truncated: true, originalSizeBytes: sizeBytes }, null, 2);
|
|
24851
|
+
} else {
|
|
24852
|
+
body = serialised;
|
|
24853
|
+
}
|
|
24854
|
+
const safeDocId = tuple.documentId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
24855
|
+
zip.file(`entries/${safeUidForPath(tuple.uid)}/${safeDocId}--${tuple.locale}.json`, body);
|
|
24856
|
+
}
|
|
24857
|
+
zip.file(
|
|
24858
|
+
"README.md",
|
|
24859
|
+
buildReadme({
|
|
24860
|
+
sessionShortId,
|
|
24861
|
+
sessionId: session.id,
|
|
24862
|
+
schemaVersion: DEBUG_BUNDLE_SCHEMA_VERSION,
|
|
24863
|
+
hostPackagesSkipped: !hostPackages.data
|
|
24864
|
+
})
|
|
24865
|
+
);
|
|
24866
|
+
if (truncationMarkers.length > 0 || !hostPackages.data) {
|
|
24867
|
+
const marker = {
|
|
24868
|
+
schemaVersion: DEBUG_BUNDLE_SCHEMA_VERSION,
|
|
24869
|
+
markers: truncationMarkers,
|
|
24870
|
+
hostPackagesSkipped: hostPackages.data ? null : hostPackages.reason ?? "unknown"
|
|
24871
|
+
};
|
|
24872
|
+
zip.file("bundle-truncated.json", JSON.stringify(marker, null, 2));
|
|
24873
|
+
}
|
|
24874
|
+
const zipBuffer = await zip.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
|
|
24875
|
+
const filename = `localazy-debug-bundle-${sessionShortId}-${formatTimestampForFilename(Date.now())}.zip`;
|
|
24876
|
+
return { zipBuffer, filename };
|
|
24877
|
+
};
|
|
24878
|
+
const DebugBundleController = ({ strapi: strapi2 }) => ({
|
|
24879
|
+
async getBundle(ctx) {
|
|
24880
|
+
const { sessionId } = ctx.params;
|
|
24881
|
+
const browserPayloadEncoded = typeof ctx.query.browser === "string" ? ctx.query.browser : null;
|
|
24882
|
+
try {
|
|
24883
|
+
const { zipBuffer, filename } = await buildDebugBundle({
|
|
24884
|
+
strapi: strapi2,
|
|
24885
|
+
sessionId,
|
|
24886
|
+
browserPayloadEncoded
|
|
24887
|
+
});
|
|
24888
|
+
ctx.type = "application/zip";
|
|
24889
|
+
ctx.set("Content-Disposition", `attachment; filename="${filename}"`);
|
|
24890
|
+
ctx.body = zipBuffer;
|
|
24891
|
+
} catch (err) {
|
|
24892
|
+
if (err instanceof SessionNotFoundError) {
|
|
24893
|
+
return ctx.notFound("Session not found");
|
|
24894
|
+
}
|
|
24895
|
+
strapi2.log.error(`[localazy] failed to build debug bundle: ${err instanceof Error ? err.message : err}`);
|
|
24896
|
+
throw err;
|
|
24897
|
+
}
|
|
24898
|
+
}
|
|
24899
|
+
});
|
|
24491
24900
|
const controllers = {
|
|
24492
24901
|
LocalazyUserController,
|
|
24493
24902
|
LocalazyAuthController,
|
|
@@ -24496,7 +24905,8 @@ const controllers = {
|
|
|
24496
24905
|
PluginSettingsController,
|
|
24497
24906
|
LocalazyTransferController,
|
|
24498
24907
|
EntryExclusionController,
|
|
24499
|
-
ActivityLogsController
|
|
24908
|
+
ActivityLogsController,
|
|
24909
|
+
DebugBundleController
|
|
24500
24910
|
};
|
|
24501
24911
|
const verifyWebhook = (config2, { strapi: strapi2 }) => {
|
|
24502
24912
|
return async (ctx, next) => {
|
|
@@ -24807,6 +25217,16 @@ const ActivityLogsRoutes = [
|
|
|
24807
25217
|
}
|
|
24808
25218
|
}
|
|
24809
25219
|
];
|
|
25220
|
+
const DebugBundleRoutes = [
|
|
25221
|
+
{
|
|
25222
|
+
method: "GET",
|
|
25223
|
+
path: "/activity-logs/:sessionId/bundle",
|
|
25224
|
+
handler: "DebugBundleController.getBundle",
|
|
25225
|
+
config: {
|
|
25226
|
+
policies: []
|
|
25227
|
+
}
|
|
25228
|
+
}
|
|
25229
|
+
];
|
|
24810
25230
|
const routes = {
|
|
24811
25231
|
"content-api": {
|
|
24812
25232
|
routes: [...StrapiRoutes]
|
|
@@ -24821,7 +25241,8 @@ const routes = {
|
|
|
24821
25241
|
...LocalazyTransferRoutes,
|
|
24822
25242
|
...LocalazyProjectRoutes,
|
|
24823
25243
|
...EntryExclusionRoutes,
|
|
24824
|
-
...ActivityLogsRoutes
|
|
25244
|
+
...ActivityLogsRoutes,
|
|
25245
|
+
...DebugBundleRoutes
|
|
24825
25246
|
]
|
|
24826
25247
|
},
|
|
24827
25248
|
LocalazyPublicTransferRoutes: {
|
|
@@ -25100,6 +25521,8 @@ const LocalazyTransferUploadService = ({ strapi: strapi2 }) => ({
|
|
|
25100
25521
|
const { setup: setup2 } = contentTransferSetup;
|
|
25101
25522
|
const collectionsNames = getCollectionsNames(setup2);
|
|
25102
25523
|
const models = await StrapiService2.getModels();
|
|
25524
|
+
const strapiDefaultLocaleCode = await StrapiI18nService2.getDefaultLocaleCode();
|
|
25525
|
+
const attemptedTuples = [];
|
|
25103
25526
|
let pickedFlattenStrapiContent = {};
|
|
25104
25527
|
for (const collectionName of collectionsNames) {
|
|
25105
25528
|
const currentModel = models.find((model) => model.collectionName === collectionName);
|
|
@@ -25162,6 +25585,15 @@ const LocalazyTransferUploadService = ({ strapi: strapi2 }) => ({
|
|
|
25162
25585
|
});
|
|
25163
25586
|
await activityLogsService.addEntry(sessionId, message);
|
|
25164
25587
|
strapi2.log.info(message);
|
|
25588
|
+
if (strapiDefaultLocaleCode) {
|
|
25589
|
+
for (const entry of entries) {
|
|
25590
|
+
attemptedTuples.push({
|
|
25591
|
+
uid: modelUid,
|
|
25592
|
+
documentId: entry.documentId,
|
|
25593
|
+
locale: strapiDefaultLocaleCode
|
|
25594
|
+
});
|
|
25595
|
+
}
|
|
25596
|
+
}
|
|
25165
25597
|
entries = omitDeep(entries, ["locale", "createdAt", "updatedAt", "publishedAt"]);
|
|
25166
25598
|
entries.forEach((entry) => {
|
|
25167
25599
|
const flatten = flattenObject({
|
|
@@ -25179,7 +25611,6 @@ const LocalazyTransferUploadService = ({ strapi: strapi2 }) => ({
|
|
|
25179
25611
|
...characterLimitsMetadata
|
|
25180
25612
|
};
|
|
25181
25613
|
}
|
|
25182
|
-
const strapiDefaultLocaleCode = await StrapiI18nService2.getDefaultLocaleCode();
|
|
25183
25614
|
const localazyLocaleCode = strapiDefaultLocaleCode ? isoStrapiToLocalazy(strapiDefaultLocaleCode) : config.default.LOCALAZY_DEFAULT_LOCALE;
|
|
25184
25615
|
const importFile = LocalazyUploadService2.createImportFileRepresentation(
|
|
25185
25616
|
localazyLocaleCode,
|
|
@@ -25201,6 +25632,10 @@ const LocalazyTransferUploadService = ({ strapi: strapi2 }) => ({
|
|
|
25201
25632
|
await activityLogsService.addEntry(sessionId, "Uploading collections to Localazy...");
|
|
25202
25633
|
try {
|
|
25203
25634
|
await LocalazyUploadService2.upload(importFile, uploadConfig);
|
|
25635
|
+
await activityLogsService.recordAttemptedEntries(
|
|
25636
|
+
sessionId,
|
|
25637
|
+
attemptedTuples.map((t3) => ({ ...t3, status: "success" }))
|
|
25638
|
+
);
|
|
25204
25639
|
} catch (error) {
|
|
25205
25640
|
success = false;
|
|
25206
25641
|
const errorMessage = `Upload to Localazy failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -25210,6 +25645,10 @@ const LocalazyTransferUploadService = ({ strapi: strapi2 }) => ({
|
|
|
25210
25645
|
message: errorMessage
|
|
25211
25646
|
});
|
|
25212
25647
|
await activityLogsService.addEntry(sessionId, errorMessage);
|
|
25648
|
+
await activityLogsService.recordAttemptedEntries(
|
|
25649
|
+
sessionId,
|
|
25650
|
+
attemptedTuples.map((t3) => ({ ...t3, status: "failed", errorMessage }))
|
|
25651
|
+
);
|
|
25213
25652
|
await activityLogsService.finishSession(sessionId, "failed", errorMessage);
|
|
25214
25653
|
console.timeEnd("upload");
|
|
25215
25654
|
return;
|
|
@@ -25934,6 +26373,12 @@ const LocalazyTransferDownloadService = ({ strapi: strapi2 }) => ({
|
|
|
25934
26373
|
message
|
|
25935
26374
|
});
|
|
25936
26375
|
await activityLogsService.addEntry(sessionId, message);
|
|
26376
|
+
await activityLogsService.recordAttemptedEntry(sessionId, {
|
|
26377
|
+
uid,
|
|
26378
|
+
documentId,
|
|
26379
|
+
locale: isoStrapi,
|
|
26380
|
+
status: "success"
|
|
26381
|
+
});
|
|
25937
26382
|
await markKeysProcessed(sourceKeys);
|
|
25938
26383
|
} catch (e2) {
|
|
25939
26384
|
success = false;
|
|
@@ -25950,6 +26395,13 @@ const LocalazyTransferDownloadService = ({ strapi: strapi2 }) => ({
|
|
|
25950
26395
|
sessionId,
|
|
25951
26396
|
`Failed to create ${uid} [${documentId}] in ${isoStrapi}: ${e2.message}`
|
|
25952
26397
|
);
|
|
26398
|
+
await activityLogsService.recordAttemptedEntry(sessionId, {
|
|
26399
|
+
uid,
|
|
26400
|
+
documentId,
|
|
26401
|
+
locale: isoStrapi,
|
|
26402
|
+
status: "failed",
|
|
26403
|
+
errorMessage: e2.message
|
|
26404
|
+
});
|
|
25953
26405
|
}
|
|
25954
26406
|
} else {
|
|
25955
26407
|
try {
|
|
@@ -25968,6 +26420,12 @@ const LocalazyTransferDownloadService = ({ strapi: strapi2 }) => ({
|
|
|
25968
26420
|
message
|
|
25969
26421
|
});
|
|
25970
26422
|
await activityLogsService.addEntry(sessionId, message);
|
|
26423
|
+
await activityLogsService.recordAttemptedEntry(sessionId, {
|
|
26424
|
+
uid,
|
|
26425
|
+
documentId,
|
|
26426
|
+
locale: isoStrapi,
|
|
26427
|
+
status: "success"
|
|
26428
|
+
});
|
|
25971
26429
|
await markKeysProcessed(sourceKeys);
|
|
25972
26430
|
} catch (e2) {
|
|
25973
26431
|
success = false;
|
|
@@ -25984,6 +26442,13 @@ const LocalazyTransferDownloadService = ({ strapi: strapi2 }) => ({
|
|
|
25984
26442
|
sessionId,
|
|
25985
26443
|
`Failed to update ${uid} [${documentId}] in ${isoStrapi}: ${e2.message}`
|
|
25986
26444
|
);
|
|
26445
|
+
await activityLogsService.recordAttemptedEntry(sessionId, {
|
|
26446
|
+
uid,
|
|
26447
|
+
documentId,
|
|
26448
|
+
locale: isoStrapi,
|
|
26449
|
+
status: "failed",
|
|
26450
|
+
errorMessage: e2.message
|
|
26451
|
+
});
|
|
25987
26452
|
}
|
|
25988
26453
|
}
|
|
25989
26454
|
} catch (e2) {
|
|
@@ -25996,6 +26461,13 @@ const LocalazyTransferDownloadService = ({ strapi: strapi2 }) => ({
|
|
|
25996
26461
|
sessionId,
|
|
25997
26462
|
`Error processing ${uid} [${documentId}] in ${isoStrapi}: ${e2.message}`
|
|
25998
26463
|
);
|
|
26464
|
+
await activityLogsService.recordAttemptedEntry(sessionId, {
|
|
26465
|
+
uid,
|
|
26466
|
+
documentId,
|
|
26467
|
+
locale: isoStrapi,
|
|
26468
|
+
status: "failed",
|
|
26469
|
+
errorMessage: e2.message
|
|
26470
|
+
});
|
|
25999
26471
|
}
|
|
26000
26472
|
}
|
|
26001
26473
|
}
|
|
@@ -26746,6 +27218,7 @@ const emptyActivityLogs = {
|
|
|
26746
27218
|
sessions: []
|
|
26747
27219
|
};
|
|
26748
27220
|
const ONE_YEAR_MS = 365 * 24 * 60 * 60 * 1e3;
|
|
27221
|
+
const ATTEMPTED_ENTRIES_CAP = 5e3;
|
|
26749
27222
|
let writeLock = Promise.resolve();
|
|
26750
27223
|
const withLock = async (fn) => {
|
|
26751
27224
|
let release;
|
|
@@ -26810,6 +27283,38 @@ const ActivityLogsService = ({ strapi: strapi2 }) => ({
|
|
|
26810
27283
|
await this.saveActivityLogs(logs);
|
|
26811
27284
|
});
|
|
26812
27285
|
},
|
|
27286
|
+
async recordAttemptedEntry(sessionId, entry) {
|
|
27287
|
+
return this.recordAttemptedEntries(sessionId, [entry]);
|
|
27288
|
+
},
|
|
27289
|
+
async recordAttemptedEntries(sessionId, entries) {
|
|
27290
|
+
if (!entries.length) {
|
|
27291
|
+
return;
|
|
27292
|
+
}
|
|
27293
|
+
return withLock(async () => {
|
|
27294
|
+
const logs = await this.getActivityLogs();
|
|
27295
|
+
const session = logs.sessions.find((s2) => s2.id === sessionId);
|
|
27296
|
+
if (!session) {
|
|
27297
|
+
return;
|
|
27298
|
+
}
|
|
27299
|
+
if (!session.attemptedEntries) {
|
|
27300
|
+
session.attemptedEntries = [];
|
|
27301
|
+
}
|
|
27302
|
+
const now = Date.now();
|
|
27303
|
+
const wasAtCap = session.attemptedEntries.length >= ATTEMPTED_ENTRIES_CAP;
|
|
27304
|
+
const remaining = Math.max(0, ATTEMPTED_ENTRIES_CAP - session.attemptedEntries.length);
|
|
27305
|
+
const accepted = entries.slice(0, remaining);
|
|
27306
|
+
const dropped = entries.length - accepted.length;
|
|
27307
|
+
for (const entry of accepted) {
|
|
27308
|
+
session.attemptedEntries.push({ ...entry, attemptedAt: now });
|
|
27309
|
+
}
|
|
27310
|
+
if (dropped > 0 && !wasAtCap) {
|
|
27311
|
+
strapi2.log.warn(
|
|
27312
|
+
`[localazy] attemptedEntries cap (${ATTEMPTED_ENTRIES_CAP}) reached for session ${sessionId}; ${dropped} record(s) dropped.`
|
|
27313
|
+
);
|
|
27314
|
+
}
|
|
27315
|
+
await this.saveActivityLogs(logs);
|
|
27316
|
+
});
|
|
27317
|
+
},
|
|
26813
27318
|
async finishSession(sessionId, status, summary) {
|
|
26814
27319
|
return withLock(async () => {
|
|
26815
27320
|
const logs = await this.getActivityLogs();
|
|
@@ -58,5 +58,10 @@ declare const _default: {
|
|
|
58
58
|
exportSessions(ctx: any): Promise<void>;
|
|
59
59
|
clearSessions(ctx: any): Promise<void>;
|
|
60
60
|
};
|
|
61
|
+
DebugBundleController: ({ strapi }: {
|
|
62
|
+
strapi: import("@strapi/types/dist/core").Strapi;
|
|
63
|
+
}) => {
|
|
64
|
+
getBundle(ctx: any): Promise<any>;
|
|
65
|
+
};
|
|
61
66
|
};
|
|
62
67
|
export default _default;
|
|
@@ -86,6 +86,8 @@ export declare const getActivityLogsService: () => {
|
|
|
86
86
|
saveActivityLogs(logs: import("../db/model/activity-logs").ActivityLogs): Promise<void>;
|
|
87
87
|
startSession(eventType: "upload" | "download" | "webhook", initiatedBy: string): Promise<string>;
|
|
88
88
|
addEntry(sessionId: string, message: string, data?: any): Promise<void>;
|
|
89
|
+
recordAttemptedEntry(sessionId: string, entry: Omit<import("../db/model/activity-logs").AttemptedEntry, "attemptedAt">): Promise<void>;
|
|
90
|
+
recordAttemptedEntries(sessionId: string, entries: Omit<import("../db/model/activity-logs").AttemptedEntry, "attemptedAt">[]): Promise<void>;
|
|
89
91
|
finishSession(sessionId: string, status: "completed" | "failed", summary: string): Promise<void>;
|
|
90
92
|
getSessions(eventType?: "upload" | "download" | "webhook"): Promise<import("../db/model/activity-logs").ActivityLogSession[]>;
|
|
91
93
|
getAllSessionsWithEntries(): Promise<import("../db/model/activity-logs").ActivityLogSession[]>;
|