@infuro/cms-core 1.0.23 → 1.0.24
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 +2595 -1681
- package/dist/admin.cjs.map +1 -1
- package/dist/admin.d.cts +29 -7
- package/dist/admin.d.ts +29 -7
- package/dist/admin.js +2585 -1668
- package/dist/admin.js.map +1 -1
- package/dist/api.cjs +183 -18
- package/dist/api.cjs.map +1 -1
- package/dist/api.js +183 -18
- package/dist/api.js.map +1 -1
- package/dist/index.cjs +185 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +185 -18
- package/dist/index.js.map +1 -1
- package/package.json +130 -129
package/dist/index.js
CHANGED
|
@@ -3979,11 +3979,11 @@ async function readBufferFromPublicUrl(url) {
|
|
|
3979
3979
|
throw new Error("Unsupported media URL");
|
|
3980
3980
|
}
|
|
3981
3981
|
function sanitizeZipPath(entryName) {
|
|
3982
|
-
const
|
|
3983
|
-
for (const seg of
|
|
3982
|
+
const norm3 = entryName.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
3983
|
+
for (const seg of norm3) {
|
|
3984
3984
|
if (seg === ".." || seg === ".") return null;
|
|
3985
3985
|
}
|
|
3986
|
-
return
|
|
3986
|
+
return norm3;
|
|
3987
3987
|
}
|
|
3988
3988
|
function shouldSkipEntry(parts) {
|
|
3989
3989
|
if (parts[0] === "__MACOSX") return true;
|
|
@@ -8544,6 +8544,66 @@ function getNextAuthOptions(config) {
|
|
|
8544
8544
|
|
|
8545
8545
|
// src/api/crud.ts
|
|
8546
8546
|
import { ILike as ILike2, MoreThan as MoreThan2, Not } from "typeorm";
|
|
8547
|
+
|
|
8548
|
+
// src/lib/address-geo-validation.ts
|
|
8549
|
+
import { Country, State, City } from "country-state-city";
|
|
8550
|
+
function norm2(s) {
|
|
8551
|
+
return typeof s === "string" ? s.trim() : "";
|
|
8552
|
+
}
|
|
8553
|
+
function resolveCountry(input) {
|
|
8554
|
+
const t = input.trim();
|
|
8555
|
+
if (!t) return void 0;
|
|
8556
|
+
if (t.length === 2) {
|
|
8557
|
+
const byCode = Country.getCountryByCode(t.toUpperCase());
|
|
8558
|
+
if (byCode) return byCode;
|
|
8559
|
+
}
|
|
8560
|
+
const lower = t.toLowerCase();
|
|
8561
|
+
return Country.getAllCountries().find((c) => c.name.toLowerCase() === lower);
|
|
8562
|
+
}
|
|
8563
|
+
function resolveState(countryIso, input) {
|
|
8564
|
+
const t = input.trim();
|
|
8565
|
+
if (!t || !countryIso) return void 0;
|
|
8566
|
+
const states = State.getStatesOfCountry(countryIso);
|
|
8567
|
+
const lower = t.toLowerCase();
|
|
8568
|
+
return states.find((s) => s.isoCode.toLowerCase() === t.toLowerCase() || s.name.toLowerCase() === lower);
|
|
8569
|
+
}
|
|
8570
|
+
function resolveCity(countryIso, stateIso, input) {
|
|
8571
|
+
const t = input.trim();
|
|
8572
|
+
if (!t || !countryIso || !stateIso) return void 0;
|
|
8573
|
+
const lower = t.toLowerCase();
|
|
8574
|
+
const cities = City.getCitiesOfState(countryIso, stateIso);
|
|
8575
|
+
return cities.find((c) => c.name.toLowerCase() === lower);
|
|
8576
|
+
}
|
|
8577
|
+
function assertValidAddressHierarchy(country, state, city) {
|
|
8578
|
+
const c = resolveCountry(country);
|
|
8579
|
+
if (!c) return { ok: false, error: "Invalid or unknown country." };
|
|
8580
|
+
const st = resolveState(c.isoCode, state);
|
|
8581
|
+
if (!st) return { ok: false, error: "State or province does not match the selected country." };
|
|
8582
|
+
const ct = resolveCity(c.isoCode, st.isoCode, city);
|
|
8583
|
+
if (!ct) return { ok: false, error: "City does not match the selected state." };
|
|
8584
|
+
return { ok: true, country: c.name, state: st.name, city: ct.name };
|
|
8585
|
+
}
|
|
8586
|
+
function validateAndNormalizeAddressRow(row) {
|
|
8587
|
+
const line1 = norm2(row.line1);
|
|
8588
|
+
const postalCode = norm2(row.postalCode);
|
|
8589
|
+
const countryIn = norm2(row.country);
|
|
8590
|
+
const stateIn = norm2(row.state);
|
|
8591
|
+
const cityIn = norm2(row.city);
|
|
8592
|
+
if (!line1) return "Street address (line 1) is required.";
|
|
8593
|
+
if (!postalCode) return "Postal code is required.";
|
|
8594
|
+
if (!countryIn || !stateIn || !cityIn) return "Country, state, and city are required.";
|
|
8595
|
+
const geo = assertValidAddressHierarchy(countryIn, stateIn, cityIn);
|
|
8596
|
+
if (!geo.ok) return geo.error;
|
|
8597
|
+
row.line1 = line1;
|
|
8598
|
+
row.line2 = norm2(row.line2) || null;
|
|
8599
|
+
row.postalCode = postalCode;
|
|
8600
|
+
row.country = geo.country;
|
|
8601
|
+
row.state = geo.state;
|
|
8602
|
+
row.city = geo.city;
|
|
8603
|
+
return null;
|
|
8604
|
+
}
|
|
8605
|
+
|
|
8606
|
+
// src/api/crud.ts
|
|
8547
8607
|
var CRUD_LOG = "[cms-crud]";
|
|
8548
8608
|
function logCrudClientError(op, detail) {
|
|
8549
8609
|
console.warn(CRUD_LOG, op, detail);
|
|
@@ -8813,6 +8873,17 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
8813
8873
|
if (statusFilter) productWhere.status = statusFilter;
|
|
8814
8874
|
if (inventory === "in_stock") productWhere.quantity = MoreThan2(0);
|
|
8815
8875
|
if (inventory === "out_of_stock") productWhere.quantity = 0;
|
|
8876
|
+
for (const key of ["brandId", "categoryId", "collectionId"]) {
|
|
8877
|
+
const raw = searchParams.get(key)?.trim();
|
|
8878
|
+
if (raw) {
|
|
8879
|
+
const n = Number(raw);
|
|
8880
|
+
if (Number.isFinite(n)) productWhere[key] = n;
|
|
8881
|
+
}
|
|
8882
|
+
}
|
|
8883
|
+
const featuredRaw = searchParams.get("featured")?.trim();
|
|
8884
|
+
if (featuredRaw === "true" || featuredRaw === "false") {
|
|
8885
|
+
productWhere.featured = featuredRaw === "true";
|
|
8886
|
+
}
|
|
8816
8887
|
if (search && typeof search === "string" && search.trim()) {
|
|
8817
8888
|
productWhere.name = ILike2(`%${search.trim()}%`);
|
|
8818
8889
|
}
|
|
@@ -8918,7 +8989,7 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
8918
8989
|
if (search) {
|
|
8919
8990
|
where = buildSearchWhereClause(repo, search);
|
|
8920
8991
|
}
|
|
8921
|
-
const intFilterKeys = ["productId", "attributeId", "taxId"];
|
|
8992
|
+
const intFilterKeys = ["productId", "attributeId", "taxId", "brandId", "categoryId", "collectionId", "parentId"];
|
|
8922
8993
|
const extraWhere = {};
|
|
8923
8994
|
for (const key of intFilterKeys) {
|
|
8924
8995
|
const v = searchParams.get(key);
|
|
@@ -8927,6 +8998,15 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
8927
8998
|
if (Number.isFinite(n)) extraWhere[key] = n;
|
|
8928
8999
|
}
|
|
8929
9000
|
}
|
|
9001
|
+
for (const col of repo.metadata.columns) {
|
|
9002
|
+
if (String(col.type) !== "boolean") continue;
|
|
9003
|
+
const name = col.propertyName;
|
|
9004
|
+
if (!columnNames.has(name)) continue;
|
|
9005
|
+
const raw = searchParams.get(name)?.trim();
|
|
9006
|
+
if (raw === "true" || raw === "false") {
|
|
9007
|
+
extraWhere[name] = raw === "true";
|
|
9008
|
+
}
|
|
9009
|
+
}
|
|
8930
9010
|
if (Object.keys(extraWhere).length > 0) {
|
|
8931
9011
|
if (Array.isArray(where)) {
|
|
8932
9012
|
where = where.map((w) => ({ ...w, ...extraWhere }));
|
|
@@ -9035,6 +9115,17 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
9035
9115
|
}
|
|
9036
9116
|
}
|
|
9037
9117
|
}
|
|
9118
|
+
if (resource === "addresses") {
|
|
9119
|
+
const cid = Number(persistBody.contactId);
|
|
9120
|
+
if (!Number.isFinite(cid)) {
|
|
9121
|
+
return json({ error: "Valid contactId is required." }, { status: 400 });
|
|
9122
|
+
}
|
|
9123
|
+
if (persistBody.tag === "") persistBody.tag = null;
|
|
9124
|
+
const addrErr = validateAndNormalizeAddressRow(persistBody);
|
|
9125
|
+
if (addrErr) {
|
|
9126
|
+
return json({ error: addrErr }, { status: 400 });
|
|
9127
|
+
}
|
|
9128
|
+
}
|
|
9038
9129
|
sanitizeBodyForEntity(repo, persistBody);
|
|
9039
9130
|
let created;
|
|
9040
9131
|
try {
|
|
@@ -9354,8 +9445,54 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
9354
9445
|
const updatePayload = rawBody && typeof rawBody === "object" ? pickColumnUpdates(repo, rawBody) : {};
|
|
9355
9446
|
if (resource === "media") {
|
|
9356
9447
|
const u = updatePayload;
|
|
9357
|
-
delete u.parentId;
|
|
9358
9448
|
delete u.kind;
|
|
9449
|
+
if (rawBody && typeof rawBody === "object" && "parentId" in rawBody) {
|
|
9450
|
+
let pid = null;
|
|
9451
|
+
const p = rawBody.parentId;
|
|
9452
|
+
if (p != null && p !== "") {
|
|
9453
|
+
const n = Number(p);
|
|
9454
|
+
if (!Number.isFinite(n)) {
|
|
9455
|
+
return json({ error: "Invalid parentId" }, { status: 400 });
|
|
9456
|
+
}
|
|
9457
|
+
pid = n;
|
|
9458
|
+
}
|
|
9459
|
+
if (pid != null) {
|
|
9460
|
+
const parent = await repo.findOne({
|
|
9461
|
+
where: { id: pid, deleted: false }
|
|
9462
|
+
});
|
|
9463
|
+
if (!parent || parent.kind !== "folder") {
|
|
9464
|
+
return json({ error: "parent must be a folder" }, { status: 400 });
|
|
9465
|
+
}
|
|
9466
|
+
}
|
|
9467
|
+
const row = await repo.findOne({
|
|
9468
|
+
where: { id: numericId, deleted: false }
|
|
9469
|
+
});
|
|
9470
|
+
if (!row) return json({ message: "Not found" }, { status: 404 });
|
|
9471
|
+
if (pid === numericId) {
|
|
9472
|
+
return json({ error: "Invalid parentId" }, { status: 400 });
|
|
9473
|
+
}
|
|
9474
|
+
if (row.kind === "folder" && pid != null) {
|
|
9475
|
+
let walk = pid;
|
|
9476
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9477
|
+
while (walk != null) {
|
|
9478
|
+
if (walk === numericId) {
|
|
9479
|
+
return json(
|
|
9480
|
+
{ error: "Cannot move a folder into itself or a descendant folder" },
|
|
9481
|
+
{ status: 400 }
|
|
9482
|
+
);
|
|
9483
|
+
}
|
|
9484
|
+
if (seen.has(walk)) break;
|
|
9485
|
+
seen.add(walk);
|
|
9486
|
+
const anc = await repo.findOne({
|
|
9487
|
+
where: { id: walk, deleted: false }
|
|
9488
|
+
});
|
|
9489
|
+
walk = anc ? anc.parentId ?? null : null;
|
|
9490
|
+
}
|
|
9491
|
+
}
|
|
9492
|
+
u.parentId = pid;
|
|
9493
|
+
} else {
|
|
9494
|
+
delete u.parentId;
|
|
9495
|
+
}
|
|
9359
9496
|
}
|
|
9360
9497
|
if (resource === "products") {
|
|
9361
9498
|
const currentRow = await repo.findOne({
|
|
@@ -9374,6 +9511,26 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
9374
9511
|
updatePayload.sku = effSku;
|
|
9375
9512
|
}
|
|
9376
9513
|
}
|
|
9514
|
+
if (resource === "addresses" && Object.keys(updatePayload).length > 0) {
|
|
9515
|
+
const currentRow = await repo.findOne({
|
|
9516
|
+
where: { id: numericId }
|
|
9517
|
+
});
|
|
9518
|
+
if (!currentRow) return json({ message: "Not found" }, { status: 404 });
|
|
9519
|
+
const merged = {
|
|
9520
|
+
...currentRow,
|
|
9521
|
+
...updatePayload
|
|
9522
|
+
};
|
|
9523
|
+
if (merged.tag === "") merged.tag = null;
|
|
9524
|
+
const addrErr = validateAndNormalizeAddressRow(merged);
|
|
9525
|
+
if (addrErr) {
|
|
9526
|
+
return json({ error: addrErr }, { status: 400 });
|
|
9527
|
+
}
|
|
9528
|
+
for (const k of Object.keys(updatePayload)) {
|
|
9529
|
+
if (k in merged) {
|
|
9530
|
+
updatePayload[k] = merged[k];
|
|
9531
|
+
}
|
|
9532
|
+
}
|
|
9533
|
+
}
|
|
9377
9534
|
if (Object.keys(updatePayload).length > 0) {
|
|
9378
9535
|
sanitizeBodyForEntity(repo, updatePayload);
|
|
9379
9536
|
await repo.update(numericId, updatePayload);
|
|
@@ -11375,18 +11532,19 @@ function createStorefrontApiHandler(config) {
|
|
|
11375
11532
|
const contactOrErr = await getContactForAddresses();
|
|
11376
11533
|
if (contactOrErr instanceof Response) return contactOrErr;
|
|
11377
11534
|
const b = await req.json().catch(() => ({}));
|
|
11378
|
-
const
|
|
11379
|
-
|
|
11380
|
-
|
|
11381
|
-
|
|
11382
|
-
|
|
11383
|
-
|
|
11384
|
-
|
|
11385
|
-
|
|
11386
|
-
|
|
11387
|
-
|
|
11388
|
-
|
|
11389
|
-
);
|
|
11535
|
+
const row = {
|
|
11536
|
+
contactId: contactOrErr.contactId,
|
|
11537
|
+
tag: typeof b.tag === "string" ? b.tag.trim() || null : null,
|
|
11538
|
+
line1: typeof b.line1 === "string" ? b.line1 : "",
|
|
11539
|
+
line2: typeof b.line2 === "string" ? b.line2.trim() || null : null,
|
|
11540
|
+
city: typeof b.city === "string" ? b.city : "",
|
|
11541
|
+
state: typeof b.state === "string" ? b.state : "",
|
|
11542
|
+
postalCode: typeof b.postalCode === "string" ? b.postalCode : "",
|
|
11543
|
+
country: typeof b.country === "string" ? b.country : ""
|
|
11544
|
+
};
|
|
11545
|
+
const addrErr = validateAndNormalizeAddressRow(row);
|
|
11546
|
+
if (addrErr) return json({ error: addrErr }, { status: 400 });
|
|
11547
|
+
const created = await addressRepo().save(addressRepo().create(row));
|
|
11390
11548
|
return json(serializeAddress2(created));
|
|
11391
11549
|
}
|
|
11392
11550
|
if (path[0] === "addresses" && path.length === 2 && (method === "PATCH" || method === "PUT")) {
|
|
@@ -11405,7 +11563,16 @@ function createStorefrontApiHandler(config) {
|
|
|
11405
11563
|
if (b.state !== void 0) updates.state = typeof b.state === "string" ? b.state.trim() || null : null;
|
|
11406
11564
|
if (b.postalCode !== void 0) updates.postalCode = typeof b.postalCode === "string" ? b.postalCode.trim() || null : null;
|
|
11407
11565
|
if (b.country !== void 0) updates.country = typeof b.country === "string" ? b.country.trim() || null : null;
|
|
11408
|
-
if (Object.keys(updates).length)
|
|
11566
|
+
if (Object.keys(updates).length) {
|
|
11567
|
+
const merged = { ...existing, ...updates };
|
|
11568
|
+
if (merged.tag === "") merged.tag = null;
|
|
11569
|
+
const addrErr = validateAndNormalizeAddressRow(merged);
|
|
11570
|
+
if (addrErr) return json({ error: addrErr }, { status: 400 });
|
|
11571
|
+
for (const k of Object.keys(updates)) {
|
|
11572
|
+
if (k in merged) updates[k] = merged[k];
|
|
11573
|
+
}
|
|
11574
|
+
await addressRepo().update(id, updates);
|
|
11575
|
+
}
|
|
11409
11576
|
const updated = await addressRepo().findOne({ where: { id } });
|
|
11410
11577
|
return json(serializeAddress2(updated));
|
|
11411
11578
|
}
|