@endora-commerce/mod-customers 0.100.0
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/LICENSE +21 -0
- package/README.md +60 -0
- package/dist/admin/index.d.ts +26 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +68 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/CustomerDetail.d.ts +9 -0
- package/dist/admin/pages/CustomerDetail.d.ts.map +1 -0
- package/dist/admin/pages/CustomerDetail.js +72 -0
- package/dist/admin/pages/CustomerDetail.js.map +1 -0
- package/dist/admin/pages/CustomersList.d.ts +9 -0
- package/dist/admin/pages/CustomersList.d.ts.map +1 -0
- package/dist/admin/pages/CustomersList.js +115 -0
- package/dist/admin/pages/CustomersList.js.map +1 -0
- package/dist/admin/pages/OnlineCustomers.d.ts +9 -0
- package/dist/admin/pages/OnlineCustomers.d.ts.map +1 -0
- package/dist/admin/pages/OnlineCustomers.js +77 -0
- package/dist/admin/pages/OnlineCustomers.js.map +1 -0
- package/dist/admin/panels/HistoryPanels.d.ts +11 -0
- package/dist/admin/panels/HistoryPanels.d.ts.map +1 -0
- package/dist/admin/panels/HistoryPanels.js +64 -0
- package/dist/admin/panels/HistoryPanels.js.map +1 -0
- package/dist/admin/panels/ManagementPanels.d.ts +14 -0
- package/dist/admin/panels/ManagementPanels.d.ts.map +1 -0
- package/dist/admin/panels/ManagementPanels.js +93 -0
- package/dist/admin/panels/ManagementPanels.js.map +1 -0
- package/dist/backend/entities/customer-address.entity.d.ts +25 -0
- package/dist/backend/entities/customer-address.entity.d.ts.map +1 -0
- package/dist/backend/entities/customer-address.entity.js +95 -0
- package/dist/backend/entities/customer-address.entity.js.map +1 -0
- package/dist/backend/index.d.ts +91 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +201 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/plugin.d.ts +168 -0
- package/dist/backend/plugin.d.ts.map +1 -0
- package/dist/backend/plugin.js +83 -0
- package/dist/backend/plugin.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +70 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +313 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.register.d.ts +12 -0
- package/dist/backend/routes.register.d.ts.map +1 -0
- package/dist/backend/routes.register.js +40 -0
- package/dist/backend/routes.register.js.map +1 -0
- package/dist/backend/routes.self.d.ts +46 -0
- package/dist/backend/routes.self.d.ts.map +1 -0
- package/dist/backend/routes.self.js +145 -0
- package/dist/backend/routes.self.js.map +1 -0
- package/dist/backend/serializers.d.ts +13 -0
- package/dist/backend/serializers.d.ts.map +1 -0
- package/dist/backend/serializers.js +39 -0
- package/dist/backend/serializers.js.map +1 -0
- package/dist/backend/services/customer-address-read-port.d.ts +29 -0
- package/dist/backend/services/customer-address-read-port.d.ts.map +1 -0
- package/dist/backend/services/customer-address-read-port.js +53 -0
- package/dist/backend/services/customer-address-read-port.js.map +1 -0
- package/dist/backend/services/customer-address-service.d.ts +94 -0
- package/dist/backend/services/customer-address-service.d.ts.map +1 -0
- package/dist/backend/services/customer-address-service.js +176 -0
- package/dist/backend/services/customer-address-service.js.map +1 -0
- package/dist/backend/services/customer-admin-query-service.d.ts +57 -0
- package/dist/backend/services/customer-admin-query-service.d.ts.map +1 -0
- package/dist/backend/services/customer-admin-query-service.js +97 -0
- package/dist/backend/services/customer-admin-query-service.js.map +1 -0
- package/dist/backend/services/customer-authority-service.d.ts +46 -0
- package/dist/backend/services/customer-authority-service.d.ts.map +1 -0
- package/dist/backend/services/customer-authority-service.js +42 -0
- package/dist/backend/services/customer-authority-service.js.map +1 -0
- package/dist/backend/services/customer-defaults-service.d.ts +48 -0
- package/dist/backend/services/customer-defaults-service.d.ts.map +1 -0
- package/dist/backend/services/customer-defaults-service.js +71 -0
- package/dist/backend/services/customer-defaults-service.js.map +1 -0
- package/dist/backend/services/customer-deletion-service.d.ts +39 -0
- package/dist/backend/services/customer-deletion-service.d.ts.map +1 -0
- package/dist/backend/services/customer-deletion-service.js +94 -0
- package/dist/backend/services/customer-deletion-service.js.map +1 -0
- package/dist/backend/services/customer-moderation-service.d.ts +58 -0
- package/dist/backend/services/customer-moderation-service.d.ts.map +1 -0
- package/dist/backend/services/customer-moderation-service.js +70 -0
- package/dist/backend/services/customer-moderation-service.js.map +1 -0
- package/dist/backend/services/customer-org-assignment-service.d.ts +52 -0
- package/dist/backend/services/customer-org-assignment-service.d.ts.map +1 -0
- package/dist/backend/services/customer-org-assignment-service.js +106 -0
- package/dist/backend/services/customer-org-assignment-service.js.map +1 -0
- package/dist/backend/services/customer-presence-service.d.ts +44 -0
- package/dist/backend/services/customer-presence-service.d.ts.map +1 -0
- package/dist/backend/services/customer-presence-service.js +47 -0
- package/dist/backend/services/customer-presence-service.js.map +1 -0
- package/dist/backend/services/customer-registration-service.d.ts +56 -0
- package/dist/backend/services/customer-registration-service.d.ts.map +1 -0
- package/dist/backend/services/customer-registration-service.js +43 -0
- package/dist/backend/services/customer-registration-service.js.map +1 -0
- package/dist/backend/workers/anonymization-sweep-worker.d.ts +15 -0
- package/dist/backend/workers/anonymization-sweep-worker.d.ts.map +1 -0
- package/dist/backend/workers/anonymization-sweep-worker.js +20 -0
- package/dist/backend/workers/anonymization-sweep-worker.js.map +1 -0
- package/dist/manifest.d.ts +181 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +192 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260611T140404_customers_customer_addresses_init.d.ts +14 -0
- package/dist/migrations/20260611T140404_customers_customer_addresses_init.d.ts.map +1 -0
- package/dist/migrations/20260611T140404_customers_customer_addresses_init.js +40 -0
- package/dist/migrations/20260611T140404_customers_customer_addresses_init.js.map +1 -0
- package/dist/migrations/index.d.ts +27 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +29 -0
- package/dist/migrations/index.js.map +1 -0
- package/docs/customers.md +108 -0
- package/i18n/en.json +94 -0
- package/i18n/pl.json +94 -0
- package/package.json +101 -0
- package/tailwind.css +14 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { blockCustomerRequestSchema, startImpersonationRequestSchema, assignOrganizationRequestSchema, assignCustomerGroupRequestSchema, customerAddressInputSchema, customFieldValuesSchema, validateCustomerVatRequestSchema, ERROR_CODES, isCustomFieldValidationFailure, SESSION_COOKIE_NAME, ADMIN_SESSION_COOKIE_NAME, } from '@endora-commerce/contracts';
|
|
2
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
3
|
+
import { rethrowIfModuleDisabled } from '@endora-commerce/platform/kernel';
|
|
4
|
+
import { serializeCustomerAddress, serializeOrganizationAddress, } from './serializers.js';
|
|
5
|
+
const ADMIN_SHADOW_COOKIE = 'admin_shadow_session';
|
|
6
|
+
function setSessionCookie(reply, value, expiresAt) {
|
|
7
|
+
reply.setCookie(SESSION_COOKIE_NAME, value, {
|
|
8
|
+
path: '/',
|
|
9
|
+
httpOnly: true,
|
|
10
|
+
sameSite: 'lax',
|
|
11
|
+
secure: process.env['NODE_ENV'] === 'production',
|
|
12
|
+
expires: expiresAt,
|
|
13
|
+
signed: false,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
export async function registerCustomersAdminRoutes(app, deps) {
|
|
17
|
+
const { requireAdmin, resolveModerationActor, moderationService } = deps;
|
|
18
|
+
const { impersonationService, queryService, accounts, accountWrites } = deps;
|
|
19
|
+
const customFieldValues = deps.customFieldValues;
|
|
20
|
+
// PATCH /api/v1/admin/customers/:id/custom-fields (feature 055).
|
|
21
|
+
//
|
|
22
|
+
// Feature 075 — the row is `customer_accounts`', so the Command that writes
|
|
23
|
+
// it and the audit row it carries are on that side of the port. What stays
|
|
24
|
+
// here is the merge: this surface owns the screen and resolves
|
|
25
|
+
// `custom_fields`' validator, and the merge has to run between the owner's
|
|
26
|
+
// load and its write or a concurrent patch is lost. Hence the callback.
|
|
27
|
+
if (customFieldValues) {
|
|
28
|
+
app.patch('/api/v1/admin/customers/:id/custom-fields', { preHandler: requireAdmin('customers:manage'), schema: { body: customFieldValuesSchema } }, async (request) => {
|
|
29
|
+
const patch = customFieldValuesSchema.parse(request.body);
|
|
30
|
+
const id = request.params.id;
|
|
31
|
+
try {
|
|
32
|
+
await accountWrites.setCustomFieldValues(id, (current) => customFieldValues.validateAndMerge('customer', current, patch));
|
|
33
|
+
const detail = await queryService.getDetail(id);
|
|
34
|
+
if (!detail)
|
|
35
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Customer not found.');
|
|
36
|
+
return { data: detail };
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
// The tolerance is narrow and one-shot: a rejected custom-field patch
|
|
40
|
+
// is a 422 and nothing else here is. `rethrowIfModuleDisabled` comes
|
|
41
|
+
// first so a switched-off owner keeps failing closed instead of being
|
|
42
|
+
// laundered into a validation error (AGENTS.md composition item 7).
|
|
43
|
+
rethrowIfModuleDisabled(err);
|
|
44
|
+
if (isCustomFieldValidationFailure(err)) {
|
|
45
|
+
throw new HttpError(422, ERROR_CODES.CUSTOM_FIELD_VALUE_INVALID, 'One or more custom fields are invalid.', err.errors.map((e) => ({ path: e.field, issue: e.message })));
|
|
46
|
+
}
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
// GET /api/v1/admin/customers — list (authority-scoped)
|
|
52
|
+
app.get('/api/v1/admin/customers', { preHandler: requireAdmin('customers:read') }, async (request) => {
|
|
53
|
+
const actor = await resolveModerationActor(request);
|
|
54
|
+
const q = request.query;
|
|
55
|
+
const page = Math.max(1, Number.parseInt(q.page ?? '1', 10) || 1);
|
|
56
|
+
const pageSize = Math.min(100, Math.max(1, Number.parseInt(q.pageSize ?? '20', 10) || 20));
|
|
57
|
+
const result = await queryService.list({
|
|
58
|
+
...(q.q !== undefined ? { q: q.q } : {}),
|
|
59
|
+
...(q.status !== undefined ? { status: q.status } : {}),
|
|
60
|
+
...(q.organizationId !== undefined ? { organizationId: q.organizationId } : {}),
|
|
61
|
+
...(q.customerGroupId !== undefined ? { customerGroupId: q.customerGroupId } : {}),
|
|
62
|
+
page,
|
|
63
|
+
pageSize,
|
|
64
|
+
}, {
|
|
65
|
+
isPlatformAdmin: actor.isPlatformAdmin,
|
|
66
|
+
allowedOrganizationIds: actor.allowedOrganizationIds,
|
|
67
|
+
});
|
|
68
|
+
return { data: result.rows, meta: { page, pageSize, total: result.total } };
|
|
69
|
+
});
|
|
70
|
+
// GET /api/v1/admin/customers/:id — detail
|
|
71
|
+
app.get('/api/v1/admin/customers/:id', { preHandler: requireAdmin('customers:read') }, async (request, reply) => {
|
|
72
|
+
await resolveModerationActor(request);
|
|
73
|
+
const detail = await queryService.getDetail(request.params.id);
|
|
74
|
+
if (!detail) {
|
|
75
|
+
reply.code(404);
|
|
76
|
+
return { error: { code: 'CUSTOMER_NOT_FOUND', message: 'Customer not found.' } };
|
|
77
|
+
}
|
|
78
|
+
return { data: detail };
|
|
79
|
+
});
|
|
80
|
+
// POST /api/v1/admin/customers/:id/block
|
|
81
|
+
app.post('/api/v1/admin/customers/:id/block', { preHandler: requireAdmin('customers:manage'), schema: { body: blockCustomerRequestSchema } }, async (request) => {
|
|
82
|
+
const actor = await resolveModerationActor(request);
|
|
83
|
+
const body = blockCustomerRequestSchema.parse(request.body ?? {});
|
|
84
|
+
const customer = await moderationService.block({
|
|
85
|
+
targetCustomerAccountId: request.params.id,
|
|
86
|
+
actor,
|
|
87
|
+
reason: body.reason ?? null,
|
|
88
|
+
audit: {
|
|
89
|
+
ipAddress: request.ip,
|
|
90
|
+
...(typeof request.headers['user-agent'] === 'string'
|
|
91
|
+
? { userAgent: request.headers['user-agent'] }
|
|
92
|
+
: {}),
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
return { data: { id: customer.id, blocked: customer.blockedAt != null } };
|
|
96
|
+
});
|
|
97
|
+
// POST /api/v1/admin/customers/:id/unblock
|
|
98
|
+
app.post('/api/v1/admin/customers/:id/unblock', { preHandler: requireAdmin('customers:manage') }, async (request) => {
|
|
99
|
+
const actor = await resolveModerationActor(request);
|
|
100
|
+
const customer = await moderationService.unblock({
|
|
101
|
+
targetCustomerAccountId: request.params.id,
|
|
102
|
+
actor,
|
|
103
|
+
audit: {
|
|
104
|
+
ipAddress: request.ip,
|
|
105
|
+
...(typeof request.headers['user-agent'] === 'string'
|
|
106
|
+
? { userAgent: request.headers['user-agent'] }
|
|
107
|
+
: {}),
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
return { data: { id: customer.id, blocked: customer.blockedAt != null } };
|
|
111
|
+
});
|
|
112
|
+
// POST /api/v1/admin/customers/:id/impersonate (US4) — works for org-less
|
|
113
|
+
// customers (organizationId omitted → target resolved by id alone, R10).
|
|
114
|
+
app.post('/api/v1/admin/customers/:id/impersonate', {
|
|
115
|
+
preHandler: requireAdmin('customers:impersonate'),
|
|
116
|
+
schema: { body: startImpersonationRequestSchema },
|
|
117
|
+
}, async (request, reply) => {
|
|
118
|
+
const actor = await resolveModerationActor(request);
|
|
119
|
+
const body = startImpersonationRequestSchema.parse(request.body ?? {});
|
|
120
|
+
const cookies = request.cookies;
|
|
121
|
+
// Admin session lives in the dedicated admin cookie; fall back to the
|
|
122
|
+
// legacy customer-cookie name so older sessions / test stubs still work.
|
|
123
|
+
const adminCookie = cookies?.[ADMIN_SESSION_COOKIE_NAME] ?? cookies?.[SESSION_COOKIE_NAME] ?? '';
|
|
124
|
+
const result = await impersonationService.start({
|
|
125
|
+
adminUserId: actor.adminUserId,
|
|
126
|
+
adminSessionCookieValue: adminCookie,
|
|
127
|
+
customerAccountId: request.params.id,
|
|
128
|
+
...(body.reason !== undefined ? { reason: body.reason } : {}),
|
|
129
|
+
...(request.ip ? { ip: request.ip } : {}),
|
|
130
|
+
...(typeof request.headers['user-agent'] === 'string'
|
|
131
|
+
? { userAgent: request.headers['user-agent'] }
|
|
132
|
+
: {}),
|
|
133
|
+
requestId: request.id,
|
|
134
|
+
});
|
|
135
|
+
setSessionCookie(reply, result.impersonationCookieValue, result.impersonationExpiresAt);
|
|
136
|
+
reply.setCookie(ADMIN_SHADOW_COOKIE, result.adminShadowSessionCookieValue, {
|
|
137
|
+
path: '/',
|
|
138
|
+
httpOnly: true,
|
|
139
|
+
sameSite: 'lax',
|
|
140
|
+
secure: process.env['NODE_ENV'] === 'production',
|
|
141
|
+
expires: result.impersonationExpiresAt,
|
|
142
|
+
signed: false,
|
|
143
|
+
});
|
|
144
|
+
return {
|
|
145
|
+
data: {
|
|
146
|
+
impersonationSessionId: result.impersonationSessionId,
|
|
147
|
+
impersonatedCustomerAccount: {
|
|
148
|
+
id: result.impersonatedCustomerAccount.id,
|
|
149
|
+
email: result.impersonatedCustomerAccount.email,
|
|
150
|
+
firstName: result.impersonatedCustomerAccount.firstName,
|
|
151
|
+
lastName: result.impersonatedCustomerAccount.lastName,
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
// ── Organization assignment (US5) ───────────────────────────────────────
|
|
157
|
+
app.post('/api/v1/admin/customers/:id/organization', { preHandler: requireAdmin('customers:manage'), schema: { body: assignOrganizationRequestSchema } }, async (request) => {
|
|
158
|
+
const actor = await resolveModerationActor(request);
|
|
159
|
+
const body = assignOrganizationRequestSchema.parse(request.body);
|
|
160
|
+
const customer = await deps.orgAssignmentService.assign(request.params.id, body.organizationId, actor);
|
|
161
|
+
return { data: { id: customer.id, organizationId: customer.organizationId } };
|
|
162
|
+
});
|
|
163
|
+
/**
|
|
164
|
+
* Detach a customer from the company they belong to.
|
|
165
|
+
*
|
|
166
|
+
* The verb is still `DELETE` and the response still carries the customer's
|
|
167
|
+
* organisation, but since D-178 the organisation it carries is the customer's
|
|
168
|
+
* **own** personal one rather than `null`. An account with no organisation
|
|
169
|
+
* cannot transact and cannot be scoped, and the column refuses one.
|
|
170
|
+
*/
|
|
171
|
+
app.delete('/api/v1/admin/customers/:id/organization', { preHandler: requireAdmin('customers:manage') }, async (request) => {
|
|
172
|
+
const actor = await resolveModerationActor(request);
|
|
173
|
+
const customer = await deps.orgAssignmentService.unassign(request.params.id, actor);
|
|
174
|
+
return { data: { id: customer.id, organizationId: customer.organizationId } };
|
|
175
|
+
});
|
|
176
|
+
// ── Customer group (US6) ────────────────────────────────────────────────
|
|
177
|
+
app.put('/api/v1/admin/customers/:id/customer-group', { preHandler: requireAdmin('customers:manage'), schema: { body: assignCustomerGroupRequestSchema } }, async (request) => {
|
|
178
|
+
const actor = await resolveModerationActor(request);
|
|
179
|
+
const body = assignCustomerGroupRequestSchema.parse(request.body);
|
|
180
|
+
const customer = await deps.orgAssignmentService.setCustomerGroup(request.params.id, body.customerGroupId, actor);
|
|
181
|
+
return { data: { id: customer.id, customerGroupId: customer.customerGroupId ?? null } };
|
|
182
|
+
});
|
|
183
|
+
// ── Addresses (US5) ─────────────────────────────────────────────────────
|
|
184
|
+
app.get('/api/v1/admin/customers/:id/addresses', { preHandler: requireAdmin('customers:read') }, async (request) => {
|
|
185
|
+
await resolveModerationActor(request);
|
|
186
|
+
const customer = await accounts.findById(request.params.id);
|
|
187
|
+
const personal = await deps.addressService.listPersonal(request.params.id);
|
|
188
|
+
// `customer` is `null` when no such account exists; the organisation half
|
|
189
|
+
// is then empty rather than 404. D-178 narrowed what this condition
|
|
190
|
+
// covers — it used to fold "the customer has no organisation" in with it,
|
|
191
|
+
// and that state is gone — but the missing-customer arm is unchanged and
|
|
192
|
+
// is not this ruling's to settle.
|
|
193
|
+
const organization = customer === null
|
|
194
|
+
? []
|
|
195
|
+
: await deps.addressService.listOrganizationAddresses(customer.organizationId);
|
|
196
|
+
return {
|
|
197
|
+
data: {
|
|
198
|
+
personal: personal.map(serializeCustomerAddress),
|
|
199
|
+
organization: organization.map(serializeOrganizationAddress),
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
});
|
|
203
|
+
app.post('/api/v1/admin/customers/:id/addresses', { preHandler: requireAdmin('customers:manage'), schema: { body: customerAddressInputSchema } }, async (request, reply) => {
|
|
204
|
+
await resolveModerationActor(request);
|
|
205
|
+
const body = customerAddressInputSchema.parse(request.body);
|
|
206
|
+
const created = await deps.addressService.create(request.params.id, {
|
|
207
|
+
kind: body.kind,
|
|
208
|
+
recipientName: body.recipientName,
|
|
209
|
+
street: body.street,
|
|
210
|
+
city: body.city,
|
|
211
|
+
postalCode: body.postalCode,
|
|
212
|
+
country: body.country,
|
|
213
|
+
phone: body.phone,
|
|
214
|
+
isDefault: body.isDefault,
|
|
215
|
+
});
|
|
216
|
+
reply.code(201);
|
|
217
|
+
return { data: serializeCustomerAddress(created) };
|
|
218
|
+
});
|
|
219
|
+
// ── NIP / VAT validation (US5) ──────────────────────────────────────────
|
|
220
|
+
app.post('/api/v1/admin/customers/:id/vat-validate', { preHandler: requireAdmin('customers:manage'), schema: { body: validateCustomerVatRequestSchema } }, async (request) => {
|
|
221
|
+
await resolveModerationActor(request);
|
|
222
|
+
const body = validateCustomerVatRequestSchema.parse(request.body);
|
|
223
|
+
const result = await deps.vatValidator.validate({
|
|
224
|
+
taxId: body.taxId,
|
|
225
|
+
...(body.countryCode !== undefined ? { countryCode: body.countryCode } : {}),
|
|
226
|
+
});
|
|
227
|
+
return {
|
|
228
|
+
data: {
|
|
229
|
+
outcome: result.outcome,
|
|
230
|
+
legalName: result.legalName,
|
|
231
|
+
address: result.address,
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
});
|
|
235
|
+
// ── Read-only history panels (US5) ──────────────────────────────────────
|
|
236
|
+
app.get('/api/v1/admin/customers/:id/orders', { preHandler: requireAdmin('customers:read') }, async (request) => {
|
|
237
|
+
await resolveModerationActor(request);
|
|
238
|
+
const page = Math.max(1, Number.parseInt(request.query.page ?? '1', 10) || 1);
|
|
239
|
+
const pageSize = Math.min(100, Math.max(1, Number.parseInt(request.query.pageSize ?? '20', 10) || 20));
|
|
240
|
+
const result = await deps.orderList.list({
|
|
241
|
+
placedByCustomerAccountId: request.params.id,
|
|
242
|
+
page,
|
|
243
|
+
pageSize,
|
|
244
|
+
});
|
|
245
|
+
return { data: result.rows, meta: { page, pageSize, total: result.total } };
|
|
246
|
+
});
|
|
247
|
+
app.get('/api/v1/admin/customers/:id/quote-requests', { preHandler: requireAdmin('customers:read') }, async (request) => {
|
|
248
|
+
await resolveModerationActor(request);
|
|
249
|
+
const customer = await accounts.findById(request.params.id);
|
|
250
|
+
const data = await deps.rfqService.listForCustomer({
|
|
251
|
+
customerAccountId: request.params.id,
|
|
252
|
+
organizationId: customer?.organizationId ?? '',
|
|
253
|
+
isOrgAdmin: false,
|
|
254
|
+
});
|
|
255
|
+
return { data };
|
|
256
|
+
});
|
|
257
|
+
app.get('/api/v1/admin/customers/:id/carts', { preHandler: requireAdmin('customers:read') }, async (request) => {
|
|
258
|
+
await resolveModerationActor(request);
|
|
259
|
+
return { data: await deps.cartQueryService.listForCustomer(request.params.id) };
|
|
260
|
+
});
|
|
261
|
+
// ── Online customers (US7) ──────────────────────────────────────────────
|
|
262
|
+
app.get('/api/v1/admin/customers/online', { preHandler: requireAdmin('customers:read') }, async (request) => {
|
|
263
|
+
await resolveModerationActor(request);
|
|
264
|
+
return { data: await deps.presenceService.listOnline() };
|
|
265
|
+
});
|
|
266
|
+
// ── Password reset (US7) ────────────────────────────────────────────────
|
|
267
|
+
app.post('/api/v1/admin/customers/:id/password-reset', { preHandler: requireAdmin('customers:manage') }, async (request, reply) => {
|
|
268
|
+
const actor = await resolveModerationActor(request);
|
|
269
|
+
const customer = await accounts.findById(request.params.id, { activeOnly: true });
|
|
270
|
+
if (!customer) {
|
|
271
|
+
reply.code(404);
|
|
272
|
+
return { error: { code: 'CUSTOMER_NOT_FOUND', message: 'Customer not found.' } };
|
|
273
|
+
}
|
|
274
|
+
const { rawToken } = await deps.passwordResetService.requestReset(customer.email);
|
|
275
|
+
if (rawToken) {
|
|
276
|
+
const link = `${deps.storefrontBaseUrl}/reset-password?token=${rawToken}`;
|
|
277
|
+
const outcome = await deps.mailer.send({
|
|
278
|
+
messageId: `admin-pwd-reset.${customer.id}.${rawToken.slice(0, 8)}`,
|
|
279
|
+
to: customer.email,
|
|
280
|
+
subject: 'Set a new password',
|
|
281
|
+
text: `An administrator started a password reset for your account. Set a new password here: ${link}`,
|
|
282
|
+
kind: 'admin_password_reset',
|
|
283
|
+
});
|
|
284
|
+
if (outcome.status !== 'sent') {
|
|
285
|
+
// The reset token is minted either way, and the audit row below
|
|
286
|
+
// records the request rather than the delivery — so the message's own
|
|
287
|
+
// fate is named here and kept by D-59's record.
|
|
288
|
+
request.log.warn({ customerId: customer.id, reason: outcome.reason }, '[customers] the password-reset e-mail was not sent');
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
await deps.auditLogService.record({
|
|
292
|
+
actorAdminUserId: actor.adminUserId,
|
|
293
|
+
action: 'customer_account.password_reset_requested',
|
|
294
|
+
objectType: 'customer_account',
|
|
295
|
+
objectId: customer.id,
|
|
296
|
+
stateBefore: null,
|
|
297
|
+
stateAfter: null,
|
|
298
|
+
});
|
|
299
|
+
return { data: { ok: true } };
|
|
300
|
+
});
|
|
301
|
+
// ── Soft-delete + restore (US7) ─────────────────────────────────────────
|
|
302
|
+
app.delete('/api/v1/admin/customers/:id', { preHandler: requireAdmin('customers:manage') }, async (request) => {
|
|
303
|
+
const actor = await resolveModerationActor(request);
|
|
304
|
+
const customer = await deps.deletionService.softDelete(request.params.id, actor);
|
|
305
|
+
return { data: { id: customer.id, deleted: customer.deletedAt != null } };
|
|
306
|
+
});
|
|
307
|
+
app.post('/api/v1/admin/customers/:id/restore', { preHandler: requireAdmin('customers:manage') }, async (request) => {
|
|
308
|
+
const actor = await resolveModerationActor(request);
|
|
309
|
+
const customer = await deps.deletionService.restore(request.params.id, actor);
|
|
310
|
+
return { data: { id: customer.id, deleted: customer.deletedAt != null } };
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=routes.admin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.admin.js","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,0BAA0B,EAC1B,+BAA+B,EAC/B,+BAA+B,EAC/B,gCAAgC,EAChC,0BAA0B,EAC1B,uBAAuB,EACvB,gCAAgC,EAChC,WAAW,EACX,8BAA8B,EAC9B,mBAAmB,EACnB,yBAAyB,GAU1B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAS3E,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,kBAAkB,CAAC;AAE1B,MAAM,mBAAmB,GAAG,sBAAsB,CAAC;AAEnD,SAAS,gBAAgB,CAAC,KAAmB,EAAE,KAAa,EAAE,SAAe;IAC3E,KAAK,CAAC,SAAS,CAAC,mBAAmB,EAAE,KAAK,EAAE;QAC1C,IAAI,EAAE,GAAG;QACT,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,KAAK;QACf,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,YAAY;QAChD,OAAO,EAAE,SAAS;QAClB,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;AACL,CAAC;AAmED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,GAAoB,EACpB,IAAwB;IAExB,MAAM,EAAE,YAAY,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,GAAG,IAAI,CAAC;IACzE,MAAM,EAAE,oBAAoB,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC;IAC7E,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,CAAC;IAEjD,iEAAiE;IACjE,EAAE;IACF,4EAA4E;IAC5E,2EAA2E;IAC3E,+DAA+D;IAC/D,2EAA2E;IAC3E,wEAAwE;IACxE,IAAI,iBAAiB,EAAE,CAAC;QACtB,GAAG,CAAC,KAAK,CACP,2CAA2C,EAC3C,EAAE,UAAU,EAAE,YAAY,CAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,uBAAuB,EAAE,EAAE,EAC3F,KAAK,EAAE,OAAO,EAAE,EAAE;YAChB,MAAM,KAAK,GAAG,uBAAuB,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC1D,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACH,MAAM,aAAa,CAAC,oBAAoB,CAAC,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE,CACvD,iBAAiB,CAAC,gBAAgB,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,CAC/D,CAAC;gBACF,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;gBAChD,IAAI,CAAC,MAAM;oBAAE,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,SAAS,EAAE,qBAAqB,CAAC,CAAC;gBACpF,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;YAC1B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,sEAAsE;gBACtE,qEAAqE;gBACrE,sEAAsE;gBACtE,oEAAoE;gBACpE,uBAAuB,CAAC,GAAG,CAAC,CAAC;gBAC7B,IAAI,8BAA8B,CAAC,GAAG,CAAC,EAAE,CAAC;oBACxC,MAAM,IAAI,SAAS,CACjB,GAAG,EACH,WAAW,CAAC,0BAA0B,EACtC,wCAAwC,EACxC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAC7D,CAAC;gBACJ,CAAC;gBACD,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC,CACF,CAAC;IACJ,CAAC;IAED,wDAAwD;IACxD,GAAG,CAAC,GAAG,CAUL,yBAAyB,EACzB,EAAE,UAAU,EAAE,YAAY,CAAC,gBAAgB,CAAC,EAAE,EAC9C,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,KAAK,GAAG,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC;QACxB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;QAClE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,IAAI,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC3F,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,CACpC;YACE,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACxC,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvD,GAAG,CAAC,CAAC,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/E,GAAG,CAAC,CAAC,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClF,IAAI;YACJ,QAAQ;SACT,EACD;YACE,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,sBAAsB,EAAE,KAAK,CAAC,sBAAsB;SACrD,CACF,CAAC;QACF,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC;IAC9E,CAAC,CACF,CAAC;IAEF,2CAA2C;IAC3C,GAAG,CAAC,GAAG,CACL,6BAA6B,EAC7B,EAAE,UAAU,EAAE,YAAY,CAAC,gBAAgB,CAAC,EAAE,EAC9C,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC/D,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAChB,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,qBAAqB,EAAE,EAAE,CAAC;QACnF,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1B,CAAC,CACF,CAAC;IAEF,yCAAyC;IACzC,GAAG,CAAC,IAAI,CACN,mCAAmC,EACnC,EAAE,UAAU,EAAE,YAAY,CAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,0BAA0B,EAAE,EAAE,EAC9F,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,KAAK,GAAG,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,0BAA0B,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAClE,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC;YAC7C,uBAAuB,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE;YAC1C,KAAK;YACL,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI;YAC3B,KAAK,EAAE;gBACL,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,GAAG,CAAC,OAAO,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,QAAQ;oBACnD,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE;oBAC9C,CAAC,CAAC,EAAE,CAAC;aACR;SACF,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,SAAS,IAAI,IAAI,EAAE,EAAE,CAAC;IAC5E,CAAC,CACF,CAAC;IAEF,2CAA2C;IAC3C,GAAG,CAAC,IAAI,CACN,qCAAqC,EACrC,EAAE,UAAU,EAAE,YAAY,CAAC,kBAAkB,CAAC,EAAE,EAChD,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,KAAK,GAAG,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC;YAC/C,uBAAuB,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE;YAC1C,KAAK;YACL,KAAK,EAAE;gBACL,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,GAAG,CAAC,OAAO,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,QAAQ;oBACnD,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE;oBAC9C,CAAC,CAAC,EAAE,CAAC;aACR;SACF,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,SAAS,IAAI,IAAI,EAAE,EAAE,CAAC;IAC5E,CAAC,CACF,CAAC;IAEF,0EAA0E;IAC1E,yEAAyE;IACzE,GAAG,CAAC,IAAI,CACN,yCAAyC,EACzC;QACE,UAAU,EAAE,YAAY,CAAC,uBAAuB,CAAC;QACjD,MAAM,EAAE,EAAE,IAAI,EAAE,+BAA+B,EAAE;KAClD,EACD,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,KAAK,GAAG,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,+BAA+B,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QACvE,MAAM,OAAO,GAAI,OAA4D,CAAC,OAAO,CAAC;QACtF,sEAAsE;QACtE,yEAAyE;QACzE,MAAM,WAAW,GACf,OAAO,EAAE,CAAC,yBAAyB,CAAC,IAAI,OAAO,EAAE,CAAC,mBAAmB,CAAC,IAAI,EAAE,CAAC;QAE/E,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,KAAK,CAAC;YAC9C,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,uBAAuB,EAAE,WAAW;YACpC,iBAAiB,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE;YACpC,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7D,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzC,GAAG,CAAC,OAAO,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,QAAQ;gBACnD,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE;gBAC9C,CAAC,CAAC,EAAE,CAAC;YACP,SAAS,EAAE,OAAO,CAAC,EAAE;SACtB,CAAC,CAAC;QAEH,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,wBAAwB,EAAE,MAAM,CAAC,sBAAsB,CAAC,CAAC;QACxF,KAAK,CAAC,SAAS,CAAC,mBAAmB,EAAE,MAAM,CAAC,6BAA6B,EAAE;YACzE,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,YAAY;YAChD,OAAO,EAAE,MAAM,CAAC,sBAAsB;YACtC,MAAM,EAAE,KAAK;SACd,CAAC,CAAC;QAEH,OAAO;YACL,IAAI,EAAE;gBACJ,sBAAsB,EAAE,MAAM,CAAC,sBAAsB;gBACrD,2BAA2B,EAAE;oBAC3B,EAAE,EAAE,MAAM,CAAC,2BAA2B,CAAC,EAAE;oBACzC,KAAK,EAAE,MAAM,CAAC,2BAA2B,CAAC,KAAK;oBAC/C,SAAS,EAAE,MAAM,CAAC,2BAA2B,CAAC,SAAS;oBACvD,QAAQ,EAAE,MAAM,CAAC,2BAA2B,CAAC,QAAQ;iBACtD;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,2EAA2E;IAE3E,GAAG,CAAC,IAAI,CACN,0CAA0C,EAC1C,EAAE,UAAU,EAAE,YAAY,CAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,+BAA+B,EAAE,EAAE,EACnG,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,KAAK,GAAG,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,+BAA+B,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACjE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,MAAM,CACrD,OAAO,CAAC,MAAM,CAAC,EAAE,EACjB,IAAI,CAAC,cAAc,EACnB,KAAK,CACN,CAAC;QACF,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,cAAc,EAAE,QAAQ,CAAC,cAAc,EAAE,EAAE,CAAC;IAChF,CAAC,CACF,CAAC;IAEF;;;;;;;OAOG;IACH,GAAG,CAAC,MAAM,CACR,0CAA0C,EAC1C,EAAE,UAAU,EAAE,YAAY,CAAC,kBAAkB,CAAC,EAAE,EAChD,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,KAAK,GAAG,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACpF,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,cAAc,EAAE,QAAQ,CAAC,cAAc,EAAE,EAAE,CAAC;IAChF,CAAC,CACF,CAAC;IAEF,2EAA2E;IAE3E,GAAG,CAAC,GAAG,CACL,4CAA4C,EAC5C,EAAE,UAAU,EAAE,YAAY,CAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,gCAAgC,EAAE,EAAE,EACpG,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,KAAK,GAAG,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,gCAAgC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAClE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,gBAAgB,CAC/D,OAAO,CAAC,MAAM,CAAC,EAAE,EACjB,IAAI,CAAC,eAAe,EACpB,KAAK,CACN,CAAC;QACF,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,eAAe,EAAE,QAAQ,CAAC,eAAe,IAAI,IAAI,EAAE,EAAE,CAAC;IAC1F,CAAC,CACF,CAAC;IAEF,2EAA2E;IAE3E,GAAG,CAAC,GAAG,CACL,uCAAuC,EACvC,EAAE,UAAU,EAAE,YAAY,CAAC,gBAAgB,CAAC,EAAE,EAC9C,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC3E,0EAA0E;QAC1E,oEAAoE;QACpE,0EAA0E;QAC1E,yEAAyE;QACzE,kCAAkC;QAClC,MAAM,YAAY,GAChB,QAAQ,KAAK,IAAI;YACf,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,MAAM,IAAI,CAAC,cAAc,CAAC,yBAAyB,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;QACnF,OAAO;YACL,IAAI,EAAE;gBACJ,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,wBAAwB,CAAC;gBAChD,YAAY,EAAE,YAAY,CAAC,GAAG,CAAC,4BAA4B,CAAC;aAC7D;SACF,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,GAAG,CAAC,IAAI,CACN,uCAAuC,EACvC,EAAE,UAAU,EAAE,YAAY,CAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,0BAA0B,EAAE,EAAE,EAC9F,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,0BAA0B,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE;YAClE,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC,CAAC;QACH,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChB,OAAO,EAAE,IAAI,EAAE,wBAAwB,CAAC,OAAO,CAAC,EAAE,CAAC;IACrD,CAAC,CACF,CAAC;IAEF,2EAA2E;IAE3E,GAAG,CAAC,IAAI,CACN,0CAA0C,EAC1C,EAAE,UAAU,EAAE,YAAY,CAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,gCAAgC,EAAE,EAAE,EACpG,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,gCAAgC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAClE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;YAC9C,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,GAAG,CAAC,IAAI,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC7E,CAAC,CAAC;QACH,OAAO;YACL,IAAI,EAAE;gBACJ,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,OAAO,EAAE,MAAM,CAAC,OAAO;aACxB;SACF,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,2EAA2E;IAE3E,GAAG,CAAC,GAAG,CACL,oCAAoC,EACpC,EAAE,UAAU,EAAE,YAAY,CAAC,gBAAgB,CAAC,EAAE,EAC9C,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9E,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,IAAI,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACvG,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YACvC,yBAAyB,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE;YAC5C,IAAI;YACJ,QAAQ;SACT,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC;IAC9E,CAAC,CACF,CAAC;IAEF,GAAG,CAAC,GAAG,CACL,4CAA4C,EAC5C,EAAE,UAAU,EAAE,YAAY,CAAC,gBAAgB,CAAC,EAAE,EAC9C,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC5D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC;YACjD,iBAAiB,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE;YACpC,cAAc,EAAE,QAAQ,EAAE,cAAc,IAAI,EAAE;YAC9C,UAAU,EAAE,KAAK;SAClB,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,CAAC;IAClB,CAAC,CACF,CAAC;IAEF,GAAG,CAAC,GAAG,CACL,mCAAmC,EACnC,EAAE,UAAU,EAAE,YAAY,CAAC,gBAAgB,CAAC,EAAE,EAC9C,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACtC,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;IAClF,CAAC,CACF,CAAC;IAEF,2EAA2E;IAE3E,GAAG,CAAC,GAAG,CACL,gCAAgC,EAChC,EAAE,UAAU,EAAE,YAAY,CAAC,gBAAgB,CAAC,EAAE,EAC9C,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACtC,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,EAAE,CAAC;IAC3D,CAAC,CACF,CAAC;IAEF,2EAA2E;IAE3E,GAAG,CAAC,IAAI,CACN,4CAA4C,EAC5C,EAAE,UAAU,EAAE,YAAY,CAAC,kBAAkB,CAAC,EAAE,EAChD,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,KAAK,GAAG,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QAClF,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAChB,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,qBAAqB,EAAE,EAAE,CAAC;QACnF,CAAC;QACD,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAClF,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,iBAAiB,yBAAyB,QAAQ,EAAE,CAAC;YAC1E,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;gBACrC,SAAS,EAAE,mBAAmB,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;gBACnE,EAAE,EAAE,QAAQ,CAAC,KAAK;gBAClB,OAAO,EAAE,oBAAoB;gBAC7B,IAAI,EAAE,wFAAwF,IAAI,EAAE;gBACpG,IAAI,EAAE,sBAAsB;aAC7B,CAAC,CAAC;YACH,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC9B,gEAAgE;gBAChE,sEAAsE;gBACtE,gDAAgD;gBAChD,OAAO,CAAC,GAAG,CAAC,IAAI,CACd,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,EACnD,oDAAoD,CACrD,CAAC;YACJ,CAAC;QACH,CAAC;QACD,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC;YAChC,gBAAgB,EAAE,KAAK,CAAC,WAAW;YACnC,MAAM,EAAE,2CAA2C;YACnD,UAAU,EAAE,kBAAkB;YAC9B,QAAQ,EAAE,QAAQ,CAAC,EAAE;YACrB,WAAW,EAAE,IAAI;YACjB,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;IAChC,CAAC,CACF,CAAC;IAEF,2EAA2E;IAE3E,GAAG,CAAC,MAAM,CACR,6BAA6B,EAC7B,EAAE,UAAU,EAAE,YAAY,CAAC,kBAAkB,CAAC,EAAE,EAChD,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,KAAK,GAAG,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACjF,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,SAAS,IAAI,IAAI,EAAE,EAAE,CAAC;IAC5E,CAAC,CACF,CAAC;IAEF,GAAG,CAAC,IAAI,CACN,qCAAqC,EACrC,EAAE,UAAU,EAAE,YAAY,CAAC,kBAAkB,CAAC,EAAE,EAChD,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,KAAK,GAAG,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAC9E,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,SAAS,IAAI,IAAI,EAAE,EAAE,CAAC;IAC5E,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import type { CustomerRegistrationService } from './services/customer-registration-service.js';
|
|
3
|
+
/**
|
|
4
|
+
* Public standalone-registration route (feature 040, US1 / FR-002).
|
|
5
|
+
* Gated inside the service by the
|
|
6
|
+
* `customers.allow_registration_without_organization` setting.
|
|
7
|
+
*/
|
|
8
|
+
export interface CustomersRegisterDeps {
|
|
9
|
+
registrationService: CustomerRegistrationService;
|
|
10
|
+
}
|
|
11
|
+
export declare function registerCustomersRegisterRoutes(app: FastifyInstance, deps: CustomersRegisterDeps): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=routes.register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.register.d.ts","sourceRoot":"","sources":["../../src/backend/routes.register.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAgB,MAAM,SAAS,CAAC;AAE7D,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,6CAA6C,CAAC;AAE/F;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,mBAAmB,EAAE,2BAA2B,CAAC;CAClD;AAiBD,wBAAsB,+BAA+B,CACnD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,IAAI,CAAC,CA+Bf"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { SESSION_COOKIE_NAME, customerRegisterRequestSchema } from '@endora-commerce/contracts';
|
|
2
|
+
function setSessionCookie(reply, value, expiresAt) {
|
|
3
|
+
reply.setCookie(SESSION_COOKIE_NAME, value, {
|
|
4
|
+
path: '/',
|
|
5
|
+
httpOnly: true,
|
|
6
|
+
sameSite: 'lax',
|
|
7
|
+
secure: process.env['NODE_ENV'] === 'production',
|
|
8
|
+
expires: expiresAt,
|
|
9
|
+
signed: false,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export async function registerCustomersRegisterRoutes(app, deps) {
|
|
13
|
+
app.post('/api/v1/customers/register', { schema: { body: customerRegisterRequestSchema } }, async (request, reply) => {
|
|
14
|
+
const body = customerRegisterRequestSchema.parse(request.body);
|
|
15
|
+
const result = await deps.registrationService.registerStandalone({
|
|
16
|
+
email: body.email,
|
|
17
|
+
password: body.password,
|
|
18
|
+
firstName: body.firstName,
|
|
19
|
+
lastName: body.lastName,
|
|
20
|
+
ip: request.ip,
|
|
21
|
+
...(typeof request.headers['user-agent'] === 'string'
|
|
22
|
+
? { userAgent: request.headers['user-agent'] }
|
|
23
|
+
: {}),
|
|
24
|
+
});
|
|
25
|
+
setSessionCookie(reply, result.sessionCookieValue, result.sessionExpiresAt);
|
|
26
|
+
reply.code(201);
|
|
27
|
+
return {
|
|
28
|
+
data: {
|
|
29
|
+
customerAccount: {
|
|
30
|
+
id: result.customerAccount.id,
|
|
31
|
+
email: result.customerAccount.email,
|
|
32
|
+
firstName: result.customerAccount.firstName,
|
|
33
|
+
lastName: result.customerAccount.lastName,
|
|
34
|
+
organizationId: result.customerAccount.organizationId ?? null,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=routes.register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.register.js","sourceRoot":"","sources":["../../src/backend/routes.register.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,mBAAmB,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAC;AAYhG,SAAS,gBAAgB,CACvB,KAAmB,EACnB,KAAa,EACb,SAAe;IAEf,KAAK,CAAC,SAAS,CAAC,mBAAmB,EAAE,KAAK,EAAE;QAC1C,IAAI,EAAE,GAAG;QACT,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,KAAK;QACf,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,YAAY;QAChD,OAAO,EAAE,SAAS;QAClB,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,+BAA+B,CACnD,GAAoB,EACpB,IAA2B;IAE3B,GAAG,CAAC,IAAI,CACN,4BAA4B,EAC5B,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,6BAA6B,EAAE,EAAE,EACnD,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,IAAI,GAAG,6BAA6B,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,kBAAkB,CAAC;YAC/D,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,GAAG,CAAC,OAAO,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,QAAQ;gBACnD,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE;gBAC9C,CAAC,CAAC,EAAE,CAAC;SACR,CAAC,CAAC;QACH,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,kBAAkB,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC;QAC5E,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChB,OAAO;YACL,IAAI,EAAE;gBACJ,eAAe,EAAE;oBACf,EAAE,EAAE,MAAM,CAAC,eAAe,CAAC,EAAE;oBAC7B,KAAK,EAAE,MAAM,CAAC,eAAe,CAAC,KAAK;oBACnC,SAAS,EAAE,MAAM,CAAC,eAAe,CAAC,SAAS;oBAC3C,QAAQ,EAAE,MAAM,CAAC,eAAe,CAAC,QAAQ;oBACzC,cAAc,EAAE,MAAM,CAAC,eAAe,CAAC,cAAc,IAAI,IAAI;iBAC9D;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
|
|
2
|
+
import type { CustomerAccountReadPort, CustomerAuthPort, OrderListPort, RfqCustomerPort } from '@endora-commerce/contracts';
|
|
3
|
+
import type { CustomerAddressService } from './services/customer-address-service.js';
|
|
4
|
+
import type { CustomerDefaultsService } from './services/customer-defaults-service.js';
|
|
5
|
+
/**
|
|
6
|
+
* Customer self-service routes (feature 040, US1). Each endpoint is scoped to
|
|
7
|
+
* the authenticated customer's own data (FR-011); org-only capabilities are
|
|
8
|
+
* simply absent for org-less customers (FR-003).
|
|
9
|
+
*/
|
|
10
|
+
export type RequireCustomerGuard = (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
11
|
+
export interface CustomerActorView {
|
|
12
|
+
customerAccountId: string;
|
|
13
|
+
organizationId: string | null;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Resolves the authenticated customer WITHOUT requiring an Organization —
|
|
17
|
+
* org-less customers are first-class here (FR-003). Injected by the host so
|
|
18
|
+
* the actor source (production `request.actor` vs test `request.testActor`)
|
|
19
|
+
* stays out of the route logic.
|
|
20
|
+
*/
|
|
21
|
+
export type ResolveCustomerActor = (request: FastifyRequest) => CustomerActorView;
|
|
22
|
+
export interface CustomersSelfDeps {
|
|
23
|
+
/**
|
|
24
|
+
* Feature 075 — `customer_accounts`' published read. The own-profile
|
|
25
|
+
* endpoint used to load that module's entity through an `EntityManager` this
|
|
26
|
+
* route file no longer needs at all.
|
|
27
|
+
*/
|
|
28
|
+
accounts: CustomerAccountReadPort;
|
|
29
|
+
requireCustomer: RequireCustomerGuard;
|
|
30
|
+
resolveCustomerActor: ResolveCustomerActor;
|
|
31
|
+
customerAuthService: CustomerAuthPort;
|
|
32
|
+
/**
|
|
33
|
+
* `orders`' published list (feature 075). It used to be a lazy getter over
|
|
34
|
+
* `Pick<OrderListService, 'list'>` — a type operator in front of a
|
|
35
|
+
* cross-module import of the class. The port keeps the getter's timing: the
|
|
36
|
+
* service is built inside the `orders` plugin body, so a call made before
|
|
37
|
+
* route registration answers 503 rather than a `null` this file would have
|
|
38
|
+
* to remember to check.
|
|
39
|
+
*/
|
|
40
|
+
orderList: OrderListPort;
|
|
41
|
+
rfqService: RfqCustomerPort;
|
|
42
|
+
customerAddressService: CustomerAddressService;
|
|
43
|
+
customerDefaultsService: CustomerDefaultsService;
|
|
44
|
+
}
|
|
45
|
+
export declare function registerCustomersSelfRoutes(app: FastifyInstance, deps: CustomersSelfDeps): Promise<void>;
|
|
46
|
+
//# sourceMappingURL=routes.self.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.self.d.ts","sourceRoot":"","sources":["../../src/backend/routes.self.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,KAAK,EACV,uBAAuB,EACvB,gBAAgB,EAChB,aAAa,EACb,eAAe,EAChB,MAAM,4BAA4B,CAAC;AAQpC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAMvF;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,CACjC,GAAG,EAAE,cAAc,EACnB,KAAK,EAAE,YAAY,KAChB,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,CACjC,OAAO,EAAE,cAAc,KACpB,iBAAiB,CAAC;AAEvB,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,QAAQ,EAAE,uBAAuB,CAAC;IAClC,eAAe,EAAE,oBAAoB,CAAC;IACtC,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,mBAAmB,EAAE,gBAAgB,CAAC;IACtC;;;;;;;OAOG;IACH,SAAS,EAAE,aAAa,CAAC;IACzB,UAAU,EAAE,eAAe,CAAC;IAC5B,sBAAsB,EAAE,sBAAsB,CAAC;IAC/C,uBAAuB,EAAE,uBAAuB,CAAC;CAClD;AAED,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,IAAI,CAAC,CA4Nf"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { changePasswordRequestSchema, customerAddressInputSchema, updateCustomerDefaultsRequestSchema, ERROR_CODES, } from '@endora-commerce/contracts';
|
|
2
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
3
|
+
import { serializeCustomerAddress, serializeOrganizationAddress, } from './serializers.js';
|
|
4
|
+
export async function registerCustomersSelfRoutes(app, deps) {
|
|
5
|
+
const { accounts, requireCustomer, customerAuthService, resolveCustomerActor } = deps;
|
|
6
|
+
const { orderList, rfqService } = deps;
|
|
7
|
+
const { customerAddressService, customerDefaultsService } = deps;
|
|
8
|
+
// GET /api/v1/me/customer — own profile
|
|
9
|
+
app.get('/api/v1/me/customer', { preHandler: requireCustomer }, async (request) => {
|
|
10
|
+
const actor = resolveCustomerActor(request);
|
|
11
|
+
const customer = await accounts.findById(actor.customerAccountId);
|
|
12
|
+
if (!customer) {
|
|
13
|
+
throw new HttpError(404, ERROR_CODES.CUSTOMER_NOT_FOUND, 'Customer not found.');
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
data: {
|
|
17
|
+
id: customer.id,
|
|
18
|
+
email: customer.email,
|
|
19
|
+
firstName: customer.firstName,
|
|
20
|
+
lastName: customer.lastName,
|
|
21
|
+
organizationId: customer.organizationId ?? null,
|
|
22
|
+
customerGroupId: customer.customerGroupId ?? null,
|
|
23
|
+
twoFactorEnabled: customer.twoFactorEnabled,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
// POST /api/v1/me/customer/change-password
|
|
28
|
+
app.post('/api/v1/me/customer/change-password', { preHandler: requireCustomer, schema: { body: changePasswordRequestSchema } }, async (request, reply) => {
|
|
29
|
+
const actor = resolveCustomerActor(request);
|
|
30
|
+
const body = changePasswordRequestSchema.parse(request.body);
|
|
31
|
+
await customerAuthService.changePassword(actor.customerAccountId, body.currentPassword, body.newPassword);
|
|
32
|
+
reply.code(204);
|
|
33
|
+
return null;
|
|
34
|
+
});
|
|
35
|
+
// GET /api/v1/me/customer/orders — own orders, aggregated across channels
|
|
36
|
+
app.get('/api/v1/me/customer/orders', { preHandler: requireCustomer }, async (request) => {
|
|
37
|
+
const actor = resolveCustomerActor(request);
|
|
38
|
+
const page = Math.max(1, Number.parseInt(request.query.page ?? '1', 10) || 1);
|
|
39
|
+
const pageSize = Math.min(100, Math.max(1, Number.parseInt(request.query.pageSize ?? '20', 10) || 20));
|
|
40
|
+
const result = await orderList.list({
|
|
41
|
+
placedByCustomerAccountId: actor.customerAccountId,
|
|
42
|
+
page,
|
|
43
|
+
pageSize,
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
data: result.rows,
|
|
47
|
+
meta: { page, pageSize, total: result.total },
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
// GET /api/v1/me/customer/quote-requests — own RFQs
|
|
51
|
+
app.get('/api/v1/me/customer/quote-requests', { preHandler: requireCustomer }, async (request) => {
|
|
52
|
+
const actor = resolveCustomerActor(request);
|
|
53
|
+
// Unreachable since D-178: every signed-in customer carries an
|
|
54
|
+
// Organization (`customer_accounts.organization_id` is `NOT NULL`), so
|
|
55
|
+
// this is a type guard over an optional actor field and not a supported
|
|
56
|
+
// "org-less customer" mode.
|
|
57
|
+
if (actor.organizationId === null) {
|
|
58
|
+
return { data: [] };
|
|
59
|
+
}
|
|
60
|
+
const data = await rfqService.listForCustomer({
|
|
61
|
+
customerAccountId: actor.customerAccountId,
|
|
62
|
+
organizationId: actor.organizationId,
|
|
63
|
+
isOrgAdmin: false,
|
|
64
|
+
});
|
|
65
|
+
return { data };
|
|
66
|
+
});
|
|
67
|
+
// ── Address book (US2) ──────────────────────────────────────────────────
|
|
68
|
+
// GET /api/v1/me/customer/addresses — personal + org-shared
|
|
69
|
+
app.get('/api/v1/me/customer/addresses', { preHandler: requireCustomer }, async (request) => {
|
|
70
|
+
const actor = resolveCustomerActor(request);
|
|
71
|
+
const personal = await customerAddressService.listPersonal(actor.customerAccountId);
|
|
72
|
+
// As above — unreachable since D-178, kept as a type guard.
|
|
73
|
+
const organization = actor.organizationId === null
|
|
74
|
+
? []
|
|
75
|
+
: await customerAddressService.listOrganizationAddresses(actor.organizationId);
|
|
76
|
+
return {
|
|
77
|
+
data: {
|
|
78
|
+
personal: personal.map(serializeCustomerAddress),
|
|
79
|
+
organization: organization.map(serializeOrganizationAddress),
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
// POST /api/v1/me/customer/addresses
|
|
84
|
+
app.post('/api/v1/me/customer/addresses', { preHandler: requireCustomer, schema: { body: customerAddressInputSchema } }, async (request, reply) => {
|
|
85
|
+
const actor = resolveCustomerActor(request);
|
|
86
|
+
const body = customerAddressInputSchema.parse(request.body);
|
|
87
|
+
const created = await customerAddressService.create(actor.customerAccountId, {
|
|
88
|
+
kind: body.kind,
|
|
89
|
+
recipientName: body.recipientName,
|
|
90
|
+
street: body.street,
|
|
91
|
+
city: body.city,
|
|
92
|
+
postalCode: body.postalCode,
|
|
93
|
+
country: body.country,
|
|
94
|
+
phone: body.phone,
|
|
95
|
+
isDefault: body.isDefault,
|
|
96
|
+
});
|
|
97
|
+
reply.code(201);
|
|
98
|
+
return { data: serializeCustomerAddress(created) };
|
|
99
|
+
});
|
|
100
|
+
// PATCH /api/v1/me/customer/addresses/:addressId
|
|
101
|
+
app.patch('/api/v1/me/customer/addresses/:addressId', { preHandler: requireCustomer, schema: { body: customerAddressInputSchema.partial() } }, async (request) => {
|
|
102
|
+
const actor = resolveCustomerActor(request);
|
|
103
|
+
const body = customerAddressInputSchema.partial().parse(request.body);
|
|
104
|
+
const updated = await customerAddressService.update(actor.customerAccountId, request.params.addressId, body);
|
|
105
|
+
return { data: serializeCustomerAddress(updated) };
|
|
106
|
+
});
|
|
107
|
+
// PUT /api/v1/me/customer/addresses/:addressId/default
|
|
108
|
+
app.put('/api/v1/me/customer/addresses/:addressId/default', { preHandler: requireCustomer }, async (request) => {
|
|
109
|
+
const actor = resolveCustomerActor(request);
|
|
110
|
+
const updated = await customerAddressService.setDefault(actor.customerAccountId, request.params.addressId);
|
|
111
|
+
return { data: serializeCustomerAddress(updated) };
|
|
112
|
+
});
|
|
113
|
+
// DELETE /api/v1/me/customer/addresses/:addressId
|
|
114
|
+
app.delete('/api/v1/me/customer/addresses/:addressId', { preHandler: requireCustomer }, async (request, reply) => {
|
|
115
|
+
const actor = resolveCustomerActor(request);
|
|
116
|
+
await customerAddressService.delete(actor.customerAccountId, request.params.addressId);
|
|
117
|
+
reply.code(204);
|
|
118
|
+
return null;
|
|
119
|
+
});
|
|
120
|
+
// ── Default payment / delivery method + addresses (US2) ──────────────────
|
|
121
|
+
// GET /api/v1/me/customer/defaults
|
|
122
|
+
app.get('/api/v1/me/customer/defaults', { preHandler: requireCustomer }, async (request) => {
|
|
123
|
+
const actor = resolveCustomerActor(request);
|
|
124
|
+
return { data: await customerDefaultsService.getForCustomer(actor.customerAccountId) };
|
|
125
|
+
});
|
|
126
|
+
// PUT /api/v1/me/customer/defaults
|
|
127
|
+
app.put('/api/v1/me/customer/defaults', { preHandler: requireCustomer, schema: { body: updateCustomerDefaultsRequestSchema } }, async (request) => {
|
|
128
|
+
const actor = resolveCustomerActor(request);
|
|
129
|
+
const body = updateCustomerDefaultsRequestSchema.parse(request.body);
|
|
130
|
+
const updated = await customerDefaultsService.setForCustomer(actor.customerAccountId, {
|
|
131
|
+
paymentMethodId: body.paymentMethodId,
|
|
132
|
+
deliveryMethodId: body.deliveryMethodId,
|
|
133
|
+
billingAddressId: body.billingAddressId,
|
|
134
|
+
shippingAddressId: body.shippingAddressId,
|
|
135
|
+
}, {
|
|
136
|
+
customerAccountId: actor.customerAccountId,
|
|
137
|
+
...(typeof request.headers['user-agent'] === 'string'
|
|
138
|
+
? { userAgent: request.headers['user-agent'] }
|
|
139
|
+
: {}),
|
|
140
|
+
ipAddress: request.ip,
|
|
141
|
+
});
|
|
142
|
+
return { data: updated };
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=routes.self.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.self.js","sourceRoot":"","sources":["../../src/backend/routes.self.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AddressRecord, CustomerAddress as CustomerAddressDTO, OrganizationAddressRef } from '@endora-commerce/contracts';
|
|
2
|
+
import type { CustomerAddress } from './entities/customer-address.entity.js';
|
|
3
|
+
/** Maps a persisted CustomerAddress to its API contract shape. */
|
|
4
|
+
export declare function serializeCustomerAddress(a: CustomerAddress): CustomerAddressDTO;
|
|
5
|
+
/**
|
|
6
|
+
* Maps an org-shared address to the read-only reference shape.
|
|
7
|
+
*
|
|
8
|
+
* Feature 075 — the argument is `addresses`' published {@link AddressRecord},
|
|
9
|
+
* not its `Address` entity: this module only ever read the columns the record
|
|
10
|
+
* carries, and an entity crossing a boundary is what the port exists to stop.
|
|
11
|
+
*/
|
|
12
|
+
export declare function serializeOrganizationAddress(a: AddressRecord): OrganizationAddressRef;
|
|
13
|
+
//# sourceMappingURL=serializers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serializers.d.ts","sourceRoot":"","sources":["../../src/backend/serializers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,eAAe,IAAI,kBAAkB,EACrC,sBAAsB,EACvB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAE7E,kEAAkE;AAClE,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,eAAe,GAAG,kBAAkB,CAe/E;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,CAAC,EAAE,aAAa,GAAG,sBAAsB,CAarF"}
|