@infuro/cms-core 1.0.60 → 1.0.61

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.
@@ -3846,14 +3846,14 @@ function createCrudHandler(dataSource, entityMap, options) {
3846
3846
  if (Number.isFinite(n)) productWhere[key] = n;
3847
3847
  }
3848
3848
  }
3849
- const idsParam = searchParams.get("ids")?.trim();
3850
- if (idsParam) {
3851
- const ids = idsParam.split(",").map((s) => Number(s.trim())).filter((n) => Number.isFinite(n) && n > 0);
3849
+ const idsParam2 = searchParams.get("ids")?.trim();
3850
+ if (idsParam2) {
3851
+ const ids = idsParam2.split(",").map((s) => Number(s.trim())).filter((n) => Number.isFinite(n) && n > 0);
3852
3852
  productWhere.id = ids.length > 0 ? In(ids) : -1;
3853
3853
  } else {
3854
- const excludeIdsParam = searchParams.get("excludeIds")?.trim();
3855
- if (excludeIdsParam) {
3856
- const excludeIds = excludeIdsParam.split(",").map((s) => Number(s.trim())).filter((n) => Number.isFinite(n) && n > 0);
3854
+ const excludeIdsParam2 = searchParams.get("excludeIds")?.trim();
3855
+ if (excludeIdsParam2) {
3856
+ const excludeIds = excludeIdsParam2.split(",").map((s) => Number(s.trim())).filter((n) => Number.isFinite(n) && n > 0);
3857
3857
  if (excludeIds.length > 0) productWhere.id = Not(In(excludeIds));
3858
3858
  }
3859
3859
  }
@@ -4130,6 +4130,24 @@ function createCrudHandler(dataSource, entityMap, options) {
4130
4130
  }
4131
4131
  }
4132
4132
  where = mergeDeletedFalseWhere(repo, where);
4133
+ const idsParam = searchParams.get("ids")?.trim();
4134
+ if (idsParam && columnNames.has("id")) {
4135
+ const idList = idsParam.split(",").map((s) => Number(s.trim())).filter((n) => Number.isFinite(n) && n > 0);
4136
+ if (idList.length > 0) {
4137
+ where = mergeListWhereAnd(where, {
4138
+ id: In(idList)
4139
+ });
4140
+ }
4141
+ }
4142
+ const excludeIdsParam = searchParams.get("excludeIds")?.trim();
4143
+ if (excludeIdsParam && columnNames.has("id")) {
4144
+ const excludeIdList = excludeIdsParam.split(",").map((s) => Number(s.trim())).filter((n) => Number.isFinite(n) && n > 0);
4145
+ if (excludeIdList.length > 0) {
4146
+ where = mergeListWhereAnd(where, {
4147
+ id: Not(In(excludeIdList))
4148
+ });
4149
+ }
4150
+ }
4133
4151
  if (resourceUsesVendorScope(resource)) {
4134
4152
  const scope = await resolveScope();
4135
4153
  const catalogFlags = await getVendorCatalogCreateFlags(dataSource);
@@ -8810,25 +8828,79 @@ function createUploadHandler(config) {
8810
8828
  "application/x-zip-compressed"
8811
8829
  ];
8812
8830
  return /* @__PURE__ */ __name(async function POST(req) {
8831
+ const reqId = `req-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
8832
+ console.info("[upload] POST /api/upload start", {
8833
+ reqId,
8834
+ method: req.method,
8835
+ contentType: req.headers.get("content-type"),
8836
+ contentLength: req.headers.get("content-length"),
8837
+ hasStorageConfig: storage != null
8838
+ });
8813
8839
  const authErr = await requireAuth(req);
8814
- if (authErr) return authErr;
8840
+ if (authErr) {
8841
+ console.error("[upload] auth failed", {
8842
+ reqId,
8843
+ status: authErr.status
8844
+ });
8845
+ return authErr;
8846
+ }
8847
+ console.info("[upload] auth ok", {
8848
+ reqId
8849
+ });
8815
8850
  if (requireEntityPermission) {
8816
8851
  const pe = await requireEntityPermission(req, "upload", "create");
8817
- if (pe) return pe;
8852
+ if (pe) {
8853
+ console.error("[upload] permission denied", {
8854
+ reqId,
8855
+ status: pe.status
8856
+ });
8857
+ return pe;
8858
+ }
8859
+ console.info("[upload] permission ok", {
8860
+ reqId,
8861
+ entity: "upload",
8862
+ action: "create"
8863
+ });
8818
8864
  }
8819
8865
  try {
8866
+ console.info("[upload] parsing formData", {
8867
+ reqId
8868
+ });
8820
8869
  const formData = await req.formData();
8821
8870
  const file = formData.get("file");
8822
- if (!file) return json({
8823
- error: "No file uploaded"
8824
- }, {
8825
- status: 400
8826
- });
8827
- if (!allowed.includes(file.type)) return json({
8828
- error: "File type not allowed"
8829
- }, {
8830
- status: 400
8871
+ console.info("[upload] formData parsed", {
8872
+ reqId,
8873
+ hasFile: Boolean(file),
8874
+ fileName: file?.name ?? null,
8875
+ fileType: file?.type ?? null,
8876
+ fileSize: file?.size ?? null,
8877
+ formKeys: [
8878
+ ...formData.keys()
8879
+ ]
8831
8880
  });
8881
+ if (!file) {
8882
+ console.error("[upload] no file in formData", {
8883
+ reqId
8884
+ });
8885
+ return json({
8886
+ error: "No file uploaded"
8887
+ }, {
8888
+ status: 400
8889
+ });
8890
+ }
8891
+ if (!allowed.includes(file.type)) {
8892
+ console.error("[upload] file type not allowed", {
8893
+ reqId,
8894
+ fileType: file.type,
8895
+ fileName: file.name,
8896
+ allowed
8897
+ });
8898
+ return json({
8899
+ error: "File type not allowed"
8900
+ }, {
8901
+ status: 400
8902
+ });
8903
+ }
8832
8904
  const defaultMax = 10 * 1024 * 1024;
8833
8905
  const maxZipBytes = 80 * 1024 * 1024;
8834
8906
  const maxVideoBytes = 100 * 1024 * 1024;
@@ -8836,25 +8908,51 @@ function createUploadHandler(config) {
8836
8908
  const isZip = file.type === "application/zip" || file.type === "application/x-zip-compressed";
8837
8909
  const isVideo = file.type === "video/mp4" || file.type === "video/webm" || file.type === "video/quicktime";
8838
8910
  const effectiveMax = isZip ? Math.max(baseMax, maxZipBytes) : isVideo ? Math.max(baseMax, maxVideoBytes) : baseMax;
8839
- if (file.size > effectiveMax) return json({
8840
- error: "File size exceeds limit"
8841
- }, {
8842
- status: 400
8911
+ console.info("[upload] size check", {
8912
+ reqId,
8913
+ fileSize: file.size,
8914
+ effectiveMax,
8915
+ isZip,
8916
+ isVideo,
8917
+ isImage: file.type.startsWith("image/")
8843
8918
  });
8919
+ if (file.size > effectiveMax) {
8920
+ console.error("[upload] file too large", {
8921
+ reqId,
8922
+ fileSize: file.size,
8923
+ effectiveMax,
8924
+ fileName: file.name
8925
+ });
8926
+ return json({
8927
+ error: "File size exceeds limit"
8928
+ }, {
8929
+ status: 400
8930
+ });
8931
+ }
8844
8932
  const parentRaw = formData.get("parentId");
8845
8933
  let parentId = null;
8846
8934
  if (parentRaw != null && String(parentRaw).trim() !== "") {
8847
8935
  const n = Number(parentRaw);
8848
- if (!Number.isFinite(n)) return json({
8849
- error: "Invalid parentId"
8850
- }, {
8851
- status: 400
8852
- });
8936
+ if (!Number.isFinite(n)) {
8937
+ console.error("[upload] invalid parentId", {
8938
+ reqId,
8939
+ parentRaw
8940
+ });
8941
+ return json({
8942
+ error: "Invalid parentId"
8943
+ }, {
8944
+ status: 400
8945
+ });
8946
+ }
8853
8947
  parentId = n;
8854
8948
  }
8855
8949
  let folder = "";
8856
8950
  if (parentId != null) {
8857
8951
  if (!dataSource || !entityMap?.media) {
8952
+ console.error("[upload] missing dataSource/entityMap for folder upload", {
8953
+ reqId,
8954
+ parentId
8955
+ });
8858
8956
  return json({
8859
8957
  error: "Upload handler needs dataSource and entityMap for folder uploads"
8860
8958
  }, {
@@ -8868,6 +8966,12 @@ function createUploadHandler(config) {
8868
8966
  }
8869
8967
  });
8870
8968
  if (!p || p.kind !== "folder") {
8969
+ console.error("[upload] parent is not a folder", {
8970
+ reqId,
8971
+ parentId,
8972
+ found: Boolean(p),
8973
+ kind: p ? p.kind : null
8974
+ });
8871
8975
  return json({
8872
8976
  error: "parent must be a folder"
8873
8977
  }, {
@@ -8875,25 +8979,90 @@ function createUploadHandler(config) {
8875
8979
  });
8876
8980
  }
8877
8981
  folder = await relativePathFromMediaParentId(dataSource, entityMap, parentId);
8982
+ console.info("[upload] resolved folder from parentId", {
8983
+ reqId,
8984
+ parentId,
8985
+ folder
8986
+ });
8878
8987
  } else {
8879
8988
  const folderRawLegacy = formData.get("folder") ?? formData.get("folderPath");
8880
8989
  if (folderRawLegacy && typeof folderRawLegacy === "string" && folderRawLegacy.trim()) {
8881
8990
  folder = sanitizeMediaFolderPath(folderRawLegacy);
8991
+ console.info("[upload] resolved folder from legacy field", {
8992
+ reqId,
8993
+ folder
8994
+ });
8882
8995
  }
8883
8996
  }
8997
+ console.info("[upload] reading file into buffer", {
8998
+ reqId,
8999
+ fileName: file.name,
9000
+ fileType: file.type,
9001
+ fileSize: file.size
9002
+ });
8884
9003
  const buffer = Buffer.from(await file.arrayBuffer());
8885
9004
  const fileName = `${Date.now()}-${file.name}`;
8886
9005
  const contentType = file.type || "application/octet-stream";
8887
9006
  const relativeUnderUploads = folder ? `${folder}/${fileName}` : fileName;
9007
+ const storageKey = `uploads/${relativeUnderUploads}`;
9008
+ console.info("[upload] S3/storage key prepared", {
9009
+ reqId,
9010
+ originalFileName: file.name,
9011
+ stampedFileName: fileName,
9012
+ folder: folder || "(root)",
9013
+ relativeUnderUploads,
9014
+ storageKey,
9015
+ contentType,
9016
+ bytes: buffer.length
9017
+ });
8888
9018
  const raw = typeof storage === "function" ? storage() : storage;
9019
+ console.info("[upload] resolving storage service", {
9020
+ reqId,
9021
+ storageKey,
9022
+ storageIsFunction: typeof storage === "function",
9023
+ rawIsPromise: raw instanceof Promise,
9024
+ rawIsNullish: raw == null
9025
+ });
8889
9026
  const storageService = raw instanceof Promise ? await raw : raw;
9027
+ console.info("[upload] storage service resolved", {
9028
+ reqId,
9029
+ storageKey,
9030
+ hasStorageService: Boolean(storageService)
9031
+ });
8890
9032
  if (storageService) {
8891
- const fileUrl = await storageService.upload(buffer, `uploads/${relativeUnderUploads}`, contentType);
9033
+ console.info("[upload] calling storage.upload", {
9034
+ reqId,
9035
+ storageKey,
9036
+ contentType,
9037
+ bytes: buffer.length
9038
+ });
9039
+ const uploadStarted = Date.now();
9040
+ let fileUrl;
9041
+ try {
9042
+ fileUrl = await storageService.upload(buffer, storageKey, contentType);
9043
+ } catch (storageErr) {
9044
+ console.error("[upload] storage.upload threw", {
9045
+ reqId,
9046
+ storageKey,
9047
+ contentType,
9048
+ bytes: buffer.length,
9049
+ ms: Date.now() - uploadStarted,
9050
+ error: storageErr instanceof Error ? storageErr.message : String(storageErr),
9051
+ name: storageErr instanceof Error ? storageErr.name : void 0,
9052
+ stack: storageErr instanceof Error ? storageErr.stack : void 0,
9053
+ code: storageErr && typeof storageErr === "object" && ("Code" in storageErr || "code" in storageErr) ? storageErr.Code ?? storageErr.code : void 0,
9054
+ metadata: storageErr && typeof storageErr === "object" && "$metadata" in storageErr ? storageErr.$metadata : void 0
9055
+ });
9056
+ throw storageErr;
9057
+ }
8892
9058
  console.info("[upload] stored via storage plugin", {
8893
- key: `uploads/${relativeUnderUploads}`,
9059
+ reqId,
9060
+ key: storageKey,
9061
+ fullS3KeyHint: storageKey,
8894
9062
  contentType,
8895
9063
  bytes: buffer.length,
8896
- fileUrl
9064
+ fileUrl,
9065
+ ms: Date.now() - uploadStarted
8897
9066
  });
8898
9067
  return json({
8899
9068
  filePath: fileUrl,
@@ -8901,8 +9070,10 @@ function createUploadHandler(config) {
8901
9070
  });
8902
9071
  }
8903
9072
  console.warn("[upload] no storage plugin \u2014 writing local disk (broken on most deploys)", {
9073
+ reqId,
8904
9074
  localUploadDir,
8905
- key: relativeUnderUploads
9075
+ key: relativeUnderUploads,
9076
+ storageKey
8906
9077
  });
8907
9078
  const fs2 = await import('fs/promises');
8908
9079
  const path2 = await import('path');
@@ -8913,6 +9084,12 @@ function createUploadHandler(config) {
8913
9084
  });
8914
9085
  await fs2.writeFile(filePath, buffer);
8915
9086
  const urlRel = `${localUploadDir.replace(/^\/+/, "").replace(/\\/g, "/")}/${relativeUnderUploads.replace(/\\/g, "/")}`;
9087
+ console.info("[upload] local write ok", {
9088
+ reqId,
9089
+ diskPath: filePath,
9090
+ filePath: `/${urlRel}`,
9091
+ key: relativeUnderUploads
9092
+ });
8916
9093
  return json({
8917
9094
  filePath: `/${urlRel}`,
8918
9095
  parentId
@@ -8921,11 +9098,13 @@ function createUploadHandler(config) {
8921
9098
  const message = err instanceof Error ? err.message : String(err);
8922
9099
  const awsMeta = err && typeof err === "object" && "$metadata" in err ? err.$metadata : void 0;
8923
9100
  console.error("[upload] File upload failed", {
9101
+ reqId,
8924
9102
  error: message,
8925
9103
  stack: err instanceof Error ? err.stack : void 0,
8926
9104
  name: err instanceof Error ? err.name : void 0,
8927
9105
  code: err && typeof err === "object" && ("Code" in err || "code" in err) ? err.Code ?? err.code : void 0,
8928
- metadata: awsMeta
9106
+ metadata: awsMeta,
9107
+ raw: err instanceof Error ? void 0 : String(err)
8929
9108
  });
8930
9109
  if (/Failed to parse body as FormData/i.test(message)) {
8931
9110
  return json({
@@ -9262,13 +9441,43 @@ function createFormSaveHandlers(config) {
9262
9441
  }
9263
9442
  }
9264
9443
  if (Object.keys(formRow).length > 0) await formRepo().update(formId, formRow);
9265
- await fieldRepo().delete({
9266
- formId
9267
- });
9444
+ const incomingFieldIds = /* @__PURE__ */ new Set();
9268
9445
  for (let i = 0; i < fields.length; i++) {
9269
- const row = normalizeFieldRow(fields[i], formId);
9446
+ const f = fields[i];
9447
+ const fieldId = f.id != null && Number.isFinite(Number(f.id)) && Number(f.id) > 0 ? Number(f.id) : null;
9448
+ const row = normalizeFieldRow(f, formId);
9270
9449
  row.order = i + 1;
9271
- await fieldRepo().save(fieldRepo().create(row));
9450
+ if (fieldId) {
9451
+ const existingField = await fieldRepo().findOne({
9452
+ where: {
9453
+ id: fieldId,
9454
+ formId
9455
+ }
9456
+ });
9457
+ if (existingField) {
9458
+ incomingFieldIds.add(fieldId);
9459
+ await fieldRepo().update({
9460
+ id: fieldId,
9461
+ formId
9462
+ }, row);
9463
+ continue;
9464
+ }
9465
+ }
9466
+ const created = await fieldRepo().save(fieldRepo().create(row));
9467
+ if (created?.id) incomingFieldIds.add(created.id);
9468
+ }
9469
+ const allCurrentFields = await fieldRepo().find({
9470
+ where: {
9471
+ formId
9472
+ }
9473
+ });
9474
+ for (const existingField of allCurrentFields) {
9475
+ if (!incomingFieldIds.has(existingField.id)) {
9476
+ await fieldRepo().delete({
9477
+ id: existingField.id,
9478
+ formId
9479
+ });
9480
+ }
9272
9481
  }
9273
9482
  const saved = await formRepo().findOne({
9274
9483
  where: {
@@ -9422,7 +9631,7 @@ function createFormSubmissionListHandler(config) {
9422
9631
  const limit = Math.min(100, Number(searchParams.get("limit")) || 10);
9423
9632
  const skip = (page - 1) * limit;
9424
9633
  const sortField = searchParams.get("sortField") || "createdAt";
9425
- const sortOrder = searchParams.get("sortOrder") === "desc" ? "DESC" : "ASC";
9634
+ const sortOrder = searchParams.get("sortOrder") === "asc" ? "ASC" : "DESC";
9426
9635
  const [data, total] = await repo.findAndCount({
9427
9636
  skip,
9428
9637
  take: limit,
@@ -31194,7 +31403,7 @@ function createStorefrontApiHandler(config) {
31194
31403
  const meta = it.metadata && typeof it.metadata === "object" && !Array.isArray(it.metadata) ? it.metadata : {};
31195
31404
  const fromMeta = Number(meta.comboId ?? meta.combo_id);
31196
31405
  if (Number.isInteger(fromMeta) && fromMeta > 0) return fromMeta;
31197
- if (meta.isCombo === true || meta.type === "combo" || meta.lineType === "combo") {
31406
+ if (meta.isCombo === true || meta.type === "combo" || meta.lineType === "combo" || meta.lineKind === "combo" || meta.itemType === "combo") {
31198
31407
  const pid = Number(it.productId);
31199
31408
  if (Number.isInteger(pid) && pid > 0) return pid;
31200
31409
  }
@@ -33323,7 +33532,10 @@ function createStorefrontApiHandler(config) {
33323
33532
  });
33324
33533
  if (existing) {
33325
33534
  await cartItemRepo().update(existing.id, {
33326
- quantity: existing.quantity + gi.quantity
33535
+ quantity: gi.quantity,
33536
+ ...gi.metadata ? {
33537
+ metadata: gi.metadata
33538
+ } : {}
33327
33539
  });
33328
33540
  } else {
33329
33541
  await cartItemRepo().save(cartItemRepo().create({