@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,201 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ERROR_CODES } from '@endora-commerce/contracts';
|
|
3
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
4
|
+
import { CustomerAddressReadService } from './services/customer-address-read-port.js';
|
|
5
|
+
import { lazyPort } from '@endora-commerce/platform/kernel';
|
|
6
|
+
import { CUSTOMERS_SETTING_CODES } from '../manifest.js';
|
|
7
|
+
import { customersModule } from './plugin.js';
|
|
8
|
+
import { CustomerAddress } from './entities/customer-address.entity.js';
|
|
9
|
+
/**
|
|
10
|
+
* Who is moderating, and which organizations they may act on — defaulted here
|
|
11
|
+
* (`specs/117-instance-bring-up/` Phase 6, FR-033).
|
|
12
|
+
*
|
|
13
|
+
* It was contributed by `backend/src/composition.ts` and by
|
|
14
|
+
* `backend/test/helpers/test-server.ts` and by nothing else, so a composition
|
|
15
|
+
* that is neither — which is every client instance — could not resolve it, and
|
|
16
|
+
* met that fact as a 500 on the first admin customer screen rather than at
|
|
17
|
+
* boot.
|
|
18
|
+
*
|
|
19
|
+
* The production root wrote the role lookup as `knex.raw` over `admin_users`
|
|
20
|
+
* and `admin_roles`, which is two modules' tables read on this module's
|
|
21
|
+
* `EntityManager`, outside either owner's gate. The two ports say the same
|
|
22
|
+
* thing and are already this manifest's declared `dependencies`; the test
|
|
23
|
+
* harness had been written this way since T140, so the port shape is the one
|
|
24
|
+
* with the prior art and the SQL is what goes.
|
|
25
|
+
*/
|
|
26
|
+
function registerModerationActor(ctx) {
|
|
27
|
+
ctx.di.register({
|
|
28
|
+
customerModerationActorResolver: ctx
|
|
29
|
+
.asFunction(() => {
|
|
30
|
+
const admins = lazyPort(ctx, 'adminUserReadPort');
|
|
31
|
+
const roles = lazyPort(ctx, 'adminRolePort');
|
|
32
|
+
const salesRepScope = lazyPort(ctx, 'organizationSalesRepScopePort');
|
|
33
|
+
return async (request) => {
|
|
34
|
+
const actor = request.actor;
|
|
35
|
+
if (actor?.kind !== 'admin') {
|
|
36
|
+
throw new HttpError(401, ERROR_CODES.UNAUTHORIZED, 'Admin session required.');
|
|
37
|
+
}
|
|
38
|
+
const adminUser = await admins.findById(actor.adminUserId);
|
|
39
|
+
// `getById` cannot 404 here: `admin_users_admin_role_fk` is
|
|
40
|
+
// `on delete restrict`, so a non-null `adminRoleId` names a row.
|
|
41
|
+
const role = adminUser?.adminRoleId ? await roles.getById(adminUser.adminRoleId) : null;
|
|
42
|
+
const isPlatformAdmin = role?.code !== 'sales_representative';
|
|
43
|
+
// Feature 056 — subtree-expanded when the rep holds
|
|
44
|
+
// `organizations:rollup`, which is `organizations`' decision and the
|
|
45
|
+
// reason this reads an assembled port rather than the tree.
|
|
46
|
+
const allowedOrganizationIds = isPlatformAdmin
|
|
47
|
+
? []
|
|
48
|
+
: await salesRepScope.listAssignedOrganizationIds(actor.adminUserId);
|
|
49
|
+
return { adminUserId: actor.adminUserId, isPlatformAdmin, allowedOrganizationIds };
|
|
50
|
+
};
|
|
51
|
+
})
|
|
52
|
+
.singleton(),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
export function registerModule(ctx) {
|
|
56
|
+
const cradle = () => ctx.cradle();
|
|
57
|
+
registerModerationActor(ctx);
|
|
58
|
+
/**
|
|
59
|
+
* This module's own settings, read against the system-default channel. Each
|
|
60
|
+
* degrades to the manifest default rather than failing the request — the
|
|
61
|
+
* identical try/catch stood in the production root, three times over.
|
|
62
|
+
*
|
|
63
|
+
* D-48 removed an `if (!channel) return fallback;` from between the two: on
|
|
64
|
+
* the impossible no-channel branch it returned the compiled-in constant
|
|
65
|
+
* without reading the setting at all, which is worse than switching tier.
|
|
66
|
+
*/
|
|
67
|
+
const readChannelSetting = async (code, schema, fallback) => {
|
|
68
|
+
try {
|
|
69
|
+
const channel = await cradle().salesChannelResolutionPort.getSystemDefault();
|
|
70
|
+
return (await cradle().settingsReadPort.get(code, channel.id, schema));
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return fallback;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Feature 075, Phase P — the customer's own saved addresses.
|
|
78
|
+
*
|
|
79
|
+
* `quick_order` reads one when it fills in a buyer's one-click defaults. The
|
|
80
|
+
* table is distinct from `addresses`' on purpose and the port keeps them
|
|
81
|
+
* distinct: this one hangs off a customer account, that one off an
|
|
82
|
+
* organisation, and a B2C buyer keeps addresses that are theirs rather than
|
|
83
|
+
* their personal organisation's.
|
|
84
|
+
*/
|
|
85
|
+
ctx.di.providePort('customerAddressReadPort', ctx
|
|
86
|
+
.asFunction(({ emFactory }) => new CustomerAddressReadService(emFactory))
|
|
87
|
+
.singleton());
|
|
88
|
+
ctx.di.register({
|
|
89
|
+
customers: ctx
|
|
90
|
+
.asFunction(({ emFactory, auditLogService }) => customersModule({
|
|
91
|
+
emFactory,
|
|
92
|
+
auditLogService,
|
|
93
|
+
// Every one of these is another module's gated port and is stored by
|
|
94
|
+
// the constructor, so a singleton may not hold one directly.
|
|
95
|
+
// Feature 075, Phase C (issue #195) — `customerAuthPort` rather
|
|
96
|
+
// than the `customerAuthService` class registration. Unlike the
|
|
97
|
+
// address pair this was never a record type over a class: the
|
|
98
|
+
// cradle field resolved to a direct class-type import, an ordinary
|
|
99
|
+
// FR-011 edge that happened to share the container name.
|
|
100
|
+
customerAuthService: lazyPort(ctx, 'customerAuthPort'),
|
|
101
|
+
passwordResetService: lazyPort(ctx, 'passwordResetService'),
|
|
102
|
+
// D-98.2 / issue #196 — `authSessionPort` is the name the contract
|
|
103
|
+
// publishes; `sessionService` is `auth`'s class registration, and
|
|
104
|
+
// resolving it here was the leak D-98.1 repaired for `addressService`.
|
|
105
|
+
sessionService: lazyPort(ctx, 'authSessionPort'),
|
|
106
|
+
customerAccountReadPort: lazyPort(ctx, 'customerAccountReadPort'),
|
|
107
|
+
// Feature 075, Phase C — the rest of `customer_accounts`' surface
|
|
108
|
+
// this module's screens run on. Every one of these replaced a
|
|
109
|
+
// direct load or mutation of that module's `CustomerAccount`
|
|
110
|
+
// entity: the block/unblock/delete/restore/assign writes, the
|
|
111
|
+
// retention scrub, the admin list's paginated query, and the
|
|
112
|
+
// customer-group names beside it.
|
|
113
|
+
customerAccountLifecycleWritePort: lazyPort(ctx, 'customerAccountLifecycleWritePort'),
|
|
114
|
+
customerAccountAdminSearchPort: lazyPort(ctx, 'customerAccountAdminSearchPort'),
|
|
115
|
+
customerGroupReadPort: lazyPort(ctx, 'customerGroupReadPort'),
|
|
116
|
+
// `organizations`' row-level read (names on the admin list, the
|
|
117
|
+
// existence check on the assignment screen) and its personal-org
|
|
118
|
+
// provisioner, which the B2C registration and the retention sweep
|
|
119
|
+
// both go through. Both replaced an `em.findOne(Organization, …)`
|
|
120
|
+
// — and the sweep's three-field scrub of that row — here.
|
|
121
|
+
organizationDetailsPort: lazyPort(ctx, 'organizationDetailsPort'),
|
|
122
|
+
personalOrganizationPort: lazyPort(ctx, 'personalOrganizationPort'),
|
|
123
|
+
// `addresses`' read: the org-shared book a customer may pick a
|
|
124
|
+
// delivery address from, which this module used to query directly.
|
|
125
|
+
addressReadPort: lazyPort(ctx, 'addressReadPort'),
|
|
126
|
+
customFieldValues: lazyPort(ctx, 'customFieldValueService'),
|
|
127
|
+
rfqService: lazyPort(ctx, 'rfqService'),
|
|
128
|
+
// Issue #108 — `organizations`' scope, not a private copy of its
|
|
129
|
+
// wiring. The copy this replaces omitted the optional subtree deps,
|
|
130
|
+
// so feature 056's roll-up was skipped for every block / unblock /
|
|
131
|
+
// delete / org-assign decision, and `tsc` had nothing to say about
|
|
132
|
+
// it.
|
|
133
|
+
salesRepVisibility: lazyPort(ctx, 'organizationSalesRepScopePort'),
|
|
134
|
+
mailer: lazyPort(ctx, 'emailMailer'),
|
|
135
|
+
// Feature 075 — `carts`' reporting read and `admin_users`'
|
|
136
|
+
// impersonation seam, where this module built a second instance of
|
|
137
|
+
// each from an import of the owner's directory. Both are gated
|
|
138
|
+
// ports, so a singleton may not hold one directly.
|
|
139
|
+
cartQueryPort: lazyPort(ctx, 'cartQueryPort'),
|
|
140
|
+
impersonationPort: lazyPort(ctx, 'impersonationPort'),
|
|
141
|
+
// `auth`'s read over the session table, for the online-customers
|
|
142
|
+
// panel — the last thing in this module that named `Session`.
|
|
143
|
+
authSessionReadPort: lazyPort(ctx, 'authSessionReadPort'),
|
|
144
|
+
// `orders`' published list, replacing the late-bound accessor and
|
|
145
|
+
// the `Pick<OrderListService, 'list'>` that typed it. `orders` is
|
|
146
|
+
// non-deactivatable, so the edge is an ordinary binding dependency
|
|
147
|
+
// and the panels have no absent state to degrade into: the port's
|
|
148
|
+
// own 503 covers the window before `orders` registers its routes.
|
|
149
|
+
orderList: lazyPort(ctx, 'orderListPort'),
|
|
150
|
+
// Feature 080, T048 (D-169) — `orders`' row-level read, for the
|
|
151
|
+
// customer-detail header's sales-channel list. That list was an
|
|
152
|
+
// `em.find(Order, …)` inside this module: a plain read of another
|
|
153
|
+
// module's table, which kept answering out of an `orders` the
|
|
154
|
+
// platform was not serving. A read takes a read-port method, never
|
|
155
|
+
// an `EntityManager`-taking apply port. Same binding edge as
|
|
156
|
+
// `orderListPort` above and the same manifest `dependencies` entry.
|
|
157
|
+
orderReadPort: lazyPort(ctx, 'orderReadPort'),
|
|
158
|
+
// Feature 076 (D-86) — `organizations`' port, resolved lazily: the
|
|
159
|
+
// factory below is a singleton and stores what it is handed, and a
|
|
160
|
+
// captured gate keeps answering after its owner is switched off.
|
|
161
|
+
vatValidator: lazyPort(ctx, 'vatValidatorPort'),
|
|
162
|
+
// Issue #216 — the ordering defaults this module renders and edits
|
|
163
|
+
// belong to `quick_order`. Until now it built a **second instance**
|
|
164
|
+
// of that module's `DefaultPreferenceService` from an import of its
|
|
165
|
+
// directory, so the two holders had to be converted together and
|
|
166
|
+
// the port published for this consumer went unreached. Binding, per
|
|
167
|
+
// the port's own contract: a switched-off `quick_order` answers 503
|
|
168
|
+
// `MODULE_DISABLED` at the seam rather than half a set of defaults.
|
|
169
|
+
defaultPreferencePort: lazyPort(ctx, 'defaultPreferencePort'),
|
|
170
|
+
storefrontBaseUrl: cradle().storefrontBaseUrl,
|
|
171
|
+
requireAdmin: (permission) => async (req, reply) => cradle().requireAdmin(permission)(req, reply),
|
|
172
|
+
requireCustomer: (req, reply) => cradle().requireCustomer(req, reply),
|
|
173
|
+
resolveCustomerActor: (req) => cradle().customerActorResolver(req),
|
|
174
|
+
resolveModerationActor: (req) => cradle().customerModerationActorResolver(req),
|
|
175
|
+
// The three reads that used to be a root's, one of them exercised
|
|
176
|
+
// by no test at all — see the note above.
|
|
177
|
+
resolveAllowRegistrationWithoutOrganization: () => readChannelSetting(CUSTOMERS_SETTING_CODES.ALLOW_REGISTRATION_WITHOUT_ORGANIZATION, z.boolean(), false),
|
|
178
|
+
resolveDeletionRetentionDays: () => readChannelSetting(CUSTOMERS_SETTING_CODES.DELETION_RETENTION_DAYS, z.number(), 365),
|
|
179
|
+
resolvePresenceFreshnessMinutes: () => readChannelSetting(CUSTOMERS_SETTING_CODES.PRESENCE_FRESHNESS_MINUTES, z.number(), 10),
|
|
180
|
+
}))
|
|
181
|
+
.singleton(),
|
|
182
|
+
});
|
|
183
|
+
ctx.routes(async (app) => {
|
|
184
|
+
await cradle().customers.plugin(app);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* The module's persisted entity classes, on the `./backend` subpath, as one
|
|
189
|
+
* array and **no named class export** (D-168).
|
|
190
|
+
*
|
|
191
|
+
* This is the shape the platform reads when the package is *installed*: the
|
|
192
|
+
* boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
|
|
193
|
+
* and the static declaration reader (`scripts/lib/package-declarations.ts`),
|
|
194
|
+
* which is the third source of `check:module-boundary`'s `table→owner` map and
|
|
195
|
+
* the package pass of `check-entity-tenant-classification`. A missing array is
|
|
196
|
+
* answered with `[]` — zero entities registered, no error anywhere.
|
|
197
|
+
*/
|
|
198
|
+
export const entities = [
|
|
199
|
+
CustomerAddress,
|
|
200
|
+
];
|
|
201
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
3
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
4
|
+
import { CustomerRegistrationService } from './services/customer-registration-service.js';
|
|
5
|
+
import { CustomerAuthorityService, type SalesRepVisibility } from './services/customer-authority-service.js';
|
|
6
|
+
import { AnonymizationSweepWorker } from './workers/anonymization-sweep-worker.js';
|
|
7
|
+
import type { AddressReadPort, AuthSessionPort, AuthSessionReadPort, CartQueryPort, CustomFieldValuePort, CustomerAccountAdminSearchPort, CustomerAccountLifecycleWritePort, CustomerAccountReadPort, CustomerAuthPort, CustomerGroupReadPort, CustomerPasswordResetPort, DefaultPreferencePort, EmailMailerPort, ImpersonationPort, OrderListPort, OrderReadPort, OrganizationDetailsPort, PersonalOrganizationPort, RfqCustomerPort, VatValidator } from '@endora-commerce/contracts';
|
|
8
|
+
import { type RequireCustomerGuard, type ResolveCustomerActor } from './routes.self.js';
|
|
9
|
+
import { type RequireAdminGuard, type ResolveModerationActor } from './routes.admin.js';
|
|
10
|
+
/**
|
|
11
|
+
* The attach function this module hands its composition root.
|
|
12
|
+
*
|
|
13
|
+
* Typed on `fastify`'s own `FastifyInstance` rather than on the platform's
|
|
14
|
+
* `ModulePlugin`, which `contracts/host-package.md` §1.4g classifies **A**: the
|
|
15
|
+
* host does not publish it, so a packaged module cannot name it. The
|
|
16
|
+
* already-packaged `quote_requests` types its attach function the same way.
|
|
17
|
+
*/
|
|
18
|
+
type ModuleAttach = (app: FastifyInstance) => Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Customers module composition root (feature 040).
|
|
21
|
+
*
|
|
22
|
+
* Orchestrates the customer-lifecycle surfaces over the existing data
|
|
23
|
+
* modules. Sibling services are injected from `composition.ts` so the module
|
|
24
|
+
* never imports another module's internals (Constitution Principle I).
|
|
25
|
+
*/
|
|
26
|
+
export interface CustomersModuleOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Feature 072 (T094) — `customer_accounts` owns these now. Injected rather
|
|
29
|
+
* than built here, because this host and `organizations` each built their own and the
|
|
30
|
+
* MFA argument differed between them.
|
|
31
|
+
*/
|
|
32
|
+
customerAuthService: CustomerAuthPort;
|
|
33
|
+
passwordResetService: CustomerPasswordResetPort;
|
|
34
|
+
emFactory: () => EntityManager;
|
|
35
|
+
/**
|
|
36
|
+
* `auth`'s published session port, resolved under the name the contract
|
|
37
|
+
* publishes (D-98.2 / issue #196). It used to be the `SessionService`
|
|
38
|
+
* class, resolved as `sessionService`: the three calls this host makes —
|
|
39
|
+
* `createSession`, `destroyAllForCustomer`, `listRecentlyActiveCustomers`
|
|
40
|
+
* — are all on {@link AuthSessionPort}, so nothing needed the class.
|
|
41
|
+
*
|
|
42
|
+
* It was one of two options over the same port until this module's Phase-C
|
|
43
|
+
* cut: the duplicate existed only to feed the `ImpersonationService` this
|
|
44
|
+
* host built itself, and that instance is now `admin_users`' port.
|
|
45
|
+
*/
|
|
46
|
+
sessionService: AuthSessionPort;
|
|
47
|
+
/**
|
|
48
|
+
* `admin_users`' published impersonation seam (feature 075). This host used
|
|
49
|
+
* to construct a **second** `ImpersonationService` from an import of that
|
|
50
|
+
* module's directory, over the port `admin_users`' own Phase-C cut published
|
|
51
|
+
* for it — so the machinery ran here whether its owner was present or not.
|
|
52
|
+
* The port is gated: a switched-off `admin_users` answers 503
|
|
53
|
+
* `MODULE_DISABLED` at the seam instead of minting a session.
|
|
54
|
+
*/
|
|
55
|
+
impersonationPort: ImpersonationPort;
|
|
56
|
+
/**
|
|
57
|
+
* `auth`'s published read over the session table, for the online-customers
|
|
58
|
+
* panel. The panel *reports* on sessions rather than managing them, which is
|
|
59
|
+
* why it is a separate port from {@link AuthSessionPort} — and why this
|
|
60
|
+
* module no longer needs `Session` to answer "when was this account last
|
|
61
|
+
* seen".
|
|
62
|
+
*/
|
|
63
|
+
authSessionReadPort: AuthSessionReadPort;
|
|
64
|
+
customerAccountReadPort: CustomerAccountReadPort;
|
|
65
|
+
/**
|
|
66
|
+
* Feature 075, Phase C — the rest of `customer_accounts`' published surface
|
|
67
|
+
* this module's own screens run on: the lifecycle writes it used to perform
|
|
68
|
+
* by mutating that module's entity, the admin list's query, and the group
|
|
69
|
+
* names beside it.
|
|
70
|
+
*/
|
|
71
|
+
customerAccountLifecycleWritePort: CustomerAccountLifecycleWritePort;
|
|
72
|
+
customerAccountAdminSearchPort: CustomerAccountAdminSearchPort;
|
|
73
|
+
customerGroupReadPort: CustomerGroupReadPort;
|
|
74
|
+
/**
|
|
75
|
+
* `organizations`' published surface: the row-level read the admin list and
|
|
76
|
+
* the assignment screen ask for names and existence with, and the personal
|
|
77
|
+
* organisation a B2C registration provisions — which is also what the
|
|
78
|
+
* retention sweep cascades onto.
|
|
79
|
+
*/
|
|
80
|
+
organizationDetailsPort: OrganizationDetailsPort;
|
|
81
|
+
personalOrganizationPort: PersonalOrganizationPort;
|
|
82
|
+
/** `addresses`' published read: the org-shared book a customer may pick from. */
|
|
83
|
+
addressReadPort: AddressReadPort;
|
|
84
|
+
/**
|
|
85
|
+
* Which organizations the acting staff member may see —
|
|
86
|
+
* `organizations`' `organizationSalesRepScopePort` (issue #108).
|
|
87
|
+
*
|
|
88
|
+
* Injected rather than built here. This module used to construct its own
|
|
89
|
+
* `SalesRepAssignmentService` from an entity-manager factory and an audit log,
|
|
90
|
+
* and that class's third argument — the feature-056 subtree deps — is
|
|
91
|
+
* optional, so omitting it compiled and quietly reverted every
|
|
92
|
+
* staff-authority decision to the flat pre-056 rule: a rep holding
|
|
93
|
+
* `organizations:rollup` could not act on a customer belonging to a
|
|
94
|
+
* descendant of an organization assigned to them.
|
|
95
|
+
*/
|
|
96
|
+
salesRepVisibility: SalesRepVisibility;
|
|
97
|
+
requireCustomer: RequireCustomerGuard;
|
|
98
|
+
resolveCustomerActor: ResolveCustomerActor;
|
|
99
|
+
/** Reads `customers.allow_registration_without_organization`. */
|
|
100
|
+
resolveAllowRegistrationWithoutOrganization: () => Promise<boolean>;
|
|
101
|
+
/**
|
|
102
|
+
* `orders`' published list, which the self-service history panel and the
|
|
103
|
+
* admin customer-detail orders panel both read (feature 075). It replaces
|
|
104
|
+
* `Pick<OrderListService, 'list'>` over a late-bound accessor: the port keeps
|
|
105
|
+
* the accessor's timing, because `orders` builds the service inside its
|
|
106
|
+
* plugin body, and answers a call made before that with a 503 rather than a
|
|
107
|
+
* `null` two route files had to remember to check.
|
|
108
|
+
*/
|
|
109
|
+
orderList: OrderListPort;
|
|
110
|
+
/**
|
|
111
|
+
* `orders`' row-level read. Only the customer-detail header's sales-channel
|
|
112
|
+
* list uses it, and that list was an `em.find(Order, …)` in this module until
|
|
113
|
+
* feature 080's T048 (D-169) — a plain read of another module's table, so a
|
|
114
|
+
* read-port method rather than an `EntityManager`-taking apply port.
|
|
115
|
+
*/
|
|
116
|
+
orderReadPort: OrderReadPort;
|
|
117
|
+
rfqService: RfqCustomerPort;
|
|
118
|
+
auditLogService: AuditPort;
|
|
119
|
+
/**
|
|
120
|
+
* `quick_order`'s ordering defaults, which the customer-detail screen and
|
|
121
|
+
* self-service profile render and edit (issue #216).
|
|
122
|
+
*
|
|
123
|
+
* A **binding** dependency: the port's own contract says the seam fails
|
|
124
|
+
* closed when `quick_order` is off, and this module used to answer the same
|
|
125
|
+
* question out of a second instance it constructed itself — which read the
|
|
126
|
+
* table whether the owner was present or not.
|
|
127
|
+
*/
|
|
128
|
+
defaultPreferencePort: DefaultPreferencePort;
|
|
129
|
+
requireAdmin: RequireAdminGuard;
|
|
130
|
+
resolveModerationActor: ResolveModerationActor;
|
|
131
|
+
/** VAT/NIP validator port (VIES / Biała lista in production). */
|
|
132
|
+
vatValidator: VatValidator;
|
|
133
|
+
mailer: EmailMailerPort;
|
|
134
|
+
/**
|
|
135
|
+
* `carts`' reporting read: the account's current cart and its abandoned ones,
|
|
136
|
+
* for the customer-detail panel. This host used to build a second
|
|
137
|
+
* `CartQueryService` from an import of that module's directory (feature 075).
|
|
138
|
+
*/
|
|
139
|
+
cartQueryPort: CartQueryPort;
|
|
140
|
+
/** Base URL for the storefront set-password link in reset emails. */
|
|
141
|
+
storefrontBaseUrl: string;
|
|
142
|
+
/** Reads `customers.deletion_retention_days`. */
|
|
143
|
+
resolveDeletionRetentionDays: () => Promise<number>;
|
|
144
|
+
/** Reads `customers.presence_freshness_minutes`. */
|
|
145
|
+
resolvePresenceFreshnessMinutes: () => Promise<number>;
|
|
146
|
+
/**
|
|
147
|
+
* Feature 055 — validates Customer custom-field values on the admin edit
|
|
148
|
+
* path. The persistence is `customer_accounts`': its lifecycle port runs the
|
|
149
|
+
* Command and takes this validator as the merge (feature 075).
|
|
150
|
+
*/
|
|
151
|
+
customFieldValues?: CustomFieldValuePort;
|
|
152
|
+
}
|
|
153
|
+
export interface CustomersModuleHandle {
|
|
154
|
+
registrationService: CustomerRegistrationService;
|
|
155
|
+
anonymizationSweepWorker: AnonymizationSweepWorker;
|
|
156
|
+
/**
|
|
157
|
+
* Exposed so the sales-rep scope this module *uses* can be asserted (issue
|
|
158
|
+
* #108). The defect it closes was invisible to `tsc` and to every route test,
|
|
159
|
+
* because a flat scope answers plausibly — just not with the roll-up.
|
|
160
|
+
*/
|
|
161
|
+
authorityService: CustomerAuthorityService;
|
|
162
|
+
}
|
|
163
|
+
export declare function customersModule(options: CustomersModuleOptions): {
|
|
164
|
+
plugin: ModuleAttach;
|
|
165
|
+
handle: () => CustomersModuleHandle;
|
|
166
|
+
};
|
|
167
|
+
export {};
|
|
168
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { CustomerRegistrationService } from './services/customer-registration-service.js';
|
|
2
|
+
import { CustomerAddressService } from './services/customer-address-service.js';
|
|
3
|
+
import { CustomerDefaultsService } from './services/customer-defaults-service.js';
|
|
4
|
+
import { CustomerAuthorityService, } from './services/customer-authority-service.js';
|
|
5
|
+
import { CustomerModerationService } from './services/customer-moderation-service.js';
|
|
6
|
+
import { CustomerAdminQueryService } from './services/customer-admin-query-service.js';
|
|
7
|
+
import { CustomerOrgAssignmentService } from './services/customer-org-assignment-service.js';
|
|
8
|
+
import { CustomerDeletionService } from './services/customer-deletion-service.js';
|
|
9
|
+
import { CustomerPresenceService } from './services/customer-presence-service.js';
|
|
10
|
+
import { AnonymizationSweepWorker } from './workers/anonymization-sweep-worker.js';
|
|
11
|
+
import { registerCustomersRegisterRoutes } from './routes.register.js';
|
|
12
|
+
import { registerCustomersSelfRoutes, } from './routes.self.js';
|
|
13
|
+
import { registerCustomersAdminRoutes, } from './routes.admin.js';
|
|
14
|
+
export function customersModule(options) {
|
|
15
|
+
const customerAuthService = options.customerAuthService;
|
|
16
|
+
const registrationService = new CustomerRegistrationService({
|
|
17
|
+
accounts: options.customerAccountLifecycleWritePort,
|
|
18
|
+
sessionService: options.sessionService,
|
|
19
|
+
resolveAllowRegistrationWithoutOrganization: options.resolveAllowRegistrationWithoutOrganization,
|
|
20
|
+
});
|
|
21
|
+
const customerAddressService = new CustomerAddressService(options.emFactory, options.addressReadPort, options.customerAccountReadPort, options.auditLogService);
|
|
22
|
+
const customerDefaultsService = new CustomerDefaultsService(options.emFactory, options.defaultPreferencePort, customerAddressService);
|
|
23
|
+
const authorityService = new CustomerAuthorityService(options.salesRepVisibility);
|
|
24
|
+
const moderationService = new CustomerModerationService(options.customerAccountReadPort, options.customerAccountLifecycleWritePort, authorityService, {
|
|
25
|
+
destroyAllForCustomer: (customerAccountId) => options.sessionService.destroyAllForCustomer(customerAccountId),
|
|
26
|
+
});
|
|
27
|
+
const queryService = new CustomerAdminQueryService(customerDefaultsService, {
|
|
28
|
+
accounts: options.customerAccountReadPort,
|
|
29
|
+
accountSearch: options.customerAccountAdminSearchPort,
|
|
30
|
+
organizations: options.organizationDetailsPort,
|
|
31
|
+
customerGroups: options.customerGroupReadPort,
|
|
32
|
+
orders: options.orderReadPort,
|
|
33
|
+
});
|
|
34
|
+
const orgAssignmentService = new CustomerOrgAssignmentService(options.customerAccountReadPort, options.customerAccountLifecycleWritePort, options.organizationDetailsPort, authorityService, options.personalOrganizationPort);
|
|
35
|
+
const deletionService = new CustomerDeletionService(options.customerAccountReadPort, options.customerAccountLifecycleWritePort, options.personalOrganizationPort, authorityService, {
|
|
36
|
+
destroyAllForCustomer: (customerAccountId) => options.sessionService.destroyAllForCustomer(customerAccountId),
|
|
37
|
+
});
|
|
38
|
+
const presenceService = new CustomerPresenceService({
|
|
39
|
+
listRecentlyActiveCustomers: (windowMinutes) => options.sessionService.listRecentlyActiveCustomers(windowMinutes),
|
|
40
|
+
}, options.authSessionReadPort, options.resolvePresenceFreshnessMinutes, options.customerAccountReadPort);
|
|
41
|
+
const passwordResetService = options.passwordResetService;
|
|
42
|
+
const anonymizationSweepWorker = new AnonymizationSweepWorker(deletionService, options.resolveDeletionRetentionDays);
|
|
43
|
+
const plugin = async (app) => {
|
|
44
|
+
await registerCustomersRegisterRoutes(app, { registrationService });
|
|
45
|
+
await registerCustomersSelfRoutes(app, {
|
|
46
|
+
accounts: options.customerAccountReadPort,
|
|
47
|
+
requireCustomer: options.requireCustomer,
|
|
48
|
+
resolveCustomerActor: options.resolveCustomerActor,
|
|
49
|
+
customerAuthService,
|
|
50
|
+
orderList: options.orderList,
|
|
51
|
+
rfqService: options.rfqService,
|
|
52
|
+
customerAddressService,
|
|
53
|
+
customerDefaultsService,
|
|
54
|
+
});
|
|
55
|
+
await registerCustomersAdminRoutes(app, {
|
|
56
|
+
accounts: options.customerAccountReadPort,
|
|
57
|
+
accountWrites: options.customerAccountLifecycleWritePort,
|
|
58
|
+
requireAdmin: options.requireAdmin,
|
|
59
|
+
resolveModerationActor: options.resolveModerationActor,
|
|
60
|
+
moderationService,
|
|
61
|
+
queryService,
|
|
62
|
+
orgAssignmentService,
|
|
63
|
+
addressService: customerAddressService,
|
|
64
|
+
cartQueryService: options.cartQueryPort,
|
|
65
|
+
orderList: options.orderList,
|
|
66
|
+
rfqService: options.rfqService,
|
|
67
|
+
vatValidator: options.vatValidator,
|
|
68
|
+
impersonationService: options.impersonationPort,
|
|
69
|
+
deletionService,
|
|
70
|
+
presenceService,
|
|
71
|
+
passwordResetService,
|
|
72
|
+
mailer: options.mailer,
|
|
73
|
+
auditLogService: options.auditLogService,
|
|
74
|
+
storefrontBaseUrl: options.storefrontBaseUrl,
|
|
75
|
+
...(options.customFieldValues ? { customFieldValues: options.customFieldValues } : {}),
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
plugin,
|
|
80
|
+
handle: () => ({ registrationService, anonymizationSweepWorker, authorityService }),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,2BAA2B,EAAE,MAAM,6CAA6C,CAAC;AAC1F,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAClF,OAAO,EACL,wBAAwB,GAEzB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,EAAE,yBAAyB,EAAE,MAAM,4CAA4C,CAAC;AACvF,OAAO,EAAE,4BAA4B,EAAE,MAAM,+CAA+C,CAAC;AAC7F,OAAO,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAClF,OAAO,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAClF,OAAO,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC;AAuBnF,OAAO,EAAE,+BAA+B,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EACL,2BAA2B,GAG5B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,4BAA4B,GAG7B,MAAM,mBAAmB,CAAC;AA8J3B,MAAM,UAAU,eAAe,CAAC,OAA+B;IAI7D,MAAM,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IACxD,MAAM,mBAAmB,GAAG,IAAI,2BAA2B,CAAC;QAC1D,QAAQ,EAAE,OAAO,CAAC,iCAAiC;QACnD,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,2CAA2C,EACzC,OAAO,CAAC,2CAA2C;KACtD,CAAC,CAAC;IACH,MAAM,sBAAsB,GAAG,IAAI,sBAAsB,CACvD,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,eAAe,EACvB,OAAO,CAAC,uBAAuB,EAC/B,OAAO,CAAC,eAAe,CACxB,CAAC;IACF,MAAM,uBAAuB,GAAG,IAAI,uBAAuB,CACzD,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,qBAAqB,EAC7B,sBAAsB,CACvB,CAAC;IACF,MAAM,gBAAgB,GAAG,IAAI,wBAAwB,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAClF,MAAM,iBAAiB,GAAG,IAAI,yBAAyB,CACrD,OAAO,CAAC,uBAAuB,EAC/B,OAAO,CAAC,iCAAiC,EACzC,gBAAgB,EAChB;QACE,qBAAqB,EAAE,CAAC,iBAAiB,EAAE,EAAE,CAC3C,OAAO,CAAC,cAAc,CAAC,qBAAqB,CAAC,iBAAiB,CAAC;KAClE,CACF,CAAC;IACF,MAAM,YAAY,GAAG,IAAI,yBAAyB,CAAC,uBAAuB,EAAE;QAC1E,QAAQ,EAAE,OAAO,CAAC,uBAAuB;QACzC,aAAa,EAAE,OAAO,CAAC,8BAA8B;QACrD,aAAa,EAAE,OAAO,CAAC,uBAAuB;QAC9C,cAAc,EAAE,OAAO,CAAC,qBAAqB;QAC7C,MAAM,EAAE,OAAO,CAAC,aAAa;KAC9B,CAAC,CAAC;IACH,MAAM,oBAAoB,GAAG,IAAI,4BAA4B,CAC3D,OAAO,CAAC,uBAAuB,EAC/B,OAAO,CAAC,iCAAiC,EACzC,OAAO,CAAC,uBAAuB,EAC/B,gBAAgB,EAChB,OAAO,CAAC,wBAAwB,CACjC,CAAC;IACF,MAAM,eAAe,GAAG,IAAI,uBAAuB,CACjD,OAAO,CAAC,uBAAuB,EAC/B,OAAO,CAAC,iCAAiC,EACzC,OAAO,CAAC,wBAAwB,EAChC,gBAAgB,EAChB;QACE,qBAAqB,EAAE,CAAC,iBAAiB,EAAE,EAAE,CAC3C,OAAO,CAAC,cAAc,CAAC,qBAAqB,CAAC,iBAAiB,CAAC;KAClE,CACF,CAAC;IACF,MAAM,eAAe,GAAG,IAAI,uBAAuB,CACjD;QACE,2BAA2B,EAAE,CAAC,aAAa,EAAE,EAAE,CAC7C,OAAO,CAAC,cAAc,CAAC,2BAA2B,CAAC,aAAa,CAAC;KACpE,EACD,OAAO,CAAC,mBAAmB,EAC3B,OAAO,CAAC,+BAA+B,EACvC,OAAO,CAAC,uBAAuB,CAChC,CAAC;IACF,MAAM,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAC1D,MAAM,wBAAwB,GAAG,IAAI,wBAAwB,CAC3D,eAAe,EACf,OAAO,CAAC,4BAA4B,CACrC,CAAC;IAEF,MAAM,MAAM,GAAiB,KAAK,EAAE,GAAG,EAAE,EAAE;QACzC,MAAM,+BAA+B,CAAC,GAAG,EAAE,EAAE,mBAAmB,EAAE,CAAC,CAAC;QACpE,MAAM,2BAA2B,CAAC,GAAG,EAAE;YACrC,QAAQ,EAAE,OAAO,CAAC,uBAAuB;YACzC,eAAe,EAAE,OAAO,CAAC,eAAe;YACxC,oBAAoB,EAAE,OAAO,CAAC,oBAAoB;YAClD,mBAAmB;YACnB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,sBAAsB;YACtB,uBAAuB;SACxB,CAAC,CAAC;QACH,MAAM,4BAA4B,CAAC,GAAG,EAAE;YACtC,QAAQ,EAAE,OAAO,CAAC,uBAAuB;YACzC,aAAa,EAAE,OAAO,CAAC,iCAAiC;YACxD,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,sBAAsB,EAAE,OAAO,CAAC,sBAAsB;YACtD,iBAAiB;YACjB,YAAY;YACZ,oBAAoB;YACpB,cAAc,EAAE,sBAAsB;YACtC,gBAAgB,EAAE,OAAO,CAAC,aAAa;YACvC,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,oBAAoB,EAAE,OAAO,CAAC,iBAAiB;YAC/C,eAAe;YACf,eAAe;YACf,oBAAoB;YACpB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,eAAe,EAAE,OAAO,CAAC,eAAe;YACxC,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;YAC5C,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACvF,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO;QACL,MAAM;QACN,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,CAAC;KACpF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
|
|
2
|
+
import { type CartQueryPort, type CustomerAccountLifecycleWritePort, type CustomerAccountReadPort, type CustomFieldValuePort, type EmailMailerPort, type ImpersonationPort, type OrderListPort, type RfqCustomerPort, type VatValidator } from '@endora-commerce/contracts';
|
|
3
|
+
import type { CustomerModerationService } from './services/customer-moderation-service.js';
|
|
4
|
+
import type { CustomerAdminQueryService } from './services/customer-admin-query-service.js';
|
|
5
|
+
import type { CustomerOrgAssignmentService } from './services/customer-org-assignment-service.js';
|
|
6
|
+
import type { CustomerDeletionService } from './services/customer-deletion-service.js';
|
|
7
|
+
import type { CustomerPresenceService } from './services/customer-presence-service.js';
|
|
8
|
+
import type { CustomerAddressService } from './services/customer-address-service.js';
|
|
9
|
+
import type { CustomerPasswordResetPort } from '@endora-commerce/contracts';
|
|
10
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
11
|
+
/**
|
|
12
|
+
* Admin customer-management routes (feature 040). Block/unblock land here in
|
|
13
|
+
* US3; later user stories extend this file (impersonate, detail, group,
|
|
14
|
+
* delete/restore, presence). Every route is gated by `requireAdmin`.
|
|
15
|
+
*/
|
|
16
|
+
export type RequireAdminGuard = (permission?: string) => (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Resolves the acting admin's id, whether they are a Platform Administrator,
|
|
19
|
+
* and (for a scoped Salesperson) the Organizations they may act on.
|
|
20
|
+
*/
|
|
21
|
+
export type ResolveModerationActor = (request: FastifyRequest) => Promise<{
|
|
22
|
+
adminUserId: string;
|
|
23
|
+
isPlatformAdmin: boolean;
|
|
24
|
+
allowedOrganizationIds: string[];
|
|
25
|
+
}>;
|
|
26
|
+
export interface CustomersAdminDeps {
|
|
27
|
+
/**
|
|
28
|
+
* Feature 075 — `customer_accounts`' published read and lifecycle write,
|
|
29
|
+
* where this file used to load and patch that module's entity through an
|
|
30
|
+
* `EntityManager` it no longer needs at all.
|
|
31
|
+
*/
|
|
32
|
+
accounts: CustomerAccountReadPort;
|
|
33
|
+
accountWrites: CustomerAccountLifecycleWritePort;
|
|
34
|
+
requireAdmin: RequireAdminGuard;
|
|
35
|
+
resolveModerationActor: ResolveModerationActor;
|
|
36
|
+
moderationService: CustomerModerationService;
|
|
37
|
+
queryService: CustomerAdminQueryService;
|
|
38
|
+
orgAssignmentService: CustomerOrgAssignmentService;
|
|
39
|
+
addressService: CustomerAddressService;
|
|
40
|
+
cartQueryService: CartQueryPort;
|
|
41
|
+
/**
|
|
42
|
+
* `orders`' published list (feature 075), where this surface used to hold
|
|
43
|
+
* `Pick<OrderListService, 'list'>` — a type operator in front of a
|
|
44
|
+
* cross-module import of the class.
|
|
45
|
+
*/
|
|
46
|
+
orderList: OrderListPort;
|
|
47
|
+
rfqService: RfqCustomerPort;
|
|
48
|
+
vatValidator: VatValidator;
|
|
49
|
+
/**
|
|
50
|
+
* `admin_users`' published impersonation seam. The "view as this customer"
|
|
51
|
+
* control lives on this screen and the machinery lives in that module by
|
|
52
|
+
* design, so the surface holds the port rather than a second instance of the
|
|
53
|
+
* service (feature 075).
|
|
54
|
+
*/
|
|
55
|
+
impersonationService: ImpersonationPort;
|
|
56
|
+
deletionService: CustomerDeletionService;
|
|
57
|
+
presenceService: CustomerPresenceService;
|
|
58
|
+
passwordResetService: CustomerPasswordResetPort;
|
|
59
|
+
mailer: EmailMailerPort;
|
|
60
|
+
auditLogService: AuditPort;
|
|
61
|
+
storefrontBaseUrl: string;
|
|
62
|
+
/**
|
|
63
|
+
* Feature 055 — validates Customer custom-field values. It no longer
|
|
64
|
+
* persists them: `customer_accounts` owns the row, so its lifecycle port
|
|
65
|
+
* runs the Command and this validator is the merge that port is handed.
|
|
66
|
+
*/
|
|
67
|
+
customFieldValues?: CustomFieldValuePort;
|
|
68
|
+
}
|
|
69
|
+
export declare function registerCustomersAdminRoutes(app: FastifyInstance, deps: CustomersAdminDeps): Promise<void>;
|
|
70
|
+
//# sourceMappingURL=routes.admin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.admin.d.ts","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAYL,KAAK,aAAa,EAClB,KAAK,iCAAiC,EACtC,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,YAAY,EAClB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AAC3F,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4CAA4C,CAAC;AAC5F,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,+CAA+C,CAAC;AAClG,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AACvF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AACvF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrF,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAC5E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAmBlE;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC9B,UAAU,CAAC,EAAE,MAAM,KAChB,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAErE;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,CACnC,OAAO,EAAE,cAAc,KACpB,OAAO,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;IACzB,sBAAsB,EAAE,MAAM,EAAE,CAAC;CAClC,CAAC,CAAC;AAEH,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,QAAQ,EAAE,uBAAuB,CAAC;IAClC,aAAa,EAAE,iCAAiC,CAAC;IACjD,YAAY,EAAE,iBAAiB,CAAC;IAChC,sBAAsB,EAAE,sBAAsB,CAAC;IAC/C,iBAAiB,EAAE,yBAAyB,CAAC;IAC7C,YAAY,EAAE,yBAAyB,CAAC;IACxC,oBAAoB,EAAE,4BAA4B,CAAC;IACnD,cAAc,EAAE,sBAAsB,CAAC;IACvC,gBAAgB,EAAE,aAAa,CAAC;IAChC;;;;OAIG;IACH,SAAS,EAAE,aAAa,CAAC;IACzB,UAAU,EAAE,eAAe,CAAC;IAC5B,YAAY,EAAE,YAAY,CAAC;IAC3B;;;;;OAKG;IACH,oBAAoB,EAAE,iBAAiB,CAAC;IACxC,eAAe,EAAE,uBAAuB,CAAC;IACzC,eAAe,EAAE,uBAAuB,CAAC;IACzC,oBAAoB,EAAE,yBAAyB,CAAC;IAChD,MAAM,EAAE,eAAe,CAAC;IACxB,eAAe,EAAE,SAAS,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,oBAAoB,CAAC;CAC1C;AAED,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CAgbf"}
|