@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.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
|
}
|
|
@@ -5323,15 +5336,36 @@ function createChatHandlers(config) {
|
|
|
5323
5336
|
const email = body?.email?.trim();
|
|
5324
5337
|
if (!name || !email) return json({ error: "name and email required" }, { status: 400 });
|
|
5325
5338
|
const repo = contactRepo();
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5339
|
+
const phone = body.phone?.trim() || null;
|
|
5340
|
+
const existing = await repo.findOne({ where: { email } });
|
|
5341
|
+
let contact;
|
|
5342
|
+
if (!existing) {
|
|
5343
|
+
const createdAt = /* @__PURE__ */ new Date();
|
|
5344
|
+
contact = await repo.save(
|
|
5345
|
+
repo.create({ name, email, phone, createdAt, updatedAt: createdAt })
|
|
5346
|
+
);
|
|
5347
|
+
} else {
|
|
5348
|
+
const row = existing;
|
|
5349
|
+
if (row.deleted) {
|
|
5350
|
+
await repo.update(row.id, {
|
|
5351
|
+
deleted: false,
|
|
5352
|
+
deletedAt: null,
|
|
5353
|
+
deletedBy: null,
|
|
5354
|
+
name,
|
|
5355
|
+
phone
|
|
5356
|
+
});
|
|
5357
|
+
const refreshed = await repo.findOne({ where: { id: row.id } });
|
|
5358
|
+
if (!refreshed) return json({ error: "Failed to identify", detail: "contact missing after reactivate" }, { status: 500 });
|
|
5359
|
+
contact = refreshed;
|
|
5360
|
+
} else {
|
|
5361
|
+
contact = existing;
|
|
5362
|
+
}
|
|
5330
5363
|
}
|
|
5331
5364
|
const convRepoInst = convRepo();
|
|
5332
|
-
const
|
|
5365
|
+
const contactId = contact.id;
|
|
5366
|
+
const conv = await convRepoInst.save(convRepoInst.create({ contactId }));
|
|
5333
5367
|
return json({
|
|
5334
|
-
contactId
|
|
5368
|
+
contactId,
|
|
5335
5369
|
conversationId: conv.id
|
|
5336
5370
|
});
|
|
5337
5371
|
} catch (err) {
|
|
@@ -8509,7 +8543,7 @@ function getNextAuthOptions(config) {
|
|
|
8509
8543
|
}
|
|
8510
8544
|
|
|
8511
8545
|
// src/api/crud.ts
|
|
8512
|
-
import {
|
|
8546
|
+
import { ILike as ILike2, MoreThan as MoreThan2, Not } from "typeorm";
|
|
8513
8547
|
var CRUD_LOG = "[cms-crud]";
|
|
8514
8548
|
function logCrudClientError(op, detail) {
|
|
8515
8549
|
console.warn(CRUD_LOG, op, detail);
|
|
@@ -8517,6 +8551,40 @@ function logCrudClientError(op, detail) {
|
|
|
8517
8551
|
function logCrudServerError(op, detail) {
|
|
8518
8552
|
console.error(CRUD_LOG, op, detail);
|
|
8519
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
|
+
}
|
|
8520
8588
|
var DATE_COLUMN_TYPES = /* @__PURE__ */ new Set([
|
|
8521
8589
|
"date",
|
|
8522
8590
|
"datetime",
|
|
@@ -8581,6 +8649,16 @@ function mergeDeletedFalseWhere(repo, where) {
|
|
|
8581
8649
|
}
|
|
8582
8650
|
return Object.keys(where).length > 0 ? { ...where, ...d } : d;
|
|
8583
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
|
+
}
|
|
8584
8662
|
function buildSoftDeletePayload(meta, deletedBy) {
|
|
8585
8663
|
const payload = { deleted: true };
|
|
8586
8664
|
if (meta.columns.some((c) => c.propertyName === "deletedAt")) {
|
|
@@ -8803,19 +8881,36 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
8803
8881
|
qb.andWhere("m.filename ILIKE :search", { search: `%${search.trim()}%` });
|
|
8804
8882
|
}
|
|
8805
8883
|
if (typeFilter) {
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8811
|
-
})
|
|
8812
|
-
)
|
|
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
|
+
}
|
|
8813
8899
|
}
|
|
8814
8900
|
const allowedSort = ["filename", "createdAt", "id"];
|
|
8815
8901
|
const sf = allowedSort.includes(sortFieldRaw) ? sortFieldRaw : "filename";
|
|
8816
8902
|
const so = sortOrder === "DESC" ? "DESC" : "ASC";
|
|
8817
|
-
qb.orderBy(
|
|
8818
|
-
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
|
+
}
|
|
8819
8914
|
return json({ total: total2, page, limit, totalPages: Math.ceil(total2 / limit), data: data2 });
|
|
8820
8915
|
}
|
|
8821
8916
|
const sortField = columnNames.has(sortFieldRaw) ? sortFieldRaw : "createdAt";
|
|
@@ -8926,6 +9021,20 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
8926
9021
|
});
|
|
8927
9022
|
return json({ error: "Invalid request payload" }, { status: 400 });
|
|
8928
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
|
+
}
|
|
8929
9038
|
sanitizeBodyForEntity(repo, persistBody);
|
|
8930
9039
|
let created;
|
|
8931
9040
|
try {
|
|
@@ -9120,7 +9229,20 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
9120
9229
|
relations: ["order", "order.contact", "contact"]
|
|
9121
9230
|
});
|
|
9122
9231
|
if (!payment) return json({ message: "Not found" }, { status: 404 });
|
|
9123
|
-
|
|
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
|
+
});
|
|
9124
9246
|
}
|
|
9125
9247
|
if (resource === "blogs") {
|
|
9126
9248
|
const blog = await repo.findOne({
|
|
@@ -9235,6 +9357,23 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
9235
9357
|
delete u.parentId;
|
|
9236
9358
|
delete u.kind;
|
|
9237
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
|
+
}
|
|
9238
9377
|
if (Object.keys(updatePayload).length > 0) {
|
|
9239
9378
|
sanitizeBodyForEntity(repo, updatePayload);
|
|
9240
9379
|
await repo.update(numericId, updatePayload);
|
|
@@ -9264,6 +9403,11 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
9264
9403
|
where: { id: numericId, deleted: false }
|
|
9265
9404
|
});
|
|
9266
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
|
+
}
|
|
9267
9411
|
let deletedBy = null;
|
|
9268
9412
|
if (getDeletedByUserId) {
|
|
9269
9413
|
try {
|
|
@@ -12073,7 +12217,7 @@ var DEFAULT_ADMIN_NAV = [
|
|
|
12073
12217
|
];
|
|
12074
12218
|
|
|
12075
12219
|
// src/index.ts
|
|
12076
|
-
console.log("\u{1F525} USING LOCAL CMS
|
|
12220
|
+
console.log("\u{1F525} USING LOCAL CMS (index.ts loaded) \u{1F525}");
|
|
12077
12221
|
export {
|
|
12078
12222
|
ADMIN_GROUP_NAME,
|
|
12079
12223
|
Address,
|