@infuro/cms-core 1.0.22 → 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 +3595 -1844
- package/dist/admin.cjs.map +1 -1
- package/dist/admin.d.cts +42 -15
- package/dist/admin.d.ts +42 -15
- package/dist/admin.js +3626 -1874
- package/dist/admin.js.map +1 -1
- package/dist/api.cjs +325 -34
- package/dist/api.cjs.map +1 -1
- package/dist/api.js +326 -35
- package/dist/api.js.map +1 -1
- package/dist/index.cjs +328 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +329 -36
- package/dist/index.js.map +1 -1
- package/package.json +130 -129
package/dist/index.cjs
CHANGED
|
@@ -4160,11 +4160,11 @@ async function readBufferFromPublicUrl(url) {
|
|
|
4160
4160
|
throw new Error("Unsupported media URL");
|
|
4161
4161
|
}
|
|
4162
4162
|
function sanitizeZipPath(entryName) {
|
|
4163
|
-
const
|
|
4164
|
-
for (const seg of
|
|
4163
|
+
const norm3 = entryName.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
4164
|
+
for (const seg of norm3) {
|
|
4165
4165
|
if (seg === ".." || seg === ".") return null;
|
|
4166
4166
|
}
|
|
4167
|
-
return
|
|
4167
|
+
return norm3;
|
|
4168
4168
|
}
|
|
4169
4169
|
function shouldSkipEntry(parts) {
|
|
4170
4170
|
if (parts[0] === "__MACOSX") return true;
|
|
@@ -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) {
|
|
@@ -8700,6 +8716,66 @@ function getNextAuthOptions(config) {
|
|
|
8700
8716
|
|
|
8701
8717
|
// src/api/crud.ts
|
|
8702
8718
|
var import_typeorm46 = require("typeorm");
|
|
8719
|
+
|
|
8720
|
+
// src/lib/address-geo-validation.ts
|
|
8721
|
+
var import_country_state_city = require("country-state-city");
|
|
8722
|
+
function norm2(s) {
|
|
8723
|
+
return typeof s === "string" ? s.trim() : "";
|
|
8724
|
+
}
|
|
8725
|
+
function resolveCountry(input) {
|
|
8726
|
+
const t = input.trim();
|
|
8727
|
+
if (!t) return void 0;
|
|
8728
|
+
if (t.length === 2) {
|
|
8729
|
+
const byCode = import_country_state_city.Country.getCountryByCode(t.toUpperCase());
|
|
8730
|
+
if (byCode) return byCode;
|
|
8731
|
+
}
|
|
8732
|
+
const lower = t.toLowerCase();
|
|
8733
|
+
return import_country_state_city.Country.getAllCountries().find((c) => c.name.toLowerCase() === lower);
|
|
8734
|
+
}
|
|
8735
|
+
function resolveState(countryIso, input) {
|
|
8736
|
+
const t = input.trim();
|
|
8737
|
+
if (!t || !countryIso) return void 0;
|
|
8738
|
+
const states = import_country_state_city.State.getStatesOfCountry(countryIso);
|
|
8739
|
+
const lower = t.toLowerCase();
|
|
8740
|
+
return states.find((s) => s.isoCode.toLowerCase() === t.toLowerCase() || s.name.toLowerCase() === lower);
|
|
8741
|
+
}
|
|
8742
|
+
function resolveCity(countryIso, stateIso, input) {
|
|
8743
|
+
const t = input.trim();
|
|
8744
|
+
if (!t || !countryIso || !stateIso) return void 0;
|
|
8745
|
+
const lower = t.toLowerCase();
|
|
8746
|
+
const cities = import_country_state_city.City.getCitiesOfState(countryIso, stateIso);
|
|
8747
|
+
return cities.find((c) => c.name.toLowerCase() === lower);
|
|
8748
|
+
}
|
|
8749
|
+
function assertValidAddressHierarchy(country, state, city) {
|
|
8750
|
+
const c = resolveCountry(country);
|
|
8751
|
+
if (!c) return { ok: false, error: "Invalid or unknown country." };
|
|
8752
|
+
const st = resolveState(c.isoCode, state);
|
|
8753
|
+
if (!st) return { ok: false, error: "State or province does not match the selected country." };
|
|
8754
|
+
const ct = resolveCity(c.isoCode, st.isoCode, city);
|
|
8755
|
+
if (!ct) return { ok: false, error: "City does not match the selected state." };
|
|
8756
|
+
return { ok: true, country: c.name, state: st.name, city: ct.name };
|
|
8757
|
+
}
|
|
8758
|
+
function validateAndNormalizeAddressRow(row) {
|
|
8759
|
+
const line1 = norm2(row.line1);
|
|
8760
|
+
const postalCode = norm2(row.postalCode);
|
|
8761
|
+
const countryIn = norm2(row.country);
|
|
8762
|
+
const stateIn = norm2(row.state);
|
|
8763
|
+
const cityIn = norm2(row.city);
|
|
8764
|
+
if (!line1) return "Street address (line 1) is required.";
|
|
8765
|
+
if (!postalCode) return "Postal code is required.";
|
|
8766
|
+
if (!countryIn || !stateIn || !cityIn) return "Country, state, and city are required.";
|
|
8767
|
+
const geo = assertValidAddressHierarchy(countryIn, stateIn, cityIn);
|
|
8768
|
+
if (!geo.ok) return geo.error;
|
|
8769
|
+
row.line1 = line1;
|
|
8770
|
+
row.line2 = norm2(row.line2) || null;
|
|
8771
|
+
row.postalCode = postalCode;
|
|
8772
|
+
row.country = geo.country;
|
|
8773
|
+
row.state = geo.state;
|
|
8774
|
+
row.city = geo.city;
|
|
8775
|
+
return null;
|
|
8776
|
+
}
|
|
8777
|
+
|
|
8778
|
+
// src/api/crud.ts
|
|
8703
8779
|
var CRUD_LOG = "[cms-crud]";
|
|
8704
8780
|
function logCrudClientError(op, detail) {
|
|
8705
8781
|
console.warn(CRUD_LOG, op, detail);
|
|
@@ -8707,6 +8783,40 @@ function logCrudClientError(op, detail) {
|
|
|
8707
8783
|
function logCrudServerError(op, detail) {
|
|
8708
8784
|
console.error(CRUD_LOG, op, detail);
|
|
8709
8785
|
}
|
|
8786
|
+
async function aggregateMediaFolderFileSizes(dataSource, folderIds) {
|
|
8787
|
+
const map = /* @__PURE__ */ new Map();
|
|
8788
|
+
if (folderIds.length === 0) return map;
|
|
8789
|
+
try {
|
|
8790
|
+
const rows = await dataSource.query(
|
|
8791
|
+
`
|
|
8792
|
+
WITH RECURSIVE walk AS (
|
|
8793
|
+
SELECT m."parentId" AS root_id, m.id, m.kind, m.size
|
|
8794
|
+
FROM media m
|
|
8795
|
+
WHERE m."parentId" = ANY($1) AND m.deleted = false
|
|
8796
|
+
UNION ALL
|
|
8797
|
+
SELECT w.root_id, m.id, m.kind, m.size
|
|
8798
|
+
FROM walk w
|
|
8799
|
+
INNER JOIN media m ON m."parentId" = w.id AND m.deleted = false
|
|
8800
|
+
)
|
|
8801
|
+
SELECT root_id AS "rootId", COALESCE(SUM(size) FILTER (WHERE kind = 'file'), 0)::bigint AS "totalSize"
|
|
8802
|
+
FROM walk
|
|
8803
|
+
GROUP BY root_id
|
|
8804
|
+
`,
|
|
8805
|
+
[folderIds]
|
|
8806
|
+
);
|
|
8807
|
+
for (const r of rows) {
|
|
8808
|
+
map.set(Number(r.rootId), Number(r.totalSize));
|
|
8809
|
+
}
|
|
8810
|
+
for (const id of folderIds) {
|
|
8811
|
+
if (!map.has(id)) map.set(id, 0);
|
|
8812
|
+
}
|
|
8813
|
+
} catch (err) {
|
|
8814
|
+
logCrudServerError("media folder size aggregate failed", {
|
|
8815
|
+
message: err instanceof Error ? err.message : String(err)
|
|
8816
|
+
});
|
|
8817
|
+
}
|
|
8818
|
+
return map;
|
|
8819
|
+
}
|
|
8710
8820
|
var DATE_COLUMN_TYPES = /* @__PURE__ */ new Set([
|
|
8711
8821
|
"date",
|
|
8712
8822
|
"datetime",
|
|
@@ -8771,6 +8881,16 @@ function mergeDeletedFalseWhere(repo, where) {
|
|
|
8771
8881
|
}
|
|
8772
8882
|
return Object.keys(where).length > 0 ? { ...where, ...d } : d;
|
|
8773
8883
|
}
|
|
8884
|
+
function normalizeProductSku(value) {
|
|
8885
|
+
if (value == null) return null;
|
|
8886
|
+
const s = String(value).trim();
|
|
8887
|
+
return s === "" ? null : s;
|
|
8888
|
+
}
|
|
8889
|
+
async function assertProductSkuUnique(repo, sku, excludeId) {
|
|
8890
|
+
const where = excludeId != null ? { sku, deleted: false, id: (0, import_typeorm46.Not)(excludeId) } : { sku, deleted: false };
|
|
8891
|
+
const row = await repo.findOne({ where });
|
|
8892
|
+
return row == null;
|
|
8893
|
+
}
|
|
8774
8894
|
function buildSoftDeletePayload(meta, deletedBy) {
|
|
8775
8895
|
const payload = { deleted: true };
|
|
8776
8896
|
if (meta.columns.some((c) => c.propertyName === "deletedAt")) {
|
|
@@ -8925,6 +9045,17 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
8925
9045
|
if (statusFilter) productWhere.status = statusFilter;
|
|
8926
9046
|
if (inventory === "in_stock") productWhere.quantity = (0, import_typeorm46.MoreThan)(0);
|
|
8927
9047
|
if (inventory === "out_of_stock") productWhere.quantity = 0;
|
|
9048
|
+
for (const key of ["brandId", "categoryId", "collectionId"]) {
|
|
9049
|
+
const raw = searchParams.get(key)?.trim();
|
|
9050
|
+
if (raw) {
|
|
9051
|
+
const n = Number(raw);
|
|
9052
|
+
if (Number.isFinite(n)) productWhere[key] = n;
|
|
9053
|
+
}
|
|
9054
|
+
}
|
|
9055
|
+
const featuredRaw = searchParams.get("featured")?.trim();
|
|
9056
|
+
if (featuredRaw === "true" || featuredRaw === "false") {
|
|
9057
|
+
productWhere.featured = featuredRaw === "true";
|
|
9058
|
+
}
|
|
8928
9059
|
if (search && typeof search === "string" && search.trim()) {
|
|
8929
9060
|
productWhere.name = (0, import_typeorm46.ILike)(`%${search.trim()}%`);
|
|
8930
9061
|
}
|
|
@@ -8993,19 +9124,36 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
8993
9124
|
qb.andWhere("m.filename ILIKE :search", { search: `%${search.trim()}%` });
|
|
8994
9125
|
}
|
|
8995
9126
|
if (typeFilter) {
|
|
8996
|
-
|
|
8997
|
-
|
|
8998
|
-
|
|
8999
|
-
|
|
9000
|
-
|
|
9001
|
-
})
|
|
9002
|
-
)
|
|
9127
|
+
if (typeFilter === "folder") {
|
|
9128
|
+
qb.andWhere("m.kind = :folderKind", { folderKind: "folder" });
|
|
9129
|
+
} else if (typeFilter === "file") {
|
|
9130
|
+
qb.andWhere("m.kind = :fileKind", { fileKind: "file" });
|
|
9131
|
+
} else if (typeFilter === "image") {
|
|
9132
|
+
qb.andWhere("m.mimeType LIKE :imageMimeType", { imageMimeType: "image/%" });
|
|
9133
|
+
} else if (typeFilter === "video") {
|
|
9134
|
+
qb.andWhere("m.mimeType LIKE :videoMimeType", { videoMimeType: "video/%" });
|
|
9135
|
+
} else if (typeFilter === "audio") {
|
|
9136
|
+
qb.andWhere("m.mimeType LIKE :audioMimeType", { audioMimeType: "audio/%" });
|
|
9137
|
+
} else if (typeFilter === "Document") {
|
|
9138
|
+
qb.andWhere("m.mimeType LIKE :documentMimeType", { documentMimeType: "application/pdf" });
|
|
9139
|
+
} else if (typeFilter === "application") {
|
|
9140
|
+
qb.andWhere("m.kind = :folderKind", { folderKind: "folder" });
|
|
9141
|
+
}
|
|
9003
9142
|
}
|
|
9004
9143
|
const allowedSort = ["filename", "createdAt", "id"];
|
|
9005
9144
|
const sf = allowedSort.includes(sortFieldRaw) ? sortFieldRaw : "filename";
|
|
9006
9145
|
const so = sortOrder === "DESC" ? "DESC" : "ASC";
|
|
9007
|
-
qb.orderBy(
|
|
9008
|
-
const [
|
|
9146
|
+
qb.orderBy(`m.${sf}`, so).skip(skip).take(limit);
|
|
9147
|
+
const [rows, total2] = await qb.getManyAndCount();
|
|
9148
|
+
const mediaRows = rows;
|
|
9149
|
+
const folderIds = mediaRows.filter((m) => m.kind === "folder").map((m) => m.id);
|
|
9150
|
+
let data2 = rows;
|
|
9151
|
+
if (folderIds.length > 0) {
|
|
9152
|
+
const sizeMap = await aggregateMediaFolderFileSizes(dataSource, folderIds);
|
|
9153
|
+
data2 = mediaRows.map(
|
|
9154
|
+
(m) => m.kind === "folder" ? { ...m, size: sizeMap.get(m.id) ?? 0 } : m
|
|
9155
|
+
);
|
|
9156
|
+
}
|
|
9009
9157
|
return json({ total: total2, page, limit, totalPages: Math.ceil(total2 / limit), data: data2 });
|
|
9010
9158
|
}
|
|
9011
9159
|
const sortField = columnNames.has(sortFieldRaw) ? sortFieldRaw : "createdAt";
|
|
@@ -9013,7 +9161,7 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
9013
9161
|
if (search) {
|
|
9014
9162
|
where = buildSearchWhereClause(repo, search);
|
|
9015
9163
|
}
|
|
9016
|
-
const intFilterKeys = ["productId", "attributeId", "taxId"];
|
|
9164
|
+
const intFilterKeys = ["productId", "attributeId", "taxId", "brandId", "categoryId", "collectionId", "parentId"];
|
|
9017
9165
|
const extraWhere = {};
|
|
9018
9166
|
for (const key of intFilterKeys) {
|
|
9019
9167
|
const v = searchParams.get(key);
|
|
@@ -9022,6 +9170,15 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
9022
9170
|
if (Number.isFinite(n)) extraWhere[key] = n;
|
|
9023
9171
|
}
|
|
9024
9172
|
}
|
|
9173
|
+
for (const col of repo.metadata.columns) {
|
|
9174
|
+
if (String(col.type) !== "boolean") continue;
|
|
9175
|
+
const name = col.propertyName;
|
|
9176
|
+
if (!columnNames.has(name)) continue;
|
|
9177
|
+
const raw = searchParams.get(name)?.trim();
|
|
9178
|
+
if (raw === "true" || raw === "false") {
|
|
9179
|
+
extraWhere[name] = raw === "true";
|
|
9180
|
+
}
|
|
9181
|
+
}
|
|
9025
9182
|
if (Object.keys(extraWhere).length > 0) {
|
|
9026
9183
|
if (Array.isArray(where)) {
|
|
9027
9184
|
where = where.map((w) => ({ ...w, ...extraWhere }));
|
|
@@ -9116,6 +9273,31 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
9116
9273
|
});
|
|
9117
9274
|
return json({ error: "Invalid request payload" }, { status: 400 });
|
|
9118
9275
|
}
|
|
9276
|
+
if (resource === "products") {
|
|
9277
|
+
if ("sku" in persistBody) {
|
|
9278
|
+
const skuNorm = normalizeProductSku(persistBody.sku);
|
|
9279
|
+
if (skuNorm) {
|
|
9280
|
+
const ok = await assertProductSkuUnique(repo, skuNorm);
|
|
9281
|
+
if (!ok) {
|
|
9282
|
+
return json({ error: "SKU already exists. Please use a unique SKU." }, { status: 400 });
|
|
9283
|
+
}
|
|
9284
|
+
persistBody.sku = skuNorm;
|
|
9285
|
+
} else {
|
|
9286
|
+
persistBody.sku = null;
|
|
9287
|
+
}
|
|
9288
|
+
}
|
|
9289
|
+
}
|
|
9290
|
+
if (resource === "addresses") {
|
|
9291
|
+
const cid = Number(persistBody.contactId);
|
|
9292
|
+
if (!Number.isFinite(cid)) {
|
|
9293
|
+
return json({ error: "Valid contactId is required." }, { status: 400 });
|
|
9294
|
+
}
|
|
9295
|
+
if (persistBody.tag === "") persistBody.tag = null;
|
|
9296
|
+
const addrErr = validateAndNormalizeAddressRow(persistBody);
|
|
9297
|
+
if (addrErr) {
|
|
9298
|
+
return json({ error: addrErr }, { status: 400 });
|
|
9299
|
+
}
|
|
9300
|
+
}
|
|
9119
9301
|
sanitizeBodyForEntity(repo, persistBody);
|
|
9120
9302
|
let created;
|
|
9121
9303
|
try {
|
|
@@ -9310,7 +9492,20 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
9310
9492
|
relations: ["order", "order.contact", "contact"]
|
|
9311
9493
|
});
|
|
9312
9494
|
if (!payment) return json({ message: "Not found" }, { status: 404 });
|
|
9313
|
-
|
|
9495
|
+
const p = payment;
|
|
9496
|
+
const order = p.order;
|
|
9497
|
+
const orderContact = order?.contact;
|
|
9498
|
+
const contact = p.contact;
|
|
9499
|
+
const customer = orderContact ?? contact;
|
|
9500
|
+
return json({
|
|
9501
|
+
...p,
|
|
9502
|
+
order: order ? {
|
|
9503
|
+
id: order.id,
|
|
9504
|
+
orderNumber: order.orderNumber,
|
|
9505
|
+
contact: orderContact ? { name: orderContact.name, email: orderContact.email } : null
|
|
9506
|
+
} : null,
|
|
9507
|
+
contact: customer ? { id: customer.id, name: customer.name, email: customer.email } : null
|
|
9508
|
+
});
|
|
9314
9509
|
}
|
|
9315
9510
|
if (resource === "blogs") {
|
|
9316
9511
|
const blog = await repo.findOne({
|
|
@@ -9422,8 +9617,91 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
9422
9617
|
const updatePayload = rawBody && typeof rawBody === "object" ? pickColumnUpdates(repo, rawBody) : {};
|
|
9423
9618
|
if (resource === "media") {
|
|
9424
9619
|
const u = updatePayload;
|
|
9425
|
-
delete u.parentId;
|
|
9426
9620
|
delete u.kind;
|
|
9621
|
+
if (rawBody && typeof rawBody === "object" && "parentId" in rawBody) {
|
|
9622
|
+
let pid = null;
|
|
9623
|
+
const p = rawBody.parentId;
|
|
9624
|
+
if (p != null && p !== "") {
|
|
9625
|
+
const n = Number(p);
|
|
9626
|
+
if (!Number.isFinite(n)) {
|
|
9627
|
+
return json({ error: "Invalid parentId" }, { status: 400 });
|
|
9628
|
+
}
|
|
9629
|
+
pid = n;
|
|
9630
|
+
}
|
|
9631
|
+
if (pid != null) {
|
|
9632
|
+
const parent = await repo.findOne({
|
|
9633
|
+
where: { id: pid, deleted: false }
|
|
9634
|
+
});
|
|
9635
|
+
if (!parent || parent.kind !== "folder") {
|
|
9636
|
+
return json({ error: "parent must be a folder" }, { status: 400 });
|
|
9637
|
+
}
|
|
9638
|
+
}
|
|
9639
|
+
const row = await repo.findOne({
|
|
9640
|
+
where: { id: numericId, deleted: false }
|
|
9641
|
+
});
|
|
9642
|
+
if (!row) return json({ message: "Not found" }, { status: 404 });
|
|
9643
|
+
if (pid === numericId) {
|
|
9644
|
+
return json({ error: "Invalid parentId" }, { status: 400 });
|
|
9645
|
+
}
|
|
9646
|
+
if (row.kind === "folder" && pid != null) {
|
|
9647
|
+
let walk = pid;
|
|
9648
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9649
|
+
while (walk != null) {
|
|
9650
|
+
if (walk === numericId) {
|
|
9651
|
+
return json(
|
|
9652
|
+
{ error: "Cannot move a folder into itself or a descendant folder" },
|
|
9653
|
+
{ status: 400 }
|
|
9654
|
+
);
|
|
9655
|
+
}
|
|
9656
|
+
if (seen.has(walk)) break;
|
|
9657
|
+
seen.add(walk);
|
|
9658
|
+
const anc = await repo.findOne({
|
|
9659
|
+
where: { id: walk, deleted: false }
|
|
9660
|
+
});
|
|
9661
|
+
walk = anc ? anc.parentId ?? null : null;
|
|
9662
|
+
}
|
|
9663
|
+
}
|
|
9664
|
+
u.parentId = pid;
|
|
9665
|
+
} else {
|
|
9666
|
+
delete u.parentId;
|
|
9667
|
+
}
|
|
9668
|
+
}
|
|
9669
|
+
if (resource === "products") {
|
|
9670
|
+
const currentRow = await repo.findOne({
|
|
9671
|
+
where: { id: numericId, deleted: false }
|
|
9672
|
+
});
|
|
9673
|
+
if (!currentRow) return json({ message: "Not found" }, { status: 404 });
|
|
9674
|
+
const merged = { ...currentRow, ...updatePayload };
|
|
9675
|
+
const effSku = normalizeProductSku(merged.sku);
|
|
9676
|
+
if (effSku) {
|
|
9677
|
+
const ok = await assertProductSkuUnique(repo, effSku, numericId);
|
|
9678
|
+
if (!ok) {
|
|
9679
|
+
return json({ error: "SKU already exists. Please use a unique SKU." }, { status: 400 });
|
|
9680
|
+
}
|
|
9681
|
+
}
|
|
9682
|
+
if ("sku" in updatePayload) {
|
|
9683
|
+
updatePayload.sku = effSku;
|
|
9684
|
+
}
|
|
9685
|
+
}
|
|
9686
|
+
if (resource === "addresses" && Object.keys(updatePayload).length > 0) {
|
|
9687
|
+
const currentRow = await repo.findOne({
|
|
9688
|
+
where: { id: numericId }
|
|
9689
|
+
});
|
|
9690
|
+
if (!currentRow) return json({ message: "Not found" }, { status: 404 });
|
|
9691
|
+
const merged = {
|
|
9692
|
+
...currentRow,
|
|
9693
|
+
...updatePayload
|
|
9694
|
+
};
|
|
9695
|
+
if (merged.tag === "") merged.tag = null;
|
|
9696
|
+
const addrErr = validateAndNormalizeAddressRow(merged);
|
|
9697
|
+
if (addrErr) {
|
|
9698
|
+
return json({ error: addrErr }, { status: 400 });
|
|
9699
|
+
}
|
|
9700
|
+
for (const k of Object.keys(updatePayload)) {
|
|
9701
|
+
if (k in merged) {
|
|
9702
|
+
updatePayload[k] = merged[k];
|
|
9703
|
+
}
|
|
9704
|
+
}
|
|
9427
9705
|
}
|
|
9428
9706
|
if (Object.keys(updatePayload).length > 0) {
|
|
9429
9707
|
sanitizeBodyForEntity(repo, updatePayload);
|
|
@@ -9454,6 +9732,11 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
9454
9732
|
where: { id: numericId, deleted: false }
|
|
9455
9733
|
});
|
|
9456
9734
|
if (!existing) return json({ message: "Not found" }, { status: 404 });
|
|
9735
|
+
if (resource === "contacts") {
|
|
9736
|
+
const result2 = await repo.delete(numericId);
|
|
9737
|
+
if (result2.affected === 0) return json({ message: "Not found" }, { status: 404 });
|
|
9738
|
+
return json({ message: "Deleted successfully" }, { status: 200 });
|
|
9739
|
+
}
|
|
9457
9740
|
let deletedBy = null;
|
|
9458
9741
|
if (getDeletedByUserId) {
|
|
9459
9742
|
try {
|
|
@@ -11421,18 +11704,19 @@ function createStorefrontApiHandler(config) {
|
|
|
11421
11704
|
const contactOrErr = await getContactForAddresses();
|
|
11422
11705
|
if (contactOrErr instanceof Response) return contactOrErr;
|
|
11423
11706
|
const b = await req.json().catch(() => ({}));
|
|
11424
|
-
const
|
|
11425
|
-
|
|
11426
|
-
|
|
11427
|
-
|
|
11428
|
-
|
|
11429
|
-
|
|
11430
|
-
|
|
11431
|
-
|
|
11432
|
-
|
|
11433
|
-
|
|
11434
|
-
|
|
11435
|
-
);
|
|
11707
|
+
const row = {
|
|
11708
|
+
contactId: contactOrErr.contactId,
|
|
11709
|
+
tag: typeof b.tag === "string" ? b.tag.trim() || null : null,
|
|
11710
|
+
line1: typeof b.line1 === "string" ? b.line1 : "",
|
|
11711
|
+
line2: typeof b.line2 === "string" ? b.line2.trim() || null : null,
|
|
11712
|
+
city: typeof b.city === "string" ? b.city : "",
|
|
11713
|
+
state: typeof b.state === "string" ? b.state : "",
|
|
11714
|
+
postalCode: typeof b.postalCode === "string" ? b.postalCode : "",
|
|
11715
|
+
country: typeof b.country === "string" ? b.country : ""
|
|
11716
|
+
};
|
|
11717
|
+
const addrErr = validateAndNormalizeAddressRow(row);
|
|
11718
|
+
if (addrErr) return json({ error: addrErr }, { status: 400 });
|
|
11719
|
+
const created = await addressRepo().save(addressRepo().create(row));
|
|
11436
11720
|
return json(serializeAddress2(created));
|
|
11437
11721
|
}
|
|
11438
11722
|
if (path[0] === "addresses" && path.length === 2 && (method === "PATCH" || method === "PUT")) {
|
|
@@ -11451,7 +11735,16 @@ function createStorefrontApiHandler(config) {
|
|
|
11451
11735
|
if (b.state !== void 0) updates.state = typeof b.state === "string" ? b.state.trim() || null : null;
|
|
11452
11736
|
if (b.postalCode !== void 0) updates.postalCode = typeof b.postalCode === "string" ? b.postalCode.trim() || null : null;
|
|
11453
11737
|
if (b.country !== void 0) updates.country = typeof b.country === "string" ? b.country.trim() || null : null;
|
|
11454
|
-
if (Object.keys(updates).length)
|
|
11738
|
+
if (Object.keys(updates).length) {
|
|
11739
|
+
const merged = { ...existing, ...updates };
|
|
11740
|
+
if (merged.tag === "") merged.tag = null;
|
|
11741
|
+
const addrErr = validateAndNormalizeAddressRow(merged);
|
|
11742
|
+
if (addrErr) return json({ error: addrErr }, { status: 400 });
|
|
11743
|
+
for (const k of Object.keys(updates)) {
|
|
11744
|
+
if (k in merged) updates[k] = merged[k];
|
|
11745
|
+
}
|
|
11746
|
+
await addressRepo().update(id, updates);
|
|
11747
|
+
}
|
|
11455
11748
|
const updated = await addressRepo().findOne({ where: { id } });
|
|
11456
11749
|
return json(serializeAddress2(updated));
|
|
11457
11750
|
}
|
|
@@ -12263,7 +12556,7 @@ var DEFAULT_ADMIN_NAV = [
|
|
|
12263
12556
|
];
|
|
12264
12557
|
|
|
12265
12558
|
// src/index.ts
|
|
12266
|
-
console.log("\u{1F525} USING LOCAL CMS
|
|
12559
|
+
console.log("\u{1F525} USING LOCAL CMS (index.ts loaded) \u{1F525}");
|
|
12267
12560
|
// Annotate the CommonJS export names for ESM import in node:
|
|
12268
12561
|
0 && (module.exports = {
|
|
12269
12562
|
ADMIN_GROUP_NAME,
|