@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/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 +142 -16
- package/dist/api.cjs.map +1 -1
- package/dist/api.js +143 -17
- package/dist/api.js.map +1 -1
- package/dist/index.cjs +143 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +144 -18
- 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
|
}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
8997
|
-
|
|
8998
|
-
|
|
8999
|
-
|
|
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(
|
|
9008
|
-
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
|
+
}
|
|
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
|
-
|
|
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
|
|
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,
|