@infuro/cms-core 1.0.22 → 1.0.23

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/index.cjs CHANGED
@@ -4388,12 +4388,14 @@ function createDashboardStatsHandler(config) {
4388
4388
  const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3);
4389
4389
  const repo = (name) => entityMap[name] ? dataSource.getRepository(entityMap[name]) : void 0;
4390
4390
  const [contactsCount, formsCount, formSubmissionsCount, usersCount, blogsCount, recentContacts, recentSubmissions, contactTypeRows] = await Promise.all([
4391
- repo("contacts")?.count() ?? 0,
4391
+ repo("contacts")?.count({ where: { deleted: false } }) ?? 0,
4392
4392
  repo("forms")?.count({ where: { deleted: false } }) ?? 0,
4393
4393
  repo("form_submissions")?.count() ?? 0,
4394
4394
  repo("users")?.count({ where: { deleted: false } }) ?? 0,
4395
4395
  repo("blogs")?.count({ where: { deleted: false } }) ?? 0,
4396
- repo("contacts")?.count({ where: { createdAt: (0, import_typeorm6.MoreThanOrEqual)(sevenDaysAgo) } }) ?? 0,
4396
+ repo("contacts")?.count({
4397
+ where: { deleted: false, createdAt: (0, import_typeorm6.MoreThanOrEqual)(sevenDaysAgo) }
4398
+ }) ?? 0,
4397
4399
  repo("form_submissions")?.count({ where: { createdAt: (0, import_typeorm6.MoreThanOrEqual)(sevenDaysAgo) } }) ?? 0,
4398
4400
  repo("contacts")?.createQueryBuilder("c").select("COALESCE(NULLIF(TRIM(c.type), ''), 'unknown')", "type").addSelect("COUNT(*)", "count").where("c.deleted = :deleted", { deleted: false }).groupBy("COALESCE(NULLIF(TRIM(c.type), ''), 'unknown')").getRawMany() ?? []
4399
4401
  ]);
