@infuro/cms-core 1.0.24 → 1.0.26
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 +5725 -4475
- package/dist/admin.cjs.map +1 -1
- package/dist/admin.d.cts +23 -4
- package/dist/admin.d.ts +23 -4
- package/dist/admin.js +5672 -4420
- package/dist/admin.js.map +1 -1
- package/dist/api.cjs +422 -52
- package/dist/api.cjs.map +1 -1
- package/dist/api.d.cts +1 -1
- package/dist/api.d.ts +1 -1
- package/dist/api.js +430 -60
- package/dist/api.js.map +1 -1
- package/dist/auth.cjs +9 -1
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.js +9 -1
- package/dist/auth.js.map +1 -1
- package/dist/cli.cjs +7 -0
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +7 -0
- package/dist/cli.js.map +1 -1
- package/dist/{index-BGAh4fPQ.d.cts → index--vbixpxE.d.cts} +17 -2
- package/dist/{index-Cnwh7B3r.d.ts → index-DMJgi-fy.d.ts} +17 -2
- package/dist/index.cjs +508 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +509 -68
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17,10 +17,52 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
17
17
|
return result;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
// src/plugins/erp/erp-log.ts
|
|
21
|
+
function logErp(event, detail) {
|
|
22
|
+
if (detail && Object.keys(detail).length) console.info(ERP_LOG, event, detail);
|
|
23
|
+
else console.info(ERP_LOG, event);
|
|
24
|
+
}
|
|
25
|
+
function warnErp(event, detail) {
|
|
26
|
+
console.warn(ERP_LOG, event, detail);
|
|
27
|
+
}
|
|
28
|
+
function errorErp(event, detail) {
|
|
29
|
+
console.error(ERP_LOG, event, detail);
|
|
30
|
+
}
|
|
31
|
+
function erpSafeWebhookUrl(url) {
|
|
32
|
+
try {
|
|
33
|
+
const u = new URL(url);
|
|
34
|
+
return `${u.origin}${u.pathname}`;
|
|
35
|
+
} catch {
|
|
36
|
+
return "(invalid webhook URL)";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
var ERP_LOG;
|
|
40
|
+
var init_erp_log = __esm({
|
|
41
|
+
"src/plugins/erp/erp-log.ts"() {
|
|
42
|
+
"use strict";
|
|
43
|
+
ERP_LOG = "[webcore:erp]";
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
20
47
|
// src/plugins/erp/erp-queue.ts
|
|
48
|
+
function queuePayloadSummary(payload) {
|
|
49
|
+
if (payload.kind === "order") {
|
|
50
|
+
const o = payload.order;
|
|
51
|
+
return {
|
|
52
|
+
kind: payload.kind,
|
|
53
|
+
platformOrderId: o.platformOrderId ?? o.platformOrderNumber,
|
|
54
|
+
itemCount: Array.isArray(o.items) ? o.items.length : 0
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return { kind: payload.kind };
|
|
58
|
+
}
|
|
21
59
|
async function queueErp(cms, payload) {
|
|
22
60
|
const queue = cms.getPlugin("queue");
|
|
23
|
-
if (!queue)
|
|
61
|
+
if (!queue) {
|
|
62
|
+
warnErp("queue:add_skipped", { reason: "queue_plugin_missing", ...queuePayloadSummary(payload) });
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
logErp("queue:add", { job: ERP_QUEUE_NAME, ...queuePayloadSummary(payload) });
|
|
24
66
|
await queue.add(ERP_QUEUE_NAME, payload);
|
|
25
67
|
}
|
|
26
68
|
function registerErpQueueProcessor(cms) {
|
|
@@ -28,18 +70,31 @@ function registerErpQueueProcessor(cms) {
|
|
|
28
70
|
if (!queue) return;
|
|
29
71
|
queue.registerProcessor(ERP_QUEUE_NAME, async (data) => {
|
|
30
72
|
const erp = cms.getPlugin("erp");
|
|
31
|
-
if (!erp)
|
|
73
|
+
if (!erp) {
|
|
74
|
+
warnErp("queue:processor_skip", { reason: "erp_plugin_missing" });
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
32
77
|
const payload = data;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
78
|
+
logErp("queue:job_start", queuePayloadSummary(payload));
|
|
79
|
+
try {
|
|
80
|
+
if (payload.kind === "lead") {
|
|
81
|
+
await erp.submission.submitContact(payload.contact);
|
|
82
|
+
} else if (payload.kind === "formOpportunity") {
|
|
83
|
+
await erp.submission.submitFormOpportunity(payload.contact);
|
|
84
|
+
} else if (payload.kind === "createContact") {
|
|
85
|
+
await erp.submission.submitCreateContact(payload.contact);
|
|
86
|
+
} else if (payload.kind === "order") {
|
|
87
|
+
await erp.submission.submitOrder(payload.order);
|
|
88
|
+
} else if (payload.kind === "productUpsert") {
|
|
89
|
+
await erp.submission.submitProductUpsert(payload.product);
|
|
90
|
+
}
|
|
91
|
+
logErp("queue:job_done", queuePayloadSummary(payload));
|
|
92
|
+
} catch (e) {
|
|
93
|
+
errorErp("queue:job_failed", {
|
|
94
|
+
...queuePayloadSummary(payload),
|
|
95
|
+
message: e instanceof Error ? e.message : String(e)
|
|
96
|
+
});
|
|
97
|
+
throw e;
|
|
43
98
|
}
|
|
44
99
|
});
|
|
45
100
|
}
|
|
@@ -47,6 +102,7 @@ var ERP_QUEUE_NAME;
|
|
|
47
102
|
var init_erp_queue = __esm({
|
|
48
103
|
"src/plugins/erp/erp-queue.ts"() {
|
|
49
104
|
"use strict";
|
|
105
|
+
init_erp_log();
|
|
50
106
|
ERP_QUEUE_NAME = "erp";
|
|
51
107
|
}
|
|
52
108
|
});
|
|
@@ -98,21 +154,36 @@ async function queueErpPaidOrderForOrderId(cms, dataSource, entityMap, orderId)
|
|
|
98
154
|
const cfgRows = await configRepo.find({ where: { settings: "erp", deleted: false } });
|
|
99
155
|
for (const row of cfgRows) {
|
|
100
156
|
const r = row;
|
|
101
|
-
if (r.key === "enabled" && r.value === "false")
|
|
157
|
+
if (r.key === "enabled" && r.value === "false") {
|
|
158
|
+
logErp("paid-order:skip", { orderId, reason: "erp_config_disabled" });
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (!cms.getPlugin("erp")) {
|
|
163
|
+
logErp("paid-order:skip", { orderId, reason: "erp_plugin_missing" });
|
|
164
|
+
return;
|
|
102
165
|
}
|
|
103
|
-
if (!cms.getPlugin("erp")) return;
|
|
104
166
|
const orderRepo = dataSource.getRepository(entityMap.orders);
|
|
105
167
|
const ord = await orderRepo.findOne({
|
|
106
168
|
where: { id: orderId },
|
|
107
169
|
relations: ["items", "items.product", "contact", "billingAddress", "shippingAddress", "payments"]
|
|
108
170
|
});
|
|
109
|
-
if (!ord)
|
|
171
|
+
if (!ord) {
|
|
172
|
+
logErp("paid-order:skip", { orderId, reason: "order_not_found" });
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
110
175
|
const o = ord;
|
|
111
176
|
const okKind = o.orderKind === void 0 || o.orderKind === null || o.orderKind === "sale";
|
|
112
|
-
if (!okKind)
|
|
177
|
+
if (!okKind) {
|
|
178
|
+
logErp("paid-order:skip", { orderId, reason: "order_kind_not_sale", orderKind: o.orderKind });
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
113
181
|
const rawPayments = o.payments ?? [];
|
|
114
182
|
const completedPayments = rawPayments.filter((pay) => pay.status === "completed" && pay.deleted !== true);
|
|
115
|
-
if (!completedPayments.length)
|
|
183
|
+
if (!completedPayments.length) {
|
|
184
|
+
logErp("paid-order:skip", { orderId, reason: "no_completed_payments" });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
116
187
|
const rawItems = o.items ?? [];
|
|
117
188
|
const lines = rawItems.filter((it) => it.product).map((it) => {
|
|
118
189
|
const p = it.product;
|
|
@@ -131,7 +202,10 @@ async function queueErpPaidOrderForOrderId(cms, dataSource, entityMap, orderId)
|
|
|
131
202
|
type: itemType
|
|
132
203
|
};
|
|
133
204
|
});
|
|
134
|
-
if (!lines.length)
|
|
205
|
+
if (!lines.length) {
|
|
206
|
+
logErp("paid-order:skip", { orderId, reason: "no_line_items_with_product" });
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
135
209
|
const contact = o.contact;
|
|
136
210
|
const orderTotalMajor = Number(o.total);
|
|
137
211
|
const paymentDtos = completedPayments.length === 1 && Number.isFinite(orderTotalMajor) ? [paymentRowToWebhookDto(completedPayments[0], orderTotalMajor)] : completedPayments.map((pay) => paymentRowToWebhookDto(pay));
|
|
@@ -153,13 +227,28 @@ async function queueErpPaidOrderForOrderId(cms, dataSource, entityMap, orderId)
|
|
|
153
227
|
payments: paymentDtos,
|
|
154
228
|
metadata: { ...baseMeta, source: "storefront" }
|
|
155
229
|
};
|
|
230
|
+
logErp("paid-order:payload_built", {
|
|
231
|
+
orderId,
|
|
232
|
+
platformOrderId: orderDto.platformOrderId,
|
|
233
|
+
status: orderDto.status,
|
|
234
|
+
itemCount: lines.length,
|
|
235
|
+
skus: lines.map((l) => l.sku),
|
|
236
|
+
paymentCount: paymentDtos.length,
|
|
237
|
+
paymentIds: paymentDtos.map((p) => p.id),
|
|
238
|
+
total: orderTotalMajor
|
|
239
|
+
});
|
|
156
240
|
await queueErp(cms, { kind: "order", order: orderDto });
|
|
157
|
-
} catch {
|
|
241
|
+
} catch (e) {
|
|
242
|
+
errorErp("paid-order:enqueue_failed", {
|
|
243
|
+
orderId,
|
|
244
|
+
message: e instanceof Error ? e.message : String(e)
|
|
245
|
+
});
|
|
158
246
|
}
|
|
159
247
|
}
|
|
160
248
|
var init_paid_order_erp = __esm({
|
|
161
249
|
"src/plugins/erp/paid-order-erp.ts"() {
|
|
162
250
|
"use strict";
|
|
251
|
+
init_erp_log();
|
|
163
252
|
init_erp_queue();
|
|
164
253
|
}
|
|
165
254
|
});
|
|
@@ -635,6 +724,7 @@ async function createCmsApp(options) {
|
|
|
635
724
|
}
|
|
636
725
|
|
|
637
726
|
// src/plugins/erp/erp-submission.ts
|
|
727
|
+
init_erp_log();
|
|
638
728
|
var ERPSubmissionService = class {
|
|
639
729
|
webhookUrl;
|
|
640
730
|
webhookJwt;
|
|
@@ -714,7 +804,29 @@ var ERPSubmissionService = class {
|
|
|
714
804
|
};
|
|
715
805
|
return this.postWebhookJson(envelope);
|
|
716
806
|
}
|
|
807
|
+
summarizeWebhookBody(body) {
|
|
808
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
809
|
+
return { bodyKind: typeof body };
|
|
810
|
+
}
|
|
811
|
+
const o = body;
|
|
812
|
+
const out = { event_type: o.event_type, timestamp: o.timestamp };
|
|
813
|
+
const data = o.data;
|
|
814
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
815
|
+
const d = data;
|
|
816
|
+
out.dataKeys = Object.keys(d);
|
|
817
|
+
out.platformOrderId = d.platformOrderId ?? d.platformOrderNumber;
|
|
818
|
+
out.itemCount = Array.isArray(d.items) ? d.items.length : void 0;
|
|
819
|
+
}
|
|
820
|
+
return out;
|
|
821
|
+
}
|
|
717
822
|
async postWebhookJson(body) {
|
|
823
|
+
const safeUrl = erpSafeWebhookUrl(this.webhookUrl);
|
|
824
|
+
const bodyJson = JSON.stringify(body);
|
|
825
|
+
logErp("webhook:post_start", {
|
|
826
|
+
url: safeUrl,
|
|
827
|
+
bodyBytes: bodyJson.length,
|
|
828
|
+
...this.summarizeWebhookBody(body)
|
|
829
|
+
});
|
|
718
830
|
try {
|
|
719
831
|
const res = await fetch(this.webhookUrl, {
|
|
720
832
|
method: "POST",
|
|
@@ -722,13 +834,19 @@ var ERPSubmissionService = class {
|
|
|
722
834
|
"Content-Type": "application/json",
|
|
723
835
|
"X-External-Token": this.webhookJwt
|
|
724
836
|
},
|
|
725
|
-
body:
|
|
837
|
+
body: bodyJson
|
|
726
838
|
});
|
|
727
|
-
if (res.ok) return { success: true, status: res.status };
|
|
728
839
|
const text = await res.text();
|
|
840
|
+
const preview = text.length > 500 ? `${text.slice(0, 500)}\u2026` : text;
|
|
841
|
+
if (res.ok) {
|
|
842
|
+
logErp("webhook:post_ok", { status: res.status, responsePreview: preview || "(empty body)" });
|
|
843
|
+
return { success: true, status: res.status };
|
|
844
|
+
}
|
|
845
|
+
warnErp("webhook:post_http_error", { status: res.status, responsePreview: preview });
|
|
729
846
|
return { success: false, error: `${res.status} ${text.slice(0, 500)}`, status: res.status };
|
|
730
847
|
} catch (e) {
|
|
731
848
|
const message = e instanceof Error ? e.message : "ERP webhook request failed";
|
|
849
|
+
errorErp("webhook:post_fetch_failed", { url: safeUrl, message });
|
|
732
850
|
return { success: false, error: message };
|
|
733
851
|
}
|
|
734
852
|
}
|
|
@@ -842,7 +960,20 @@ var ERPSubmissionService = class {
|
|
|
842
960
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
843
961
|
data: orderDto
|
|
844
962
|
};
|
|
845
|
-
|
|
963
|
+
logErp("submitOrder:envelope_ready", {
|
|
964
|
+
event_type: envelope.event_type,
|
|
965
|
+
timestamp: envelope.timestamp,
|
|
966
|
+
platformOrderId: orderDto.platformOrderId ?? orderDto.platformOrderNumber,
|
|
967
|
+
itemCount: Array.isArray(orderDto.items) ? orderDto.items.length : 0,
|
|
968
|
+
paymentCount: Array.isArray(orderDto.payments) ? orderDto.payments.length : 0
|
|
969
|
+
});
|
|
970
|
+
const result = await this.postWebhookJson(envelope);
|
|
971
|
+
if (result.success) {
|
|
972
|
+
logErp("submitOrder:complete", { ok: true, status: result.status });
|
|
973
|
+
} else {
|
|
974
|
+
warnErp("submitOrder:complete", { ok: false, status: result.status, error: result.error });
|
|
975
|
+
}
|
|
976
|
+
return result;
|
|
846
977
|
}
|
|
847
978
|
extractContactData(formData, formFields) {
|
|
848
979
|
const contactData = {
|
|
@@ -5003,18 +5134,37 @@ function createUsersApiHandlers(config) {
|
|
|
5003
5134
|
try {
|
|
5004
5135
|
const body = await req.json();
|
|
5005
5136
|
if (!body?.name || !body?.email) return json({ error: "Name and email are required" }, { status: 400 });
|
|
5006
|
-
const
|
|
5007
|
-
|
|
5137
|
+
const email = body.email;
|
|
5138
|
+
const existing = await userRepo().findOne({ where: { email } });
|
|
5139
|
+
if (existing && !existing.deleted) {
|
|
5140
|
+
return json({ error: "User with this email already exists" }, { status: 400 });
|
|
5141
|
+
}
|
|
5008
5142
|
const groupRepo = dataSource.getRepository(entityMap.user_groups);
|
|
5009
5143
|
const customerG = await groupRepo.findOne({ where: { name: "Customer", deleted: false } });
|
|
5010
5144
|
const gid = body.groupId ?? null;
|
|
5011
5145
|
const isCustomer = !!(customerG && gid === customerG.id);
|
|
5012
5146
|
const adminAccess = isCustomer ? false : body.adminAccess === false ? false : true;
|
|
5013
5147
|
const blocked = body.blocked === true || body.blocked === "true" || body.blocked === 1 || body.blocked === "1";
|
|
5014
|
-
const newUser = await
|
|
5148
|
+
const newUser = existing?.deleted ? await (async () => {
|
|
5149
|
+
await userRepo().update(existing.id, {
|
|
5150
|
+
deleted: false,
|
|
5151
|
+
deletedAt: null,
|
|
5152
|
+
deletedBy: null,
|
|
5153
|
+
name: body.name,
|
|
5154
|
+
email,
|
|
5155
|
+
password: null,
|
|
5156
|
+
blocked,
|
|
5157
|
+
groupId: gid,
|
|
5158
|
+
adminAccess,
|
|
5159
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
5160
|
+
});
|
|
5161
|
+
const row = await userRepo().findOne({ where: { id: existing.id } });
|
|
5162
|
+
if (!row) throw new Error("user missing after restore");
|
|
5163
|
+
return row;
|
|
5164
|
+
})() : await userRepo().save(
|
|
5015
5165
|
userRepo().create({
|
|
5016
5166
|
name: body.name,
|
|
5017
|
-
email
|
|
5167
|
+
email,
|
|
5018
5168
|
password: null,
|
|
5019
5169
|
blocked,
|
|
5020
5170
|
groupId: gid,
|
|
@@ -5167,21 +5317,110 @@ function createUserAvatarHandler(config) {
|
|
|
5167
5317
|
}
|
|
5168
5318
|
};
|
|
5169
5319
|
}
|
|
5320
|
+
var PROFILE_EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
5170
5321
|
function createUserProfileHandler(config) {
|
|
5171
|
-
const { dataSource, entityMap, json, getSession } = config;
|
|
5172
|
-
|
|
5322
|
+
const { dataSource, entityMap, json, getSession, onProfileUpdated } = config;
|
|
5323
|
+
async function loadCurrentUser() {
|
|
5173
5324
|
const session = await getSession();
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5325
|
+
const su = session?.user;
|
|
5326
|
+
if (!su?.email && su?.id == null) {
|
|
5327
|
+
return { ok: false, response: json({ error: "Unauthorized" }, { status: 401 }) };
|
|
5328
|
+
}
|
|
5329
|
+
const userRepo = dataSource.getRepository(entityMap.users);
|
|
5330
|
+
let user = null;
|
|
5331
|
+
const uidRaw = su.id != null ? String(su.id).trim() : "";
|
|
5332
|
+
const uid = uidRaw && /^\d+$/.test(uidRaw) ? parseInt(uidRaw, 10) : NaN;
|
|
5333
|
+
if (Number.isFinite(uid) && uid > 0) {
|
|
5334
|
+
user = await userRepo.findOne({
|
|
5335
|
+
where: { id: uid, deleted: false },
|
|
5336
|
+
select: ["id", "name", "email", "phone", "createdAt"]
|
|
5337
|
+
});
|
|
5338
|
+
}
|
|
5339
|
+
if (!user && su.email) {
|
|
5340
|
+
const em = String(su.email).trim().toLowerCase();
|
|
5341
|
+
if (em) {
|
|
5342
|
+
user = await userRepo.findOne({
|
|
5343
|
+
where: { email: em, deleted: false },
|
|
5344
|
+
select: ["id", "name", "email", "phone", "createdAt"]
|
|
5345
|
+
});
|
|
5346
|
+
}
|
|
5347
|
+
}
|
|
5348
|
+
if (!user) return { ok: false, response: json({ error: "Not found" }, { status: 404 }) };
|
|
5349
|
+
return { ok: true, user };
|
|
5350
|
+
}
|
|
5351
|
+
return {
|
|
5352
|
+
async GET(_req) {
|
|
5353
|
+
try {
|
|
5354
|
+
const r = await loadCurrentUser();
|
|
5355
|
+
if (!r.ok) return r.response;
|
|
5356
|
+
const u = r.user;
|
|
5357
|
+
return json({
|
|
5358
|
+
id: u.id,
|
|
5359
|
+
name: u.name ?? "",
|
|
5360
|
+
email: u.email ?? "",
|
|
5361
|
+
phone: u.phone ?? null,
|
|
5362
|
+
createdAt: u.createdAt instanceof Date ? u.createdAt.toISOString() : u.createdAt ?? void 0
|
|
5363
|
+
});
|
|
5364
|
+
} catch {
|
|
5365
|
+
return json({ error: "Internal server error" }, { status: 500 });
|
|
5366
|
+
}
|
|
5367
|
+
},
|
|
5368
|
+
async PUT(req) {
|
|
5369
|
+
try {
|
|
5370
|
+
const r = await loadCurrentUser();
|
|
5371
|
+
if (!r.ok) return r.response;
|
|
5372
|
+
const current = r.user;
|
|
5373
|
+
let body;
|
|
5374
|
+
try {
|
|
5375
|
+
body = await req.json();
|
|
5376
|
+
} catch {
|
|
5377
|
+
return json({ error: "Invalid JSON" }, { status: 400 });
|
|
5378
|
+
}
|
|
5379
|
+
const name = typeof body.name === "string" ? body.name.trim() : "";
|
|
5380
|
+
if (!name) return json({ error: "Name is required" }, { status: 400 });
|
|
5381
|
+
const emailRaw = typeof body.email === "string" ? body.email.trim().toLowerCase() : "";
|
|
5382
|
+
if (!emailRaw || !PROFILE_EMAIL_RE.test(emailRaw)) {
|
|
5383
|
+
return json({ error: "Valid email is required" }, { status: 400 });
|
|
5384
|
+
}
|
|
5385
|
+
const phone = body.phone === null || body.phone === void 0 ? null : typeof body.phone === "string" ? body.phone.trim() || null : null;
|
|
5386
|
+
const userRepo = dataSource.getRepository(entityMap.users);
|
|
5387
|
+
if (emailRaw !== String(current.email ?? "").toLowerCase()) {
|
|
5388
|
+
const taken = await userRepo.findOne({
|
|
5389
|
+
where: { email: emailRaw, deleted: false },
|
|
5390
|
+
select: ["id"]
|
|
5391
|
+
});
|
|
5392
|
+
if (taken && taken.id !== current.id) {
|
|
5393
|
+
return json({ error: "Email is already in use" }, { status: 409 });
|
|
5394
|
+
}
|
|
5395
|
+
}
|
|
5396
|
+
await userRepo.update(
|
|
5397
|
+
{ id: current.id },
|
|
5398
|
+
{
|
|
5399
|
+
name,
|
|
5400
|
+
email: emailRaw,
|
|
5401
|
+
phone,
|
|
5402
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
5403
|
+
}
|
|
5404
|
+
);
|
|
5405
|
+
const updated = await userRepo.findOne({
|
|
5406
|
+
where: { id: current.id },
|
|
5407
|
+
select: ["id", "name", "email", "phone"]
|
|
5408
|
+
});
|
|
5409
|
+
if (!updated) return json({ error: "Not found" }, { status: 404 });
|
|
5410
|
+
const row = updated;
|
|
5411
|
+
if (onProfileUpdated) {
|
|
5412
|
+
try {
|
|
5413
|
+
await onProfileUpdated(req, row);
|
|
5414
|
+
} catch {
|
|
5415
|
+
}
|
|
5416
|
+
}
|
|
5417
|
+
return json({
|
|
5418
|
+
message: "Profile updated successfully",
|
|
5419
|
+
user: { id: row.id, name: row.name, email: row.email, phone: row.phone }
|
|
5420
|
+
});
|
|
5421
|
+
} catch {
|
|
5422
|
+
return json({ error: "Internal server error" }, { status: 500 });
|
|
5423
|
+
}
|
|
5185
5424
|
}
|
|
5186
5425
|
};
|
|
5187
5426
|
}
|
|
@@ -8515,7 +8754,7 @@ function getNextAuthOptions(config) {
|
|
|
8515
8754
|
}
|
|
8516
8755
|
},
|
|
8517
8756
|
callbacks: {
|
|
8518
|
-
async jwt({ token, user }) {
|
|
8757
|
+
async jwt({ token, user, trigger, session }) {
|
|
8519
8758
|
if (user) {
|
|
8520
8759
|
const u = user;
|
|
8521
8760
|
token.id = u.id;
|
|
@@ -8524,11 +8763,19 @@ function getNextAuthOptions(config) {
|
|
|
8524
8763
|
token.entityPerms = u.entityPerms;
|
|
8525
8764
|
token.adminAccess = u.adminAccess;
|
|
8526
8765
|
}
|
|
8766
|
+
if (trigger === "update" && session && typeof session === "object") {
|
|
8767
|
+
const s = session;
|
|
8768
|
+
const t = token;
|
|
8769
|
+
if (typeof s.name === "string") t.name = s.name;
|
|
8770
|
+
if (typeof s.email === "string") t.email = s.email;
|
|
8771
|
+
}
|
|
8527
8772
|
return token;
|
|
8528
8773
|
},
|
|
8529
8774
|
async session({ session, token }) {
|
|
8530
8775
|
if (session.user) {
|
|
8531
8776
|
const t = token;
|
|
8777
|
+
if (typeof t.name === "string") session.user.name = t.name;
|
|
8778
|
+
if (typeof t.email === "string") session.user.email = t.email;
|
|
8532
8779
|
session.user.id = t.id;
|
|
8533
8780
|
session.user.groupId = t.groupId;
|
|
8534
8781
|
session.user.isRBACAdmin = t.isRBACAdmin;
|
|
@@ -8543,7 +8790,7 @@ function getNextAuthOptions(config) {
|
|
|
8543
8790
|
}
|
|
8544
8791
|
|
|
8545
8792
|
// src/api/crud.ts
|
|
8546
|
-
import { ILike as ILike2, MoreThan as MoreThan2, Not } from "typeorm";
|
|
8793
|
+
import { Between, ILike as ILike2, LessThanOrEqual, MoreThan as MoreThan2, MoreThanOrEqual as MoreThanOrEqual2, Not } from "typeorm";
|
|
8547
8794
|
|
|
8548
8795
|
// src/lib/address-geo-validation.ts
|
|
8549
8796
|
import { Country, State, City } from "country-state-city";
|
|
@@ -8700,6 +8947,156 @@ function buildSearchWhereClause(repo, search) {
|
|
|
8700
8947
|
function entityHasSoftDelete(repo) {
|
|
8701
8948
|
return repo.metadata.columns.some((c) => c.propertyName === "deleted");
|
|
8702
8949
|
}
|
|
8950
|
+
var LIST_QUERY_RESERVED = /* @__PURE__ */ new Set(["page", "limit", "sortField", "sortOrder", "search"]);
|
|
8951
|
+
function dayStartUtc(isoDay) {
|
|
8952
|
+
return /* @__PURE__ */ new Date(isoDay + "T00:00:00.000Z");
|
|
8953
|
+
}
|
|
8954
|
+
function dayEndUtc(isoDay) {
|
|
8955
|
+
return /* @__PURE__ */ new Date(isoDay + "T23:59:59.999Z");
|
|
8956
|
+
}
|
|
8957
|
+
function columnTypeLabel(col) {
|
|
8958
|
+
const t = col.type;
|
|
8959
|
+
if (typeof t === "string") return t.toLowerCase();
|
|
8960
|
+
if (typeof t === "function") return t.name?.toLowerCase?.() ?? "";
|
|
8961
|
+
if (t && typeof t === "object" && "name" in t && typeof t.name === "string") {
|
|
8962
|
+
return String(t.name).toLowerCase();
|
|
8963
|
+
}
|
|
8964
|
+
return "";
|
|
8965
|
+
}
|
|
8966
|
+
function isListDateColumn(col) {
|
|
8967
|
+
const tl = columnTypeLabel(col);
|
|
8968
|
+
return DATE_COLUMN_TYPES.has(tl) || col.type === Date || TIMESTAMP_PROP_NAMES.has(col.propertyName);
|
|
8969
|
+
}
|
|
8970
|
+
function isListNumericColumn(col) {
|
|
8971
|
+
const tl = columnTypeLabel(col);
|
|
8972
|
+
if (col.type === Number) return true;
|
|
8973
|
+
const patterns = [
|
|
8974
|
+
"int",
|
|
8975
|
+
"integer",
|
|
8976
|
+
"int2",
|
|
8977
|
+
"int4",
|
|
8978
|
+
"int8",
|
|
8979
|
+
"smallint",
|
|
8980
|
+
"bigint",
|
|
8981
|
+
"float",
|
|
8982
|
+
"double",
|
|
8983
|
+
"decimal",
|
|
8984
|
+
"numeric",
|
|
8985
|
+
"real",
|
|
8986
|
+
"tinyint",
|
|
8987
|
+
"mediumint"
|
|
8988
|
+
];
|
|
8989
|
+
if (patterns.some((x) => tl.includes(x))) return true;
|
|
8990
|
+
if (tl.includes("unsigned") && (tl.includes("int") || tl.includes("bigint") || tl.includes("small"))) return true;
|
|
8991
|
+
if (!tl && /Id$/i.test(col.propertyName) && !TIMESTAMP_PROP_NAMES.has(col.propertyName)) return true;
|
|
8992
|
+
return false;
|
|
8993
|
+
}
|
|
8994
|
+
function isListBooleanColumn(col) {
|
|
8995
|
+
const tl = columnTypeLabel(col);
|
|
8996
|
+
return tl === "boolean" || tl === "bool" || col.type === Boolean;
|
|
8997
|
+
}
|
|
8998
|
+
function isListStringColumn(col) {
|
|
8999
|
+
const tl = columnTypeLabel(col);
|
|
9000
|
+
if (isListDateColumn(col) || isListNumericColumn(col) || isListBooleanColumn(col)) return false;
|
|
9001
|
+
if (["varchar", "character varying", "text", "citext", "uuid", "char", "character", "enum"].some(
|
|
9002
|
+
(x) => tl.includes(x)
|
|
9003
|
+
)) {
|
|
9004
|
+
return true;
|
|
9005
|
+
}
|
|
9006
|
+
if (col.type === String) return true;
|
|
9007
|
+
return false;
|
|
9008
|
+
}
|
|
9009
|
+
function mergeListWhereAnd(where, patch) {
|
|
9010
|
+
if (Object.keys(patch).length === 0) return where;
|
|
9011
|
+
if (Array.isArray(where)) {
|
|
9012
|
+
if (where.length === 0) return [patch];
|
|
9013
|
+
return where.map((w) => ({ ...w, ...patch }));
|
|
9014
|
+
}
|
|
9015
|
+
if (where && typeof where === "object" && Object.keys(where).length > 0) {
|
|
9016
|
+
return { ...where, ...patch };
|
|
9017
|
+
}
|
|
9018
|
+
return patch;
|
|
9019
|
+
}
|
|
9020
|
+
function buildListFilterAndFromSearchParams(repo, searchParams) {
|
|
9021
|
+
const and = {};
|
|
9022
|
+
const columnNames = new Set(repo.metadata.columns.map((c) => c.propertyName));
|
|
9023
|
+
for (const col of repo.metadata.columns) {
|
|
9024
|
+
const name = col.propertyName;
|
|
9025
|
+
if (!columnNames.has(name)) continue;
|
|
9026
|
+
if (name === "deleted" || name === "deletedAt" || name === "deletedBy") continue;
|
|
9027
|
+
if (!isListDateColumn(col)) continue;
|
|
9028
|
+
const from = searchParams.get(`${name}From`)?.trim();
|
|
9029
|
+
const to = searchParams.get(`${name}To`)?.trim();
|
|
9030
|
+
if (!from && !to) continue;
|
|
9031
|
+
if (from && to) {
|
|
9032
|
+
and[name] = Between(dayStartUtc(from), dayEndUtc(to));
|
|
9033
|
+
} else if (from) {
|
|
9034
|
+
and[name] = MoreThanOrEqual2(dayStartUtc(from));
|
|
9035
|
+
} else if (to) {
|
|
9036
|
+
and[name] = LessThanOrEqual(dayEndUtc(to));
|
|
9037
|
+
}
|
|
9038
|
+
}
|
|
9039
|
+
for (const col of repo.metadata.columns) {
|
|
9040
|
+
const name = col.propertyName;
|
|
9041
|
+
if (!columnNames.has(name)) continue;
|
|
9042
|
+
if (name === "deleted" || name === "deletedAt" || name === "deletedBy") continue;
|
|
9043
|
+
if (!isListNumericColumn(col)) continue;
|
|
9044
|
+
if (Object.prototype.hasOwnProperty.call(and, name)) continue;
|
|
9045
|
+
const minRaw = searchParams.get(`${name}Min`)?.trim();
|
|
9046
|
+
const maxRaw = searchParams.get(`${name}Max`)?.trim();
|
|
9047
|
+
if (!minRaw && !maxRaw) continue;
|
|
9048
|
+
const parseNum = (s) => {
|
|
9049
|
+
const n = Number(s);
|
|
9050
|
+
return Number.isFinite(n) ? n : null;
|
|
9051
|
+
};
|
|
9052
|
+
const nMin = minRaw ? parseNum(minRaw) : null;
|
|
9053
|
+
const nMax = maxRaw ? parseNum(maxRaw) : null;
|
|
9054
|
+
if (nMin != null && nMax != null) {
|
|
9055
|
+
and[name] = Between(nMin, nMax);
|
|
9056
|
+
} else if (nMin != null) {
|
|
9057
|
+
and[name] = MoreThanOrEqual2(nMin);
|
|
9058
|
+
} else if (nMax != null) {
|
|
9059
|
+
and[name] = LessThanOrEqual(nMax);
|
|
9060
|
+
}
|
|
9061
|
+
}
|
|
9062
|
+
for (const col of repo.metadata.columns) {
|
|
9063
|
+
const name = col.propertyName;
|
|
9064
|
+
if (!columnNames.has(name)) continue;
|
|
9065
|
+
if (LIST_QUERY_RESERVED.has(name)) continue;
|
|
9066
|
+
if (name === "deleted" || name === "deletedAt" || name === "deletedBy") continue;
|
|
9067
|
+
if (!isListStringColumn(col)) continue;
|
|
9068
|
+
if (Object.prototype.hasOwnProperty.call(and, name)) continue;
|
|
9069
|
+
const raw = searchParams.get(name)?.trim();
|
|
9070
|
+
if (!raw) continue;
|
|
9071
|
+
and[name] = ILike2(`%${raw}%`);
|
|
9072
|
+
}
|
|
9073
|
+
return and;
|
|
9074
|
+
}
|
|
9075
|
+
function buildExactListParamWhere(repo, searchParams) {
|
|
9076
|
+
const extraWhere = {};
|
|
9077
|
+
const columnNames = new Set(repo.metadata.columns.map((c) => c.propertyName));
|
|
9078
|
+
for (const col of repo.metadata.columns) {
|
|
9079
|
+
const name = col.propertyName;
|
|
9080
|
+
if (!columnNames.has(name)) continue;
|
|
9081
|
+
if (name === "deleted" || name === "deletedAt" || name === "deletedBy") continue;
|
|
9082
|
+
if (!isListNumericColumn(col)) continue;
|
|
9083
|
+
const v = searchParams.get(name)?.trim();
|
|
9084
|
+
if (v == null || v === "") continue;
|
|
9085
|
+
const n = Number(v);
|
|
9086
|
+
if (!Number.isFinite(n)) continue;
|
|
9087
|
+
extraWhere[name] = n;
|
|
9088
|
+
}
|
|
9089
|
+
for (const col of repo.metadata.columns) {
|
|
9090
|
+
if (String(col.type) !== "boolean") continue;
|
|
9091
|
+
const name = col.propertyName;
|
|
9092
|
+
if (!columnNames.has(name)) continue;
|
|
9093
|
+
const raw = searchParams.get(name)?.trim();
|
|
9094
|
+
if (raw === "true" || raw === "false") {
|
|
9095
|
+
extraWhere[name] = raw === "true";
|
|
9096
|
+
}
|
|
9097
|
+
}
|
|
9098
|
+
return extraWhere;
|
|
9099
|
+
}
|
|
8703
9100
|
function mergeDeletedFalseWhere(repo, where) {
|
|
8704
9101
|
if (!entityHasSoftDelete(repo)) return where;
|
|
8705
9102
|
const d = { deleted: false };
|
|
@@ -8709,6 +9106,12 @@ function mergeDeletedFalseWhere(repo, where) {
|
|
|
8709
9106
|
}
|
|
8710
9107
|
return Object.keys(where).length > 0 ? { ...where, ...d } : d;
|
|
8711
9108
|
}
|
|
9109
|
+
function pickDefaultListSortField(columnNames, columns) {
|
|
9110
|
+
for (const candidate of ["createdAt", "updatedAt", "id", "name", "sortOrder", "title"]) {
|
|
9111
|
+
if (columnNames.has(candidate)) return candidate;
|
|
9112
|
+
}
|
|
9113
|
+
return columns[0]?.propertyName ?? "id";
|
|
9114
|
+
}
|
|
8712
9115
|
function normalizeProductSku(value) {
|
|
8713
9116
|
if (value == null) return null;
|
|
8714
9117
|
const s = String(value).trim();
|
|
@@ -8811,6 +9214,18 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
8811
9214
|
if (statusFilter) qb.andWhere("order.status = :status", { status: statusFilter });
|
|
8812
9215
|
if (dateFrom) qb.andWhere("order.createdAt >= :dateFrom", { dateFrom: /* @__PURE__ */ new Date(dateFrom + "T00:00:00.000Z") });
|
|
8813
9216
|
if (dateTo) qb.andWhere("order.createdAt <= :dateTo", { dateTo: /* @__PURE__ */ new Date(dateTo + "T23:59:59.999Z") });
|
|
9217
|
+
const totalMin = searchParams.get("totalMin")?.trim();
|
|
9218
|
+
const totalMax = searchParams.get("totalMax")?.trim();
|
|
9219
|
+
if (totalMin) {
|
|
9220
|
+
const n = Number(totalMin);
|
|
9221
|
+
if (Number.isFinite(n)) qb.andWhere("order.total >= :totalMin", { totalMin: n });
|
|
9222
|
+
}
|
|
9223
|
+
if (totalMax) {
|
|
9224
|
+
const n = Number(totalMax);
|
|
9225
|
+
if (Number.isFinite(n)) qb.andWhere("order.total <= :totalMax", { totalMax: n });
|
|
9226
|
+
}
|
|
9227
|
+
const currency = searchParams.get("currency")?.trim();
|
|
9228
|
+
if (currency) qb.andWhere("order.currency ILIKE :orderCurrency", { orderCurrency: `%${currency}%` });
|
|
8814
9229
|
if (orderIdsFromPayment && orderIdsFromPayment.length) qb.andWhere("order.id IN (:...orderIds)", { orderIds: orderIdsFromPayment });
|
|
8815
9230
|
const [rows, total2] = await qb.getManyAndCount();
|
|
8816
9231
|
const data2 = rows.map((order) => {
|
|
@@ -8849,8 +9264,30 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
8849
9264
|
if (statusFilter) qb.andWhere("payment.status = :status", { status: statusFilter });
|
|
8850
9265
|
if (dateFrom) qb.andWhere("payment.createdAt >= :dateFrom", { dateFrom: /* @__PURE__ */ new Date(dateFrom + "T00:00:00.000Z") });
|
|
8851
9266
|
if (dateTo) qb.andWhere("payment.createdAt <= :dateTo", { dateTo: /* @__PURE__ */ new Date(dateTo + "T23:59:59.999Z") });
|
|
9267
|
+
const paidAtFrom = searchParams.get("paidAtFrom")?.trim();
|
|
9268
|
+
const paidAtTo = searchParams.get("paidAtTo")?.trim();
|
|
9269
|
+
if (paidAtFrom) {
|
|
9270
|
+
qb.andWhere("payment.paidAt >= :paidAtFrom", { paidAtFrom: /* @__PURE__ */ new Date(paidAtFrom + "T00:00:00.000Z") });
|
|
9271
|
+
}
|
|
9272
|
+
if (paidAtTo) {
|
|
9273
|
+
qb.andWhere("payment.paidAt <= :paidAtTo", { paidAtTo: /* @__PURE__ */ new Date(paidAtTo + "T23:59:59.999Z") });
|
|
9274
|
+
}
|
|
8852
9275
|
if (methodFilter) qb.andWhere("payment.method = :method", { method: methodFilter });
|
|
8853
9276
|
if (orderNumberParam) qb.andWhere("ord.orderNumber ILIKE :orderNumber", { orderNumber: `%${orderNumberParam}%` });
|
|
9277
|
+
const amountMin = searchParams.get("amountMin")?.trim();
|
|
9278
|
+
const amountMax = searchParams.get("amountMax")?.trim();
|
|
9279
|
+
if (amountMin) {
|
|
9280
|
+
const n = Number(amountMin);
|
|
9281
|
+
if (Number.isFinite(n)) qb.andWhere("payment.amount >= :amountMin", { amountMin: n });
|
|
9282
|
+
}
|
|
9283
|
+
if (amountMax) {
|
|
9284
|
+
const n = Number(amountMax);
|
|
9285
|
+
if (Number.isFinite(n)) qb.andWhere("payment.amount <= :amountMax", { amountMax: n });
|
|
9286
|
+
}
|
|
9287
|
+
const extRef = searchParams.get("externalReference")?.trim();
|
|
9288
|
+
if (extRef) {
|
|
9289
|
+
qb.andWhere("payment.externalReference ILIKE :extRef", { extRef: `%${extRef}%` });
|
|
9290
|
+
}
|
|
8854
9291
|
const [rows, total2] = await qb.getManyAndCount();
|
|
8855
9292
|
const data2 = rows.map((payment) => {
|
|
8856
9293
|
const order = payment.order;
|
|
@@ -8869,7 +9306,10 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
8869
9306
|
const repo2 = dataSource.getRepository(entity);
|
|
8870
9307
|
const statusFilter = searchParams.get("status")?.trim();
|
|
8871
9308
|
const inventory = searchParams.get("inventory")?.trim();
|
|
8872
|
-
const productWhere = {
|
|
9309
|
+
const productWhere = {
|
|
9310
|
+
deleted: false,
|
|
9311
|
+
...buildListFilterAndFromSearchParams(repo2, searchParams)
|
|
9312
|
+
};
|
|
8873
9313
|
if (statusFilter) productWhere.status = statusFilter;
|
|
8874
9314
|
if (inventory === "in_stock") productWhere.quantity = MoreThan2(0);
|
|
8875
9315
|
if (inventory === "out_of_stock") productWhere.quantity = 0;
|
|
@@ -8887,11 +9327,15 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
8887
9327
|
if (search && typeof search === "string" && search.trim()) {
|
|
8888
9328
|
productWhere.name = ILike2(`%${search.trim()}%`);
|
|
8889
9329
|
}
|
|
9330
|
+
const productColumnNames = new Set(repo2.metadata.columns.map((c) => c.propertyName));
|
|
9331
|
+
const defaultProductSort = pickDefaultListSortField(productColumnNames, repo2.metadata.columns);
|
|
9332
|
+
const sortParam2 = (searchParams.get("sortField") ?? "").trim();
|
|
9333
|
+
const productSortField = sortParam2 && productColumnNames.has(sortParam2) ? sortParam2 : defaultProductSort;
|
|
8890
9334
|
const [data2, total2] = await repo2.findAndCount({
|
|
8891
9335
|
where: Object.keys(productWhere).length ? productWhere : void 0,
|
|
8892
9336
|
skip,
|
|
8893
9337
|
take: limit,
|
|
8894
|
-
order: { [
|
|
9338
|
+
order: { [productSortField]: sortOrder }
|
|
8895
9339
|
});
|
|
8896
9340
|
return json({ total: total2, page, limit, totalPages: Math.ceil(total2 / limit), data: data2 });
|
|
8897
9341
|
}
|
|
@@ -8984,36 +9428,22 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
8984
9428
|
}
|
|
8985
9429
|
return json({ total: total2, page, limit, totalPages: Math.ceil(total2 / limit), data: data2 });
|
|
8986
9430
|
}
|
|
8987
|
-
const
|
|
9431
|
+
const defaultSortField = pickDefaultListSortField(columnNames, repo.metadata.columns);
|
|
9432
|
+
const sortParam = (searchParams.get("sortField") ?? "").trim();
|
|
9433
|
+
const sortField = sortParam && columnNames.has(sortParam) ? sortParam : defaultSortField;
|
|
8988
9434
|
let where = {};
|
|
8989
9435
|
if (search) {
|
|
8990
9436
|
where = buildSearchWhereClause(repo, search);
|
|
8991
9437
|
}
|
|
8992
|
-
|
|
8993
|
-
const
|
|
8994
|
-
|
|
8995
|
-
const v = searchParams.get(key);
|
|
8996
|
-
if (v != null && v !== "" && columnNames.has(key)) {
|
|
8997
|
-
const n = Number(v);
|
|
8998
|
-
if (Number.isFinite(n)) extraWhere[key] = n;
|
|
8999
|
-
}
|
|
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
|
-
}
|
|
9010
|
-
if (Object.keys(extraWhere).length > 0) {
|
|
9438
|
+
where = mergeListWhereAnd(where, buildListFilterAndFromSearchParams(repo, searchParams));
|
|
9439
|
+
const exactParamWhere = buildExactListParamWhere(repo, searchParams);
|
|
9440
|
+
if (Object.keys(exactParamWhere).length > 0) {
|
|
9011
9441
|
if (Array.isArray(where)) {
|
|
9012
|
-
where = where.map((w) => ({ ...w, ...
|
|
9442
|
+
where = where.map((w) => ({ ...w, ...exactParamWhere }));
|
|
9013
9443
|
} else if (where && typeof where === "object" && Object.keys(where).length > 0) {
|
|
9014
|
-
where = { ...where, ...
|
|
9444
|
+
where = { ...where, ...exactParamWhere };
|
|
9015
9445
|
} else {
|
|
9016
|
-
where =
|
|
9446
|
+
where = exactParamWhere;
|
|
9017
9447
|
}
|
|
9018
9448
|
}
|
|
9019
9449
|
where = mergeDeletedFalseWhere(repo, where);
|
|
@@ -9093,6 +9523,10 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
9093
9523
|
}
|
|
9094
9524
|
const repo = dataSource.getRepository(entity);
|
|
9095
9525
|
const persistBody = resource === "media" ? body : pickColumnUpdates(repo, body);
|
|
9526
|
+
if (resource === "contacts" && "type" in persistBody) {
|
|
9527
|
+
const t = persistBody.type;
|
|
9528
|
+
if (t === "" || t === "none" || t == null) persistBody.type = null;
|
|
9529
|
+
}
|
|
9096
9530
|
if (resource !== "media" && Object.keys(persistBody).length === 0) {
|
|
9097
9531
|
logCrudClientError("POST create", {
|
|
9098
9532
|
reason: "no_scalar_columns_after_pick",
|
|
@@ -9443,6 +9877,10 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
9443
9877
|
if (!cur) return json({ message: "Not found" }, { status: 404 });
|
|
9444
9878
|
}
|
|
9445
9879
|
const updatePayload = rawBody && typeof rawBody === "object" ? pickColumnUpdates(repo, rawBody) : {};
|
|
9880
|
+
if (resource === "contacts" && "type" in updatePayload) {
|
|
9881
|
+
const t = updatePayload.type;
|
|
9882
|
+
if (t === "" || t === "none" || t == null) updatePayload.type = null;
|
|
9883
|
+
}
|
|
9446
9884
|
if (resource === "media") {
|
|
9447
9885
|
const u = updatePayload;
|
|
9448
9886
|
delete u.kind;
|
|
@@ -10702,7 +11140,7 @@ function createCmsApiHandler(config) {
|
|
|
10702
11140
|
} : usersApi;
|
|
10703
11141
|
const usersHandlers = usersApiMerged ? createUsersApiHandlers(mergePerm(usersApiMerged) ?? usersApiMerged) : null;
|
|
10704
11142
|
const avatarPost = userAvatar ? createUserAvatarHandler(userAvatar) : null;
|
|
10705
|
-
const
|
|
11143
|
+
const profileHandlers = userProfile ? createUserProfileHandler(userProfile) : null;
|
|
10706
11144
|
const settingsHandlers = settingsConfig ? createSettingsApiHandlers(settingsConfig) : null;
|
|
10707
11145
|
const smsMessageTemplateHandlers = createSmsMessageTemplateHandlers({
|
|
10708
11146
|
dataSource,
|
|
@@ -10801,7 +11239,10 @@ function createCmsApiHandler(config) {
|
|
|
10801
11239
|
}
|
|
10802
11240
|
if (path.length === 2) {
|
|
10803
11241
|
if (path[1] === "avatar" && m === "POST" && avatarPost) return avatarPost(req);
|
|
10804
|
-
if (path[1] === "profile" &&
|
|
11242
|
+
if (path[1] === "profile" && profileHandlers) {
|
|
11243
|
+
if (m === "GET") return profileHandlers.GET(req);
|
|
11244
|
+
if (m === "PUT") return profileHandlers.PUT(req);
|
|
11245
|
+
}
|
|
10805
11246
|
const id = path[1];
|
|
10806
11247
|
if (m === "GET") return usersHandlers.getById(req, id);
|
|
10807
11248
|
if (m === "PUT" || m === "PATCH") return usersHandlers.update(req, id);
|