@infuro/cms-core 1.0.21 → 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/admin.cjs +2373 -1536
- package/dist/admin.cjs.map +1 -1
- package/dist/admin.d.cts +13 -8
- package/dist/admin.d.ts +13 -8
- package/dist/admin.js +2367 -1532
- package/dist/admin.js.map +1 -1
- package/dist/api.cjs +165 -21
- package/dist/api.cjs.map +1 -1
- package/dist/api.js +166 -22
- package/dist/api.js.map +1 -1
- package/dist/index.cjs +166 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +167 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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({
|
|
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
|
|
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(
|
|
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
|
}
|
|
@@ -5504,15 +5517,36 @@ function createChatHandlers(config) {
|
|
|
5504
5517
|
const email = body?.email?.trim();
|
|
5505
5518
|
if (!name || !email) return json({ error: "name and email required" }, { status: 400 });
|
|
5506
5519
|
const repo = contactRepo();
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5520
|
+
const phone = body.phone?.trim() || null;
|
|
5521
|
+
const existing = await repo.findOne({ where: { email } });
|
|
5522
|
+
let contact;
|
|
5523
|
+
if (!existing) {
|
|
5524
|
+
const createdAt = /* @__PURE__ */ new Date();
|
|
5525
|
+
contact = await repo.save(
|
|
5526
|
+
repo.create({ name, email, phone, createdAt, updatedAt: createdAt })
|
|
5527
|
+
);
|
|
5528
|
+
} else {
|
|
5529
|
+
const row = existing;
|
|
5530
|
+
if (row.deleted) {
|
|
5531
|
+
await repo.update(row.id, {
|
|
5532
|
+
deleted: false,
|
|
5533
|
+
deletedAt: null,
|
|
5534
|
+
deletedBy: null,
|
|
5535
|
+
name,
|
|
5536
|
+
phone
|
|
5537
|
+
});
|
|
5538
|
+
const refreshed = await repo.findOne({ where: { id: row.id } });
|
|
5539
|
+
if (!refreshed) return json({ error: "Failed to identify", detail: "contact missing after reactivate" }, { status: 500 });
|
|
5540
|
+
contact = refreshed;
|
|
5541
|
+
} else {
|
|
5542
|
+
contact = existing;
|
|
5543
|
+
}
|
|
5511
5544
|
}
|
|
5512
5545
|
const convRepoInst = convRepo();
|
|
5513
|
-
const
|
|
5546
|
+
const contactId = contact.id;
|
|
5547
|
+
const conv = await convRepoInst.save(convRepoInst.create({ contactId }));
|
|
5514
5548
|
return json({
|
|
5515
|
-
contactId
|
|
5549
|
+
contactId,
|
|
5516
5550
|
conversationId: conv.id
|
|
5517
5551
|
});
|
|
5518
5552
|
} catch (err) {
|
|
@@ -8689,6 +8723,40 @@ function logCrudClientError(op, detail) {
|
|
|
8689
8723
|
function logCrudServerError(op, detail) {
|
|
8690
8724
|
console.error(CRUD_LOG, op, detail);
|
|
8691
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
|
+
}
|
|
8692
8760
|
var DATE_COLUMN_TYPES = /* @__PURE__ */ new Set([
|
|
8693
8761
|
"date",
|
|
8694
8762
|
"datetime",
|
|
@@ -8753,6 +8821,16 @@ function mergeDeletedFalseWhere(repo, where) {
|
|
|
8753
8821
|
}
|
|
8754
8822
|
return Object.keys(where).length > 0 ? { ...where, ...d } : d;
|
|
8755
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
|
+
}
|
|
8756
8834
|
function buildSoftDeletePayload(meta, deletedBy) {
|
|
8757
8835
|
const payload = { deleted: true };
|
|
8758
8836
|
if (meta.columns.some((c) => c.propertyName === "deletedAt")) {
|
|
@@ -8975,19 +9053,36 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
8975
9053
|
qb.andWhere("m.filename ILIKE :search", { search: `%${search.trim()}%` });
|
|
8976
9054
|
}
|
|
8977
9055
|
if (typeFilter) {
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
|
|
8981
|
-
|
|
8982
|
-
|
|
8983
|
-
})
|
|
8984
|
-
)
|
|
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
|
+
}
|
|
8985
9071
|
}
|
|
8986
9072
|
const allowedSort = ["filename", "createdAt", "id"];
|
|
8987
9073
|
const sf = allowedSort.includes(sortFieldRaw) ? sortFieldRaw : "filename";
|
|
8988
9074
|
const so = sortOrder === "DESC" ? "DESC" : "ASC";
|
|
8989
|
-
qb.orderBy(
|
|
8990
|
-
const [
|
|
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
|
+
}
|
|
8991
9086
|
return json({ total: total2, page, limit, totalPages: Math.ceil(total2 / limit), data: data2 });
|
|
8992
9087
|
}
|
|
8993
9088
|
const sortField = columnNames.has(sortFieldRaw) ? sortFieldRaw : "createdAt";
|
|
@@ -9098,6 +9193,20 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
9098
9193
|
});
|
|
9099
9194
|
return json({ error: "Invalid request payload" }, { status: 400 });
|
|
9100
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
|
+
}
|
|
9101
9210
|
sanitizeBodyForEntity(repo, persistBody);
|
|
9102
9211
|
let created;
|
|
9103
9212
|
try {
|
|
@@ -9292,7 +9401,20 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
9292
9401
|
relations: ["order", "order.contact", "contact"]
|
|
9293
9402
|
});
|
|
9294
9403
|
if (!payment) return json({ message: "Not found" }, { status: 404 });
|
|
9295
|
-
|
|
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
|
+
});
|
|
9296
9418
|
}
|
|
9297
9419
|
if (resource === "blogs") {
|
|
9298
9420
|
const blog = await repo.findOne({
|
|
@@ -9407,6 +9529,23 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
9407
9529
|
delete u.parentId;
|
|
9408
9530
|
delete u.kind;
|
|
9409
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
|
+
}
|
|
9410
9549
|
if (Object.keys(updatePayload).length > 0) {
|
|
9411
9550
|
sanitizeBodyForEntity(repo, updatePayload);
|
|
9412
9551
|
await repo.update(numericId, updatePayload);
|
|
@@ -9436,6 +9575,11 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
9436
9575
|
where: { id: numericId, deleted: false }
|
|
9437
9576
|
});
|
|
9438
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
|
+
}
|
|
9439
9583
|
let deletedBy = null;
|
|
9440
9584
|
if (getDeletedByUserId) {
|
|
9441
9585
|
try {
|
|
@@ -12245,7 +12389,7 @@ var DEFAULT_ADMIN_NAV = [
|
|
|
12245
12389
|
];
|
|
12246
12390
|
|
|
12247
12391
|
// src/index.ts
|
|
12248
|
-
console.log("\u{1F525} USING LOCAL CMS
|
|
12392
|
+
console.log("\u{1F525} USING LOCAL CMS (index.ts loaded) \u{1F525}");
|
|
12249
12393
|
// Annotate the CommonJS export names for ESM import in node:
|
|
12250
12394
|
0 && (module.exports = {
|
|
12251
12395
|
ADMIN_GROUP_NAME,
|