@@ -5026,11 +5028,14 @@ function createFormSubmissionHandler(config) {
5026
5028
  const contactRepo = dataSource.getRepository(entityMap.contacts);
5027
5029
  let contact = await contactRepo.findOne({ where: { email: contactData.email } });
5028
5030
  if (!contact) {
5031
+ const createdAt = /* @__PURE__ */ new Date();
5029
5032
  contact = await contactRepo.save(
5030
5033
  contactRepo.create({
5031
5034
  name: contactData.name,
5032
5035
  email: contactData.email,
5033
- phone: contactData.phone
5036
+ phone: contactData.phone,
5037
+ createdAt,
5038
+ updatedAt: createdAt
5034
5039
  })
5035
5040
  );
5036
5041
  }
@@ -5186,12 +5191,13 @@ function createUsersApiHandlers(config) {
5186
5191
  const gid = body.groupId ?? null;
5187
5192
  const isCustomer = !!(customerG && gid === customerG.id);
5188
5193
  const adminAccess = isCustomer ? false : body.adminAccess === false ? false : true;
5194
+ const blocked = body.blocked === true || body.blocked === "true" || body.blocked === 1 || body.blocked === "1";
5189
5195
  const newUser = await userRepo().save(
5190
5196
  userRepo().create({
5191
5197
  name: body.name,
5192
5198
  email: body.email,
5193
5199
  password: null,
5194
- blocked: true,
5200
+ blocked,
5195
5201
  groupId: gid,
5196
5202
  adminAccess
5197
5203
  })
@@ -5206,7 +5212,14 @@ function createUsersApiHandlers(config) {
5206
5212
  inviteLink,
5207
5213
  newUser.name ?? ""
5208
5214
  );
5209
- return json({ message: "User created successfully (blocked until password is set)", user: newUser, inviteLink }, { status: 201 });
5215
+ return json(
5216
+ {
5217
+ message: blocked ? "User created successfully (blocked until password is set)" : "User created successfully",
5218
+ user: newUser,
5219
+ inviteLink
5220
+ },
5221
+ { status: 201 }
5222
+ );
5210
5223
  } catch {
5211
5224
  return json({ error: "Server Error" }, { status: 500 });
5212
5225
  }
@@ -5508,7 +5521,10 @@ function createChatHandlers(config) {
5508
5521
  const existing = await repo.findOne({ where: { email } });
5509
5522
  let contact;
5510
5523
  if (!existing) {
5511
- contact = await repo.save(repo.create({ name, email, phone }));
5524
+ const createdAt = /* @__PURE__ */ new Date();
5525
+ contact = await repo.save(
5526
+ repo.create({ name, email, phone, createdAt, updatedAt: createdAt })
5527
+ );
5512
5528
  } else {
5513
5529
  const row = existing;
5514
5530
  if (row.deleted) {
@@ -8707,6 +8723,40 @@ function logCrudClientError(op, detail) {
8707
8723
  function logCrudServerError(op, detail) {
8708
8724
  console.error(CRUD_LOG, op, detail);
8709
8725
  }
8726
+ async function aggregateMediaFolderFileSizes(dataSource, folderIds) {
8727
+ const map = /* @__PURE__ */ new Map();
8728
+ if (folderIds.length === 0) return map;
8729
+ try {
8730
+ const rows = await dataSource.query(
8731
+ `
8732
+ WITH RECURSIVE walk AS (
8733
+ SELECT m."parentId" AS root_id, m.id, m.kind, m.size
8734
+ FROM media m
8735
+ WHERE m."parentId" = ANY($1) AND m.deleted = false
8736
+ UNION ALL
8737
+ SELECT w.root_id, m.id, m.kind, m.size
8738
+ FROM walk w
8739
+ INNER JOIN media m ON m."parentId" = w.id AND m.deleted = false
8740
+ )
8741
+ SELECT root_id AS "rootId", COALESCE(SUM(size) FILTER (WHERE kind = 'file'), 0)::bigint AS "totalSize"
8742
+ FROM walk
8743
+ GROUP BY root_id
8744
+ `,
8745
+ [folderIds]
8746
+ );
8747
+ for (const r of rows) {
8748
+ map.set(Number(r.rootId), Number(r.totalSize));
8749
+ }
8750
+ for (const id of folderIds) {
8751
+ if (!map.has(id)) map.set(id, 0);
8752
+ }
8753
+ } catch (err) {
8754
+ logCrudServerError("media folder size aggregate failed", {
8755
+ message: err instanceof Error ? err.message : String(err)
8756
+ });
8757
+ }
8758
+ return map;
8759
+ }
8710
8760
  var DATE_COLUMN_TYPES = /* @__PURE__ */ new Set([
8711
8761
  "date",
8712
8762
  "datetime",
@@ -8771,6 +8821,16 @@ function mergeDeletedFalseWhere(repo, where) {
8771
8821
  }
8772
8822
  return Object.keys(where).length > 0 ? { ...where, ...d } : d;
8773
8823
  }
8824
+ function normalizeProductSku(value) {
8825
+ if (value == null) return null;
8826
+ const s = String(value).trim();
8827
+ return s === "" ? null : s;
8828
+ }
8829
+ async function assertProductSkuUnique(repo, sku, excludeId) {
8830
+ const where = excludeId != null ? { sku, deleted: false, id: (0, import_typeorm46.Not)(excludeId) } : { sku, deleted: false };
8831
+ const row = await repo.findOne({ where });
8832
+ return row == null;
8833
+ }
8774
8834
  function buildSoftDeletePayload(meta, deletedBy) {
8775
8835
  const payload = { deleted: true };
8776
8836
  if (meta.columns.some((c) => c.propertyName === "deletedAt")) {
@@ -8993,19 +9053,36 @@ function createCrudHandler(dataSource, entityMap, options) {
8993
9053
  qb.andWhere("m.filename ILIKE :search", { search: `%${search.trim()}%` });
8994
9054
  }
8995
9055
  if (typeFilter) {
8996
- qb.andWhere(
8997
- new import_typeorm46.Brackets((sq) => {
8998
- sq.where("m.kind = :folderKind", { folderKind: "folder" }).orWhere("m.mimeType LIKE :mtp", {
8999
- mtp: `${typeFilter}/%`
9000
- });
9001
- })
9002
- );
9056
+ if (typeFilter === "folder") {
9057
+ qb.andWhere("m.kind = :folderKind", { folderKind: "folder" });
9058
+ } else if (typeFilter === "file") {
9059
+ qb.andWhere("m.kind = :fileKind", { fileKind: "file" });
9060
+ } else if (typeFilter === "image") {
9061
+ qb.andWhere("m.mimeType LIKE :imageMimeType", { imageMimeType: "image/%" });
9062
+ } else if (typeFilter === "video") {
9063
+ qb.andWhere("m.mimeType LIKE :videoMimeType", { videoMimeType: "video/%" });
9064
+ } else if (typeFilter === "audio") {
9065
+ qb.andWhere("m.mimeType LIKE :audioMimeType", { audioMimeType: "audio/%" });
9066
+ } else if (typeFilter === "Document") {
9067
+ qb.andWhere("m.mimeType LIKE :documentMimeType", { documentMimeType: "application/pdf" });
9068
+ } else if (typeFilter === "application") {
9069
+ qb.andWhere("m.kind = :folderKind", { folderKind: "folder" });
9070
+ }
9003
9071
  }
9004
9072
  const allowedSort = ["filename", "createdAt", "id"];
9005
9073
  const sf = allowedSort.includes(sortFieldRaw) ? sortFieldRaw : "filename";
9006
9074
  const so = sortOrder === "DESC" ? "DESC" : "ASC";
9007
- qb.orderBy("CASE WHEN m.kind = :fk THEN 0 ELSE 1 END", "ASC").addOrderBy(`m.${sf}`, so).setParameter("fk", "folder").skip(skip).take(limit);
9008
- const [data2, total2] = await qb.getManyAndCount();
9075
+ qb.orderBy(`m.${sf}`, so).skip(skip).take(limit);
9076
+ const [rows, total2] = await qb.getManyAndCount();
9077
+ const mediaRows = rows;
9078
+ const folderIds = mediaRows.filter((m) => m.kind === "folder").map((m) => m.id);
9079
+ let data2 = rows;
9080
+ if (folderIds.length > 0) {
9081
+ const sizeMap = await aggregateMediaFolderFileSizes(dataSource, folderIds);
9082
+ data2 = mediaRows.map(
9083
+ (m) => m.kind === "folder" ? { ...m, size: sizeMap.get(m.id) ?? 0 } : m
9084
+ );
9085
+ }
9009
9086
  return json({ total: total2, page, limit, totalPages: Math.ceil(total2 / limit), data: data2 });
9010
9087
  }
9011
9088
  const sortField = columnNames.has(sortFieldRaw) ? sortFieldRaw : "createdAt";
@@ -9116,6 +9193,20 @@ function createCrudHandler(dataSource, entityMap, options) {
9116
9193
  });
9117
9194
  return json({ error: "Invalid request payload" }, { status: 400 });
9118
9195
  }
9196
+ if (resource === "products") {
9197
+ if ("sku" in persistBody) {
9198
+ const skuNorm = normalizeProductSku(persistBody.sku);
9199
+ if (skuNorm) {
9200
+ const ok = await assertProductSkuUnique(repo, skuNorm);
9201
+ if (!ok) {
9202
+ return json({ error: "SKU already exists. Please use a unique SKU." }, { status: 400 });
9203
+ }
9204
+ persistBody.sku = skuNorm;
9205
+ } else {
9206
+ persistBody.sku = null;
9207
+ }
9208
+ }
9209
+ }
9119
9210
  sanitizeBodyForEntity(repo, persistBody);
9120
9211
  let created;
9121
9212
  try {
@@ -9310,7 +9401,20 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
9310
9401
  relations: ["order", "order.contact", "contact"]
9311
9402
  });
9312
9403
  if (!payment) return json({ message: "Not found" }, { status: 404 });
9313
- return json(payment);
9404
+ const p = payment;
9405
+ const order = p.order;
9406
+ const orderContact = order?.contact;
9407
+ const contact = p.contact;
9408
+ const customer = orderContact ?? contact;
9409
+ return json({
9410
+ ...p,
9411
+ order: order ? {
9412
+ id: order.id,
9413
+ orderNumber: order.orderNumber,
9414
+ contact: orderContact ? { name: orderContact.name, email: orderContact.email } : null
9415
+ } : null,
9416
+ contact: customer ? { id: customer.id, name: customer.name, email: customer.email } : null
9417
+ });
9314
9418
  }
9315
9419
  if (resource === "blogs") {
9316
9420
  const blog = await repo.findOne({
@@ -9425,6 +9529,23 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
9425
9529
  delete u.parentId;
9426
9530
  delete u.kind;
9427
9531
  }
9532
+ if (resource === "products") {
9533
+ const currentRow = await repo.findOne({
9534
+ where: { id: numericId, deleted: false }
9535
+ });
9536
+ if (!currentRow) return json({ message: "Not found" }, { status: 404 });
9537
+ const merged = { ...currentRow, ...updatePayload };
9538
+ const effSku = normalizeProductSku(merged.sku);
9539
+ if (effSku) {
9540
+ const ok = await assertProductSkuUnique(repo, effSku, numericId);
9541
+ if (!ok) {
9542
+ return json({ error: "SKU already exists. Please use a unique SKU." }, { status: 400 });
9543
+ }
9544
+ }
9545
+ if ("sku" in updatePayload) {
9546
+ updatePayload.sku = effSku;
9547
+ }
9548
+ }
9428
9549
  if (Object.keys(updatePayload).length > 0) {
9429
9550
  sanitizeBodyForEntity(repo, updatePayload);
9430
9551
  await repo.update(numericId, updatePayload);
@@ -9454,6 +9575,11 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
9454
9575
  where: { id: numericId, deleted: false }
9455
9576
  });
9456
9577
  if (!existing) return json({ message: "Not found" }, { status: 404 });
9578
+ if (resource === "contacts") {
9579
+ const result2 = await repo.delete(numericId);
9580
+ if (result2.affected === 0) return json({ message: "Not found" }, { status: 404 });
9581
+ return json({ message: "Deleted successfully" }, { status: 200 });
9582
+ }
9457
9583
  let deletedBy = null;
9458
9584
  if (getDeletedByUserId) {
9459
9585
  try {
@@ -12263,7 +12389,7 @@ var DEFAULT_ADMIN_NAV = [
12263
12389
  ];
12264
12390
 
12265
12391
  // src/index.ts
12266
- console.log("\u{1F525} USING LOCAL CMS CORE (index.ts loaded) \u{1F525}");
12392
+ console.log("\u{1F525} USING LOCAL CMS (index.ts loaded) \u{1F525}");
12267
12393
  // Annotate the CommonJS export names for ESM import in node:
12268
12394
  0 && (module.exports = {
12269
12395
  ADMIN_GROUP_NAME,