@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.js
CHANGED
|
@@ -4207,12 +4207,14 @@ function createDashboardStatsHandler(config) {
|
|
|
4207
4207
|
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3);
|
|
4208
4208
|
const repo = (name) => entityMap[name] ? dataSource.getRepository(entityMap[name]) : void 0;
|
|
4209
4209
|
const [contactsCount, formsCount, formSubmissionsCount, usersCount, blogsCount, recentContacts, recentSubmissions, contactTypeRows] = await Promise.all([
|
|
4210
|
-
repo("contacts")?.count() ?? 0,
|
|
4210
|
+
repo("contacts")?.count({ where: { deleted: false } }) ?? 0,
|
|
4211
4211
|
repo("forms")?.count({ where: { deleted: false } }) ?? 0,
|
|
4212
4212
|
repo("form_submissions")?.count() ?? 0,
|
|
4213
4213
|
repo("users")?.count({ where: { deleted: false } }) ?? 0,
|
|
4214
4214
|
repo("blogs")?.count({ where: { deleted: false } }) ?? 0,
|
|
4215
|
-
repo("contacts")?.count({
|
|
4215
|
+
repo("contacts")?.count({
|
|
4216
|
+
where: { deleted: false, createdAt: MoreThanOrEqual(sevenDaysAgo) }
|
|
4217
|
+
}) ?? 0,
|
|
4216
4218
|
repo("form_submissions")?.count({ where: { createdAt: MoreThanOrEqual(sevenDaysAgo) } }) ?? 0,
|
|
4217
4219
|
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() ?? []
|
|
4218
4220
|
]);
|
|
@@ -4845,11 +4847,14 @@ function createFormSubmissionHandler(config) {
|
|
|
4845
4847
|
const contactRepo = dataSource.getRepository(entityMap.contacts);
|
|
4846
4848
|
let contact = await contactRepo.findOne({ where: { email: contactData.email } });
|
|
4847
4849
|
if (!contact) {
|
|
4850
|
+
const createdAt = /* @__PURE__ */ new Date();
|
|
4848
4851
|
contact = await contactRepo.save(
|
|
4849
4852
|
contactRepo.create({
|
|
4850
4853
|
name: contactData.name,
|
|
4851
4854
|
email: contactData.email,
|
|
4852
|
-
phone: contactData.phone
|
|
4855
|
+
phone: contactData.phone,
|
|
4856
|
+
createdAt,
|
|
4857
|
+
updatedAt: createdAt
|
|
4853
4858
|
})
|
|
4854
4859
|
);
|
|
4855
4860
|
}
|
|
@@ -5005,12 +5010,13 @@ function createUsersApiHandlers(config) {
|
|
|
5005
5010
|
const gid = body.groupId ?? null;
|
|
5006
5011
|
const isCustomer = !!(customerG && gid === customerG.id);
|
|
5007
5012
|
const adminAccess = isCustomer ? false : body.adminAccess === false ? false : true;
|
|
5013
|
+
const blocked = body.blocked === true || body.blocked === "true" || body.blocked === 1 || body.blocked === "1";
|
|
5008
5014
|
const newUser = await userRepo().save(
|
|
5009
5015
|
userRepo().create({
|
|
5010
5016
|
name: body.name,
|
|
5011
5017
|
email: body.email,
|
|
5012
5018
|
password: null,
|
|
5013
|
-
blocked
|
|
5019
|
+
blocked,
|
|
5014
5020
|
groupId: gid,
|
|
5015
5021
|
adminAccess
|
|
5016
5022
|
})
|
|
@@ -5025,7 +5031,14 @@ function createUsersApiHandlers(config) {
|
|
|
5025
5031
|
inviteLink,
|
|
5026
5032
|
newUser.name ?? ""
|
|
5027
5033
|
);
|
|
5028
|
-
return json(
|
|
5034
|
+
return json(
|
|
5035
|
+
{
|
|
5036
|
+
message: blocked ? "User created successfully (blocked until password is set)" : "User created successfully",
|
|
5037
|
+
user: newUser,
|
|
5038
|
+
inviteLink
|
|
5039
|
+
},
|
|
5040
|
+
{ status: 201 }
|
|
5041
|
+
);
|
|
5029
5042
|
} catch {
|
|
5030
5043
|
return json({ error: "Server Error" }, { status: 500 });
|
|
5031
5044
|
}
|
|
@@ -5327,7 +5340,10 @@ function createChatHandlers(config) {
|
|
|
5327
5340
|
const existing = await repo.findOne({ where: { email } });
|
|
5328
5341
|
let contact;
|
|
5329
5342
|
if (!existing) {
|
|
5330
|
-
|
|
5343
|
+
const createdAt = /* @__PURE__ */ new Date();
|
|
5344
|
+
contact = await repo.save(
|
|
5345
|
+
repo.create({ name, email, phone, createdAt, updatedAt: createdAt })
|
|
5346
|
+
);
|
|
5331
5347
|
} else {
|
|
5332
5348
|
const row = existing;
|
|
5333
5349
|
if (row.deleted) {
|
|
@@ -8527,7 +8543,7 @@ function getNextAuthOptions(config) {
|
|
|
8527
8543
|
}
|
|
8528
8544
|
|
|
8529
8545
|
// src/api/crud.ts
|
|
8530
|
-
import {
|
|
8546
|
+
import { ILike as ILike2, MoreThan as MoreThan2, Not } from "typeorm";
|
|
8531
8547
|
var CRUD_LOG = "[cms-crud]";
|
|
8532
8548
|
function logCrudClientError(op, detail) {
|
|
8533
8549
|
console.warn(CRUD_LOG, op, detail);
|
|
@@ -8535,6 +8551,40 @@ function logCrudClientError(op, detail) {
|
|
|
8535
8551
|
function logCrudServerError(op, detail) {
|
|
8536
8552
|
console.error(CRUD_LOG, op, detail);
|
|
8537
8553
|
}
|
|
8554
|
+
async function aggregateMediaFolderFileSizes(dataSource, folderIds) {
|
|
8555
|
+
const map = /* @__PURE__ */ new Map();
|
|
8556
|
+
if (folderIds.length === 0) return map;
|
|
8557
|
+
try {
|
|
8558
|
+
const rows = await dataSource.query(
|
|
8559
|
+
`
|
|
8560
|
+
WITH RECURSIVE walk AS (
|
|
8561
|
+
SELECT m."parentId" AS root_id, m.id, m.kind, m.size
|
|
8562
|
+
FROM media m
|
|
8563
|
+
WHERE m."parentId" = ANY($1) AND m.deleted = false
|
|
8564
|
+
UNION ALL
|
|
8565
|
+
SELECT w.root_id, m.id, m.kind, m.size
|
|
8566
|
+
FROM walk w
|
|
8567
|
+
INNER JOIN media m ON m."parentId" = w.id AND m.deleted = false
|
|
8568
|
+
)
|
|
8569
|
+
SELECT root_id AS "rootId", COALESCE(SUM(size) FILTER (WHERE kind = 'file'), 0)::bigint AS "totalSize"
|
|
8570
|
+
FROM walk
|
|
8571
|
+
GROUP BY root_id
|
|
8572
|
+
`,
|
|
8573
|
+
[folderIds]
|
|
8574
|
+
);
|
|
8575
|
+
for (const r of rows) {
|
|
8576
|
+
map.set(Number(r.rootId), Number(r.totalSize));
|
|
8577
|
+
}
|
|
8578
|
+
for (const id of folderIds) {
|
|
8579
|
+
if (!map.has(id)) map.set(id, 0);
|
|
8580
|
+
}
|
|
8581
|
+
} catch (err) {
|
|
8582
|
+
logCrudServerError("media folder size aggregate failed", {
|
|
8583
|
+
message: err instanceof Error ? err.message : String(err)
|
|
8584
|
+
});
|
|
8585
|
+
}
|
|
8586
|
+
return map;
|
|
8587
|
+
}
|
|
8538
8588
|
var DATE_COLUMN_TYPES = /* @__PURE__ */ new Set([
|
|
8539
8589
|
"date",
|
|
8540
8590
|
"datetime",
|
|
@@ -8599,6 +8649,16 @@ function mergeDeletedFalseWhere(repo, where) {
|
|
|
8599
8649
|
}
|
|
8600
8650
|
return Object.keys(where).length > 0 ? { ...where, ...d } : d;
|
|
8601
8651
|
}
|
|
8652
|
+
function normalizeProductSku(value) {
|
|
8653
|
+
if (value == null) return null;
|
|
8654
|
+
const s = String(value).trim();
|
|
8655
|
+
return s === "" ? null : s;
|
|
8656
|
+
}
|
|
8657
|
+
async function assertProductSkuUnique(repo, sku, excludeId) {
|
|
8658
|
+
const where = excludeId != null ? { sku, deleted: false, id: Not(excludeId) } : { sku, deleted: false };
|
|
8659
|
+
const row = await repo.findOne({ where });
|
|
8660
|
+
return row == null;
|
|
8661
|
+
}
|
|
8602
8662
|
function buildSoftDeletePayload(meta, deletedBy) {
|
|
8603
8663
|
const payload = { deleted: true };
|
|
8604
8664
|
if (meta.columns.some((c) => c.propertyName === "deletedAt")) {
|
|
@@ -8821,19 +8881,36 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
8821
8881
|
qb.andWhere("m.filename ILIKE :search", { search: `%${search.trim()}%` });
|
|
8822
8882
|
}
|
|
8823
8883
|
if (typeFilter) {
|
|
8824
|
-
|
|
8825
|
-
|
|
8826
|
-
|
|
8827
|
-
|
|
8828
|
-
|
|
8829
|
-
})
|
|
8830
|
-
)
|
|
8884
|
+
if (typeFilter === "folder") {
|
|
8885
|
+
qb.andWhere("m.kind = :folderKind", { folderKind: "folder" });
|
|
8886
|
+
} else if (typeFilter === "file") {
|
|
8887
|
+
qb.andWhere("m.kind = :fileKind", { fileKind: "file" });
|
|
8888
|
+
} else if (typeFilter === "image") {
|
|
8889
|
+
qb.andWhere("m.mimeType LIKE :imageMimeType", { imageMimeType: "image/%" });
|
|
8890
|
+
} else if (typeFilter === "video") {
|
|
8891
|
+
qb.andWhere("m.mimeType LIKE :videoMimeType", { videoMimeType: "video/%" });
|
|
8892
|
+
} else if (typeFilter === "audio") {
|
|
8893
|
+
qb.andWhere("m.mimeType LIKE :audioMimeType", { audioMimeType: "audio/%" });
|
|
8894
|
+
} else if (typeFilter === "Document") {
|
|
8895
|
+
qb.andWhere("m.mimeType LIKE :documentMimeType", { documentMimeType: "application/pdf" });
|
|
8896
|
+
} else if (typeFilter === "application") {
|
|
8897
|
+
qb.andWhere("m.kind = :folderKind", { folderKind: "folder" });
|
|
8898
|
+
}
|
|
8831
8899
|
}
|
|
8832
8900
|
const allowedSort = ["filename", "createdAt", "id"];
|
|
8833
8901
|
const sf = allowedSort.includes(sortFieldRaw) ? sortFieldRaw : "filename";
|
|
8834
8902
|
const so = sortOrder === "DESC" ? "DESC" : "ASC";
|
|
8835
|
-
qb.orderBy(
|
|
8836
|
-
const [
|
|
8903
|
+
qb.orderBy(`m.${sf}`, so).skip(skip).take(limit);
|
|
8904
|
+
const [rows, total2] = await qb.getManyAndCount();
|
|
8905
|
+
const mediaRows = rows;
|
|
8906
|
+
const folderIds = mediaRows.filter((m) => m.kind === "folder").map((m) => m.id);
|
|
8907
|
+
let data2 = rows;
|
|
8908
|
+
if (folderIds.length > 0) {
|
|
8909
|
+
const sizeMap = await aggregateMediaFolderFileSizes(dataSource, folderIds);
|
|
8910
|
+
data2 = mediaRows.map(
|
|
8911
|
+
(m) => m.kind === "folder" ? { ...m, size: sizeMap.get(m.id) ?? 0 } : m
|
|
8912
|
+
);
|
|
8913
|
+
}
|
|
8837
8914
|
return json({ total: total2, page, limit, totalPages: Math.ceil(total2 / limit), data: data2 });
|
|
8838
8915
|
}
|
|
8839
8916
|
const sortField = columnNames.has(sortFieldRaw) ? sortFieldRaw : "createdAt";
|
|
@@ -8944,6 +9021,20 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
8944
9021
|
});
|
|
8945
9022
|
return json({ error: "Invalid request payload" }, { status: 400 });
|
|
8946
9023
|
}
|
|
9024
|
+
if (resource === "products") {
|
|
9025
|
+
if ("sku" in persistBody) {
|
|
9026
|
+
const skuNorm = normalizeProductSku(persistBody.sku);
|
|
9027
|
+
if (skuNorm) {
|
|
9028
|
+
const ok = await assertProductSkuUnique(repo, skuNorm);
|
|
9029
|
+
if (!ok) {
|
|
9030
|
+
return json({ error: "SKU already exists. Please use a unique SKU." }, { status: 400 });
|
|
9031
|
+
}
|
|
9032
|
+
persistBody.sku = skuNorm;
|
|
9033
|
+
} else {
|
|
9034
|
+
persistBody.sku = null;
|
|
9035
|
+
}
|
|
9036
|
+
}
|
|
9037
|
+
}
|
|
8947
9038
|
sanitizeBodyForEntity(repo, persistBody);
|
|
8948
9039
|
let created;
|
|
8949
9040
|
try {
|
|
@@ -9138,7 +9229,20 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
9138
9229
|
relations: ["order", "order.contact", "contact"]
|
|
9139
9230
|
});
|
|
9140
9231
|
if (!payment) return json({ message: "Not found" }, { status: 404 });
|
|
9141
|
-
|
|
9232
|
+
const p = payment;
|
|
9233
|
+
const order = p.order;
|
|
9234
|
+
const orderContact = order?.contact;
|
|
9235
|
+
const contact = p.contact;
|
|
9236
|
+
const customer = orderContact ?? contact;
|
|
9237
|
+
return json({
|
|
9238
|
+
...p,
|
|
9239
|
+
order: order ? {
|
|
9240
|
+
id: order.id,
|
|
9241
|
+
orderNumber: order.orderNumber,
|
|
9242
|
+
contact: orderContact ? { name: orderContact.name, email: orderContact.email } : null
|
|
9243
|
+
} : null,
|
|
9244
|
+
contact: customer ? { id: customer.id, name: customer.name, email: customer.email } : null
|
|
9245
|
+
});
|
|
9142
9246
|
}
|
|
9143
9247
|
if (resource === "blogs") {
|
|
9144
9248
|
const blog = await repo.findOne({
|
|
@@ -9253,6 +9357,23 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
9253
9357
|
delete u.parentId;
|
|
9254
9358
|
delete u.kind;
|
|
9255
9359
|
}
|
|
9360
|
+
if (resource === "products") {
|
|
9361
|
+
const currentRow = await repo.findOne({
|
|
9362
|
+
where: { id: numericId, deleted: false }
|
|
9363
|
+
});
|
|
9364
|
+
if (!currentRow) return json({ message: "Not found" }, { status: 404 });
|
|
9365
|
+
const merged = { ...currentRow, ...updatePayload };
|
|
9366
|
+
const effSku = normalizeProductSku(merged.sku);
|
|
9367
|
+
if (effSku) {
|
|
9368
|
+
const ok = await assertProductSkuUnique(repo, effSku, numericId);
|
|
9369
|
+
if (!ok) {
|
|
9370
|
+
return json({ error: "SKU already exists. Please use a unique SKU." }, { status: 400 });
|
|
9371
|
+
}
|
|
9372
|
+
}
|
|
9373
|
+
if ("sku" in updatePayload) {
|
|
9374
|
+
updatePayload.sku = effSku;
|
|
9375
|
+
}
|
|
9376
|
+
}
|
|
9256
9377
|
if (Object.keys(updatePayload).length > 0) {
|
|
9257
9378
|
sanitizeBodyForEntity(repo, updatePayload);
|
|
9258
9379
|
await repo.update(numericId, updatePayload);
|
|
@@ -9282,6 +9403,11 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
9282
9403
|
where: { id: numericId, deleted: false }
|
|
9283
9404
|
});
|
|
9284
9405
|
if (!existing) return json({ message: "Not found" }, { status: 404 });
|
|
9406
|
+
if (resource === "contacts") {
|
|
9407
|
+
const result2 = await repo.delete(numericId);
|
|
9408
|
+
if (result2.affected === 0) return json({ message: "Not found" }, { status: 404 });
|
|
9409
|
+
return json({ message: "Deleted successfully" }, { status: 200 });
|
|
9410
|
+
}
|
|
9285
9411
|
let deletedBy = null;
|
|
9286
9412
|
if (getDeletedByUserId) {
|
|
9287
9413
|
try {
|
|
@@ -12091,7 +12217,7 @@ var DEFAULT_ADMIN_NAV = [
|
|
|
12091
12217
|
];
|
|
12092
12218
|
|
|
12093
12219
|
// src/index.ts
|
|
12094
|
-
console.log("\u{1F525} USING LOCAL CMS
|
|
12220
|
+
console.log("\u{1F525} USING LOCAL CMS (index.ts loaded) \u{1F525}");
|
|
12095
12221
|
export {
|
|
12096
12222
|
ADMIN_GROUP_NAME,
|
|
12097
12223
|
Address,
|