@endora-commerce/mod-customer-accounts 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 +59 -0
- package/dist/admin/api/customer-groups-client.d.ts +27 -0
- package/dist/admin/api/customer-groups-client.d.ts.map +1 -0
- package/dist/admin/api/customer-groups-client.js +17 -0
- package/dist/admin/api/customer-groups-client.js.map +1 -0
- package/dist/admin/index.d.ts +33 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +38 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/CustomerGroupsPage.d.ts +18 -0
- package/dist/admin/pages/CustomerGroupsPage.d.ts.map +1 -0
- package/dist/admin/pages/CustomerGroupsPage.js +108 -0
- package/dist/admin/pages/CustomerGroupsPage.js.map +1 -0
- package/dist/backend/entities/customer-account.entity.d.ts +111 -0
- package/dist/backend/entities/customer-account.entity.d.ts.map +1 -0
- package/dist/backend/entities/customer-account.entity.js +230 -0
- package/dist/backend/entities/customer-account.entity.js.map +1 -0
- package/dist/backend/entities/customer-group.entity.d.ts +29 -0
- package/dist/backend/entities/customer-group.entity.d.ts.map +1 -0
- package/dist/backend/entities/customer-group.entity.js +70 -0
- package/dist/backend/entities/customer-group.entity.js.map +1 -0
- package/dist/backend/entities/password-reset-token.entity.d.ts +16 -0
- package/dist/backend/entities/password-reset-token.entity.d.ts.map +1 -0
- package/dist/backend/entities/password-reset-token.entity.js +59 -0
- package/dist/backend/entities/password-reset-token.entity.js.map +1 -0
- package/dist/backend/index.d.ts +144 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +407 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +26 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +35 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/services/audit-references.d.ts +22 -0
- package/dist/backend/services/audit-references.d.ts.map +1 -0
- package/dist/backend/services/audit-references.js +46 -0
- package/dist/backend/services/audit-references.js.map +1 -0
- package/dist/backend/services/customer-account-lifecycle-ports.d.ts +168 -0
- package/dist/backend/services/customer-account-lifecycle-ports.d.ts.map +1 -0
- package/dist/backend/services/customer-account-lifecycle-ports.js +420 -0
- package/dist/backend/services/customer-account-lifecycle-ports.js.map +1 -0
- package/dist/backend/services/customer-account-ports.d.ts +143 -0
- package/dist/backend/services/customer-account-ports.d.ts.map +1 -0
- package/dist/backend/services/customer-account-ports.js +422 -0
- package/dist/backend/services/customer-account-ports.js.map +1 -0
- package/dist/backend/services/customer-auth-service.d.ts +62 -0
- package/dist/backend/services/customer-auth-service.d.ts.map +1 -0
- package/dist/backend/services/customer-auth-service.js +105 -0
- package/dist/backend/services/customer-auth-service.js.map +1 -0
- package/dist/backend/services/customer-group-read-port.d.ts +24 -0
- package/dist/backend/services/customer-group-read-port.d.ts.map +1 -0
- package/dist/backend/services/customer-group-read-port.js +44 -0
- package/dist/backend/services/customer-group-read-port.js.map +1 -0
- package/dist/backend/services/customer-group-service.d.ts +21 -0
- package/dist/backend/services/customer-group-service.d.ts.map +1 -0
- package/dist/backend/services/customer-group-service.js +97 -0
- package/dist/backend/services/customer-group-service.js.map +1 -0
- package/dist/backend/services/customer-rollup-scope.d.ts +30 -0
- package/dist/backend/services/customer-rollup-scope.d.ts.map +1 -0
- package/dist/backend/services/customer-rollup-scope.js +42 -0
- package/dist/backend/services/customer-rollup-scope.js.map +1 -0
- package/dist/backend/services/password-reset-service.d.ts +15 -0
- package/dist/backend/services/password-reset-service.d.ts.map +1 -0
- package/dist/backend/services/password-reset-service.js +90 -0
- package/dist/backend/services/password-reset-service.js.map +1 -0
- package/dist/backend/services/role-service.d.ts +22 -0
- package/dist/backend/services/role-service.d.ts.map +1 -0
- package/dist/backend/services/role-service.js +83 -0
- package/dist/backend/services/role-service.js.map +1 -0
- package/dist/backend/services/two-factor-enrolments.d.ts +18 -0
- package/dist/backend/services/two-factor-enrolments.d.ts.map +1 -0
- package/dist/backend/services/two-factor-enrolments.js +2 -0
- package/dist/backend/services/two-factor-enrolments.js.map +1 -0
- package/dist/manifest.d.ts +169 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +212 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260425T055041_customer_accounts_password_reset_tokens.d.ts +6 -0
- package/dist/migrations/20260425T055041_customer_accounts_password_reset_tokens.d.ts.map +1 -0
- package/dist/migrations/20260425T055041_customer_accounts_password_reset_tokens.js +25 -0
- package/dist/migrations/20260425T055041_customer_accounts_password_reset_tokens.js.map +1 -0
- package/dist/migrations/20260611T140351_customer_accounts_organization_optional.d.ts +34 -0
- package/dist/migrations/20260611T140351_customer_accounts_organization_optional.d.ts.map +1 -0
- package/dist/migrations/20260611T140351_customer_accounts_organization_optional.js +43 -0
- package/dist/migrations/20260611T140351_customer_accounts_organization_optional.js.map +1 -0
- package/dist/migrations/20260611T140403_customer_accounts_lifecycle.d.ts +54 -0
- package/dist/migrations/20260611T140403_customer_accounts_lifecycle.d.ts.map +1 -0
- package/dist/migrations/20260611T140403_customer_accounts_lifecycle.js +97 -0
- package/dist/migrations/20260611T140403_customer_accounts_lifecycle.js.map +1 -0
- package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.d.ts +12 -0
- package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.d.ts.map +1 -0
- package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.js +16 -0
- package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.js.map +1 -0
- package/dist/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.d.ts +14 -0
- package/dist/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.d.ts.map +1 -0
- package/dist/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.js +18 -0
- package/dist/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.js.map +1 -0
- package/dist/migrations/20260819T074816_customer_accounts_password_set_at.d.ts +22 -0
- package/dist/migrations/20260819T074816_customer_accounts_password_set_at.d.ts.map +1 -0
- package/dist/migrations/20260819T074816_customer_accounts_password_set_at.js +26 -0
- package/dist/migrations/20260819T074816_customer_accounts_password_set_at.js.map +1 -0
- package/dist/migrations/20260819T142837_customer_accounts_fold_email_case.d.ts +46 -0
- package/dist/migrations/20260819T142837_customer_accounts_fold_email_case.d.ts.map +1 -0
- package/dist/migrations/20260819T142837_customer_accounts_fold_email_case.js +97 -0
- package/dist/migrations/20260819T142837_customer_accounts_fold_email_case.js.map +1 -0
- package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.d.ts +31 -0
- package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.d.ts.map +1 -0
- package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.js +35 -0
- package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.js.map +1 -0
- package/dist/migrations/20260825T141659_customer_accounts_organization_required.d.ts +58 -0
- package/dist/migrations/20260825T141659_customer_accounts_organization_required.d.ts.map +1 -0
- package/dist/migrations/20260825T141659_customer_accounts_organization_required.js +133 -0
- package/dist/migrations/20260825T141659_customer_accounts_organization_required.js.map +1 -0
- package/dist/migrations/20260912T094701_customer_accounts_sales_channel_customer_accounts.d.ts +27 -0
- package/dist/migrations/20260912T094701_customer_accounts_sales_channel_customer_accounts.d.ts.map +1 -0
- package/dist/migrations/20260912T094701_customer_accounts_sales_channel_customer_accounts.js +44 -0
- package/dist/migrations/20260912T094701_customer_accounts_sales_channel_customer_accounts.js.map +1 -0
- package/dist/migrations/index.d.ts +36 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +47 -0
- package/dist/migrations/index.js.map +1 -0
- package/docs/customer_accounts.md +38 -0
- package/i18n/en.json +30 -0
- package/i18n/pl.json +30 -0
- package/package.json +94 -0
- package/tailwind.css +14 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Feature 040 (Customers) — extends `customer_accounts` with the lifecycle
|
|
4
|
+
* fields the Customers module needs:
|
|
5
|
+
*
|
|
6
|
+
* - `customer_group_id` — direct customer→customer-group membership (FR-022),
|
|
7
|
+
* FK to `customer_groups` ON DELETE SET NULL.
|
|
8
|
+
* - block state + provenance (`blocked_at`, `block_reason`, `block_source`,
|
|
9
|
+
* `blocked_by_admin_user_id`, `blocked_by_customer_account_id`) — FR-012…017.
|
|
10
|
+
* - deletion / anonymization state (`deletion_requested_by_admin_user_id`,
|
|
11
|
+
* `anonymized_at`) paired with the existing `deleted_at` — FR-039/FR-040.
|
|
12
|
+
*
|
|
13
|
+
* All columns are nullable; existing rows are unaffected.
|
|
14
|
+
*
|
|
15
|
+
* **It also creates `customer_groups` since
|
|
16
|
+
* `specs/120-migration-closure-bridge-ownership/` Phase 3.** The table was
|
|
17
|
+
* created by `price_lists`' frozen
|
|
18
|
+
* `Migration20260426T075235PriceListsPricingInit` while this module owns it —
|
|
19
|
+
* its `CustomerGroup` entity declares the table name — because tiered pricing
|
|
20
|
+
* needed customer groups first and created them where it needed them.
|
|
21
|
+
* The table was created by whoever needed it first rather than by whoever owns
|
|
22
|
+
* it, and the comment above used to record that as *"owned by price_lists"*.
|
|
23
|
+
*
|
|
24
|
+
* **Why this site is repaired while 27 other misfiled creations are not.**
|
|
25
|
+
* Moving a *reference* into its owner's module can only move it strictly later
|
|
26
|
+
* in the computed order, which is always safe; moving a *creation* moves it to
|
|
27
|
+
* a position every existing reference must be re-checked against. The other 27
|
|
28
|
+
* fail **safe** — a client omitting `catalog` gets an empty `products` table.
|
|
29
|
+
* This one also produced a fail-**closed** reference: the foreign key below
|
|
30
|
+
* named a table outside this module's closure, and declaring `price_lists`
|
|
31
|
+
* here to fix that would close a cycle, `price_lists` already declaring
|
|
32
|
+
* `customer_accounts`. That, and nothing else, is why it is on the list
|
|
33
|
+
* (owner's ruling, 2026-09-12).
|
|
34
|
+
*
|
|
35
|
+
* **The re-check, derived from the computed order rather than from a stamp.**
|
|
36
|
+
* The only reference to `customer_groups` in the entire corpus is this
|
|
37
|
+
* migration's own foreign key, three statements below. `price_lists` does not
|
|
38
|
+
* reference it: `price_list_assignments.customer_group_id` is a nullable column
|
|
39
|
+
* with an index and no foreign key, and so is `organizations.customer_group_id`.
|
|
40
|
+
* So the creation is placed in the frozen body that carries the only reference,
|
|
41
|
+
* above it, and every position in between is unaffected.
|
|
42
|
+
*
|
|
43
|
+
* **`if not exists`, and it is load-bearing.** A database that applied the
|
|
44
|
+
* April migration and has not yet reached this one already has the table while
|
|
45
|
+
* this migration is pending; a verbatim `create table` would fail there. Both
|
|
46
|
+
* class names are unchanged and `BASELINE_MIGRATIONS` is untouched —
|
|
47
|
+
* `mikro_orm_migrations` persists the class name and no checksum, so neither
|
|
48
|
+
* edited body is re-offered to a database that has applied it.
|
|
49
|
+
*/
|
|
50
|
+
export declare class Migration20260611T140403CustomerAccountsLifecycle extends Migration {
|
|
51
|
+
up(): Promise<void>;
|
|
52
|
+
down(): Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=20260611T140403_customer_accounts_lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260611T140403_customer_accounts_lifecycle.d.ts","sourceRoot":"","sources":["../../src/migrations/20260611T140403_customer_accounts_lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,qBAAa,iDAAkD,SAAQ,SAAS;IAC/D,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAqCnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAgBrC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Feature 040 (Customers) — extends `customer_accounts` with the lifecycle
|
|
4
|
+
* fields the Customers module needs:
|
|
5
|
+
*
|
|
6
|
+
* - `customer_group_id` — direct customer→customer-group membership (FR-022),
|
|
7
|
+
* FK to `customer_groups` ON DELETE SET NULL.
|
|
8
|
+
* - block state + provenance (`blocked_at`, `block_reason`, `block_source`,
|
|
9
|
+
* `blocked_by_admin_user_id`, `blocked_by_customer_account_id`) — FR-012…017.
|
|
10
|
+
* - deletion / anonymization state (`deletion_requested_by_admin_user_id`,
|
|
11
|
+
* `anonymized_at`) paired with the existing `deleted_at` — FR-039/FR-040.
|
|
12
|
+
*
|
|
13
|
+
* All columns are nullable; existing rows are unaffected.
|
|
14
|
+
*
|
|
15
|
+
* **It also creates `customer_groups` since
|
|
16
|
+
* `specs/120-migration-closure-bridge-ownership/` Phase 3.** The table was
|
|
17
|
+
* created by `price_lists`' frozen
|
|
18
|
+
* `Migration20260426T075235PriceListsPricingInit` while this module owns it —
|
|
19
|
+
* its `CustomerGroup` entity declares the table name — because tiered pricing
|
|
20
|
+
* needed customer groups first and created them where it needed them.
|
|
21
|
+
* The table was created by whoever needed it first rather than by whoever owns
|
|
22
|
+
* it, and the comment above used to record that as *"owned by price_lists"*.
|
|
23
|
+
*
|
|
24
|
+
* **Why this site is repaired while 27 other misfiled creations are not.**
|
|
25
|
+
* Moving a *reference* into its owner's module can only move it strictly later
|
|
26
|
+
* in the computed order, which is always safe; moving a *creation* moves it to
|
|
27
|
+
* a position every existing reference must be re-checked against. The other 27
|
|
28
|
+
* fail **safe** — a client omitting `catalog` gets an empty `products` table.
|
|
29
|
+
* This one also produced a fail-**closed** reference: the foreign key below
|
|
30
|
+
* named a table outside this module's closure, and declaring `price_lists`
|
|
31
|
+
* here to fix that would close a cycle, `price_lists` already declaring
|
|
32
|
+
* `customer_accounts`. That, and nothing else, is why it is on the list
|
|
33
|
+
* (owner's ruling, 2026-09-12).
|
|
34
|
+
*
|
|
35
|
+
* **The re-check, derived from the computed order rather than from a stamp.**
|
|
36
|
+
* The only reference to `customer_groups` in the entire corpus is this
|
|
37
|
+
* migration's own foreign key, three statements below. `price_lists` does not
|
|
38
|
+
* reference it: `price_list_assignments.customer_group_id` is a nullable column
|
|
39
|
+
* with an index and no foreign key, and so is `organizations.customer_group_id`.
|
|
40
|
+
* So the creation is placed in the frozen body that carries the only reference,
|
|
41
|
+
* above it, and every position in between is unaffected.
|
|
42
|
+
*
|
|
43
|
+
* **`if not exists`, and it is load-bearing.** A database that applied the
|
|
44
|
+
* April migration and has not yet reached this one already has the table while
|
|
45
|
+
* this migration is pending; a verbatim `create table` would fail there. Both
|
|
46
|
+
* class names are unchanged and `BASELINE_MIGRATIONS` is untouched —
|
|
47
|
+
* `mikro_orm_migrations` persists the class name and no checksum, so neither
|
|
48
|
+
* edited body is re-offered to a database that has applied it.
|
|
49
|
+
*/
|
|
50
|
+
export class Migration20260611T140403CustomerAccountsLifecycle extends Migration {
|
|
51
|
+
async up() {
|
|
52
|
+
this.addSql(`
|
|
53
|
+
create table if not exists "customer_groups" (
|
|
54
|
+
"id" uuid not null,
|
|
55
|
+
"code" varchar(64) not null,
|
|
56
|
+
"name" varchar(160) not null,
|
|
57
|
+
"description" varchar(1000) null,
|
|
58
|
+
"created_at" timestamptz not null,
|
|
59
|
+
"updated_at" timestamptz not null,
|
|
60
|
+
constraint "customer_groups_pkey" primary key ("id"),
|
|
61
|
+
constraint "customer_groups_code_unique" unique ("code")
|
|
62
|
+
);
|
|
63
|
+
`);
|
|
64
|
+
this.addSql(`alter table "customer_accounts"
|
|
65
|
+
add column "customer_group_id" uuid null,
|
|
66
|
+
add column "blocked_at" timestamptz null,
|
|
67
|
+
add column "block_reason" text null,
|
|
68
|
+
add column "block_source" varchar(16) null,
|
|
69
|
+
add column "blocked_by_admin_user_id" uuid null,
|
|
70
|
+
add column "blocked_by_customer_account_id" uuid null,
|
|
71
|
+
add column "deletion_requested_by_admin_user_id" uuid null,
|
|
72
|
+
add column "anonymized_at" timestamptz null;`);
|
|
73
|
+
this.addSql(`alter table "customer_accounts"
|
|
74
|
+
add constraint "customer_accounts_customer_group_fk"
|
|
75
|
+
foreign key ("customer_group_id") references "customer_groups" ("id")
|
|
76
|
+
on update cascade on delete set null;`);
|
|
77
|
+
this.addSql(`create index "customer_accounts_customer_group_id_index" on "customer_accounts" ("customer_group_id");`);
|
|
78
|
+
this.addSql(`create index "customer_accounts_blocked_at_index" on "customer_accounts" ("blocked_at");`);
|
|
79
|
+
}
|
|
80
|
+
async down() {
|
|
81
|
+
this.addSql(`alter table "customer_accounts"
|
|
82
|
+
drop constraint if exists "customer_accounts_customer_group_fk";`);
|
|
83
|
+
this.addSql(`drop index if exists "customer_accounts_customer_group_id_index";`);
|
|
84
|
+
this.addSql(`drop index if exists "customer_accounts_blocked_at_index";`);
|
|
85
|
+
this.addSql(`alter table "customer_accounts"
|
|
86
|
+
drop column if exists "customer_group_id",
|
|
87
|
+
drop column if exists "blocked_at",
|
|
88
|
+
drop column if exists "block_reason",
|
|
89
|
+
drop column if exists "block_source",
|
|
90
|
+
drop column if exists "blocked_by_admin_user_id",
|
|
91
|
+
drop column if exists "blocked_by_customer_account_id",
|
|
92
|
+
drop column if exists "deletion_requested_by_admin_user_id",
|
|
93
|
+
drop column if exists "anonymized_at";`);
|
|
94
|
+
this.addSql('drop table if exists "customer_groups" cascade;');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=20260611T140403_customer_accounts_lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260611T140403_customer_accounts_lifecycle.js","sourceRoot":"","sources":["../../src/migrations/20260611T140403_customer_accounts_lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,MAAM,OAAO,iDAAkD,SAAQ,SAAS;IACrE,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;KAWX,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC;;;;;;;;mDAQmC,CAAC,CAAC;QAEjD,IAAI,CAAC,MAAM,CAAC;;;4CAG4B,CAAC,CAAC;QAE1C,IAAI,CAAC,MAAM,CACT,wGAAwG,CACzG,CAAC;QACF,IAAI,CAAC,MAAM,CACT,0FAA0F,CAC3F,CAAC;IACJ,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC;uEACuD,CAAC,CAAC;QACrE,IAAI,CAAC,MAAM,CAAC,mEAAmE,CAAC,CAAC;QACjF,IAAI,CAAC,MAAM,CAAC,4DAA4D,CAAC,CAAC;QAC1E,IAAI,CAAC,MAAM,CAAC;;;;;;;;6CAQ6B,CAAC,CAAC;QAC3C,IAAI,CAAC,MAAM,CAAC,iDAAiD,CAAC,CAAC;IACjE,CAAC;CACF"}
|
package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Feature 055 — Custom Fields Layer. Additive: adds the `custom_field_values`
|
|
4
|
+
* JSONB bag to `customer_accounts` so operator-defined fields persist on the host row.
|
|
5
|
+
* The column inherits the host entity's tenant classification (Principle XI);
|
|
6
|
+
* no scoping column is added.
|
|
7
|
+
*/
|
|
8
|
+
export declare class Migration20260718T200341CustomerAccountsCustomerAccountCustomFieldValues extends Migration {
|
|
9
|
+
up(): Promise<void>;
|
|
10
|
+
down(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=20260718T200341_customer_accounts_customer_account_custom_field_values.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260718T200341_customer_accounts_customer_account_custom_field_values.d.ts","sourceRoot":"","sources":["../../src/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;GAKG;AACH,qBAAa,wEAAyE,SAAQ,SAAS;IACtF,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAMnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAGrC"}
|
package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Feature 055 — Custom Fields Layer. Additive: adds the `custom_field_values`
|
|
4
|
+
* JSONB bag to `customer_accounts` so operator-defined fields persist on the host row.
|
|
5
|
+
* The column inherits the host entity's tenant classification (Principle XI);
|
|
6
|
+
* no scoping column is added.
|
|
7
|
+
*/
|
|
8
|
+
export class Migration20260718T200341CustomerAccountsCustomerAccountCustomFieldValues extends Migration {
|
|
9
|
+
async up() {
|
|
10
|
+
this.addSql(`alter table "customer_accounts" add column "custom_field_values" jsonb not null default '{}';`);
|
|
11
|
+
}
|
|
12
|
+
async down() {
|
|
13
|
+
this.addSql(`alter table "customer_accounts" drop column "custom_field_values";`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=20260718T200341_customer_accounts_customer_account_custom_field_values.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260718T200341_customer_accounts_customer_account_custom_field_values.js","sourceRoot":"","sources":["../../src/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;GAKG;AACH,MAAM,OAAO,wEAAyE,SAAQ,SAAS;IAC5F,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CACT,+FAA+F,CAChG,CAAC;IACJ,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,oEAAoE,CAAC,CAAC;IACpF,CAAC;CACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Feature 056 (T032) — customer-side roll-up capability.
|
|
4
|
+
*
|
|
5
|
+
* Adds `customer_accounts.subtree_rollup_enabled` (boolean, NOT NULL DEFAULT
|
|
6
|
+
* false). When true, the customer's tenant scope widens from single-org to its
|
|
7
|
+
* organization's subtree (server-derived, Principle XI). Default false ⇒ every
|
|
8
|
+
* existing customer stays node-only, preserving flat behavior byte-for-byte.
|
|
9
|
+
*/
|
|
10
|
+
export declare class Migration20260720T044255CustomerAccountsCustomerSubtreeRollup extends Migration {
|
|
11
|
+
up(): Promise<void>;
|
|
12
|
+
down(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=20260720T044255_customer_accounts_customer_subtree_rollup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260720T044255_customer_accounts_customer_subtree_rollup.d.ts","sourceRoot":"","sources":["../../src/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;GAOG;AACH,qBAAa,6DAA8D,SAAQ,SAAS;IAC3E,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAMnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAGrC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Feature 056 (T032) — customer-side roll-up capability.
|
|
4
|
+
*
|
|
5
|
+
* Adds `customer_accounts.subtree_rollup_enabled` (boolean, NOT NULL DEFAULT
|
|
6
|
+
* false). When true, the customer's tenant scope widens from single-org to its
|
|
7
|
+
* organization's subtree (server-derived, Principle XI). Default false ⇒ every
|
|
8
|
+
* existing customer stays node-only, preserving flat behavior byte-for-byte.
|
|
9
|
+
*/
|
|
10
|
+
export class Migration20260720T044255CustomerAccountsCustomerSubtreeRollup extends Migration {
|
|
11
|
+
async up() {
|
|
12
|
+
this.addSql('alter table "customer_accounts" add column "subtree_rollup_enabled" boolean not null default false;');
|
|
13
|
+
}
|
|
14
|
+
async down() {
|
|
15
|
+
this.addSql('alter table "customer_accounts" drop column "subtree_rollup_enabled";');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=20260720T044255_customer_accounts_customer_subtree_rollup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260720T044255_customer_accounts_customer_subtree_rollup.js","sourceRoot":"","sources":["../../src/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,OAAO,6DAA8D,SAAQ,SAAS;IACjF,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CACT,qGAAqG,CACtG,CAAC;IACJ,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,uEAAuE,CAAC,CAAC;IACvF,CAAC;CACF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Issue #222 — `customer_accounts.password_set_at`.
|
|
4
|
+
*
|
|
5
|
+
* `password_hash` is NOT NULL for every account, including the ones federated
|
|
6
|
+
* sign-in auto-creates with a random pair of UUIDs nobody is told, so it cannot
|
|
7
|
+
* answer "does the holder have a password they can use". This column can.
|
|
8
|
+
*
|
|
9
|
+
* **Backfilled `null` for every existing row, with no conditional logic.** The
|
|
10
|
+
* alternative considered was `created_at` for everyone except accounts holding
|
|
11
|
+
* a social link — but there are no socially-created accounts anywhere, so that
|
|
12
|
+
* exception has an empty extension and what would actually ship is `created_at`
|
|
13
|
+
* for *every* row: the claim that every existing holder personally chose their
|
|
14
|
+
* password, which is untrue for any account an admin, an import or any other
|
|
15
|
+
* on-their-behalf path created. `null` says what is true — we do not know — and
|
|
16
|
+
* becomes accurate for every account from the first write onwards.
|
|
17
|
+
*/
|
|
18
|
+
export declare class Migration20260819T074816CustomerAccountsPasswordSetAt extends Migration {
|
|
19
|
+
up(): Promise<void>;
|
|
20
|
+
down(): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=20260819T074816_customer_accounts_password_set_at.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260819T074816_customer_accounts_password_set_at.d.ts","sourceRoot":"","sources":["../../src/migrations/20260819T074816_customer_accounts_password_set_at.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,qDAAsD,SAAQ,SAAS;IACnE,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAInB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAGrC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Issue #222 — `customer_accounts.password_set_at`.
|
|
4
|
+
*
|
|
5
|
+
* `password_hash` is NOT NULL for every account, including the ones federated
|
|
6
|
+
* sign-in auto-creates with a random pair of UUIDs nobody is told, so it cannot
|
|
7
|
+
* answer "does the holder have a password they can use". This column can.
|
|
8
|
+
*
|
|
9
|
+
* **Backfilled `null` for every existing row, with no conditional logic.** The
|
|
10
|
+
* alternative considered was `created_at` for everyone except accounts holding
|
|
11
|
+
* a social link — but there are no socially-created accounts anywhere, so that
|
|
12
|
+
* exception has an empty extension and what would actually ship is `created_at`
|
|
13
|
+
* for *every* row: the claim that every existing holder personally chose their
|
|
14
|
+
* password, which is untrue for any account an admin, an import or any other
|
|
15
|
+
* on-their-behalf path created. `null` says what is true — we do not know — and
|
|
16
|
+
* becomes accurate for every account from the first write onwards.
|
|
17
|
+
*/
|
|
18
|
+
export class Migration20260819T074816CustomerAccountsPasswordSetAt extends Migration {
|
|
19
|
+
async up() {
|
|
20
|
+
this.addSql('alter table "customer_accounts" add column "password_set_at" timestamptz null;');
|
|
21
|
+
}
|
|
22
|
+
async down() {
|
|
23
|
+
this.addSql('alter table "customer_accounts" drop column "password_set_at";');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=20260819T074816_customer_accounts_password_set_at.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260819T074816_customer_accounts_password_set_at.js","sourceRoot":"","sources":["../../src/migrations/20260819T074816_customer_accounts_password_set_at.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,qDAAsD,SAAQ,SAAS;IACzE,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CAAC,gFAAgF,CAAC,CAAC;IAChG,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,gEAAgE,CAAC,CAAC;IAChF,CAAC;CACF"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Fold `customer_accounts.email` to the form the code now stores and compares.
|
|
4
|
+
*
|
|
5
|
+
* The writes folded the address and the login read did not, so a buyer who
|
|
6
|
+
* registered as `Jan.Kowalski@example.pl` could never sign in: Postgres' `=` on
|
|
7
|
+
* `text` is case-sensitive and the row said `jan.kowalski@example.pl`. The code
|
|
8
|
+
* side of the repair makes every read and every write go through
|
|
9
|
+
* `normalizeEmailAddress`; this migration brings the rows written before it to
|
|
10
|
+
* the same form, so an address stored mixed-case stays reachable.
|
|
11
|
+
*
|
|
12
|
+
* ## What a collision means, and why it does not fail the deploy
|
|
13
|
+
*
|
|
14
|
+
* Two rows differing only in case are two accounts that the fold turns into one
|
|
15
|
+
* address, and `customer_accounts_email_unique` will not hold both. The
|
|
16
|
+
* migration therefore **folds one row per address and leaves the rest exactly as
|
|
17
|
+
* they are** — it drops nothing, merges nothing and refuses nothing:
|
|
18
|
+
*
|
|
19
|
+
* - a row that **already holds the folded address** wins it, because it is the
|
|
20
|
+
* one every other table's foreign keys have been resolving to by that
|
|
21
|
+
* address all along;
|
|
22
|
+
* - otherwise the **earliest-created** row wins it, since it is the account
|
|
23
|
+
* with the longer history of orders, invoices and addresses behind it;
|
|
24
|
+
* - every other row in the group keeps its stored spelling and is counted by
|
|
25
|
+
* the warning below.
|
|
26
|
+
*
|
|
27
|
+
* A stranded row is not data loss but it is not a working account either: the
|
|
28
|
+
* login read folds, so nothing will match its spelling until an operator merges
|
|
29
|
+
* or renames it. That is a decision about somebody's orders, and a migration
|
|
30
|
+
* running unattended must not take it. The alternative — failing the deploy —
|
|
31
|
+
* would stop every other repair in the same release over a pair of rows that a
|
|
32
|
+
* human has to look at anyway.
|
|
33
|
+
*
|
|
34
|
+
* ## Measured before it was written
|
|
35
|
+
*
|
|
36
|
+
* Zero collisions and zero mixed-case rows across both developer databases
|
|
37
|
+
* (`b2b`: 1 account; `b2b_test`: 3), which is what the two-developer-environment
|
|
38
|
+
* / no-production state of the platform predicts. The rules above are therefore
|
|
39
|
+
* written for the environments this will meet later, not for a pair of rows
|
|
40
|
+
* anybody has today.
|
|
41
|
+
*/
|
|
42
|
+
export declare class Migration20260819T142837CustomerAccountsFoldEmailCase extends Migration {
|
|
43
|
+
up(): Promise<void>;
|
|
44
|
+
down(): Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=20260819T142837_customer_accounts_fold_email_case.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260819T142837_customer_accounts_fold_email_case.d.ts","sourceRoot":"","sources":["../../src/migrations/20260819T142837_customer_accounts_fold_email_case.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,qBAAa,qDAAsD,SAAQ,SAAS;IACnE,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IA+CnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAQrC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Fold `customer_accounts.email` to the form the code now stores and compares.
|
|
4
|
+
*
|
|
5
|
+
* The writes folded the address and the login read did not, so a buyer who
|
|
6
|
+
* registered as `Jan.Kowalski@example.pl` could never sign in: Postgres' `=` on
|
|
7
|
+
* `text` is case-sensitive and the row said `jan.kowalski@example.pl`. The code
|
|
8
|
+
* side of the repair makes every read and every write go through
|
|
9
|
+
* `normalizeEmailAddress`; this migration brings the rows written before it to
|
|
10
|
+
* the same form, so an address stored mixed-case stays reachable.
|
|
11
|
+
*
|
|
12
|
+
* ## What a collision means, and why it does not fail the deploy
|
|
13
|
+
*
|
|
14
|
+
* Two rows differing only in case are two accounts that the fold turns into one
|
|
15
|
+
* address, and `customer_accounts_email_unique` will not hold both. The
|
|
16
|
+
* migration therefore **folds one row per address and leaves the rest exactly as
|
|
17
|
+
* they are** — it drops nothing, merges nothing and refuses nothing:
|
|
18
|
+
*
|
|
19
|
+
* - a row that **already holds the folded address** wins it, because it is the
|
|
20
|
+
* one every other table's foreign keys have been resolving to by that
|
|
21
|
+
* address all along;
|
|
22
|
+
* - otherwise the **earliest-created** row wins it, since it is the account
|
|
23
|
+
* with the longer history of orders, invoices and addresses behind it;
|
|
24
|
+
* - every other row in the group keeps its stored spelling and is counted by
|
|
25
|
+
* the warning below.
|
|
26
|
+
*
|
|
27
|
+
* A stranded row is not data loss but it is not a working account either: the
|
|
28
|
+
* login read folds, so nothing will match its spelling until an operator merges
|
|
29
|
+
* or renames it. That is a decision about somebody's orders, and a migration
|
|
30
|
+
* running unattended must not take it. The alternative — failing the deploy —
|
|
31
|
+
* would stop every other repair in the same release over a pair of rows that a
|
|
32
|
+
* human has to look at anyway.
|
|
33
|
+
*
|
|
34
|
+
* ## Measured before it was written
|
|
35
|
+
*
|
|
36
|
+
* Zero collisions and zero mixed-case rows across both developer databases
|
|
37
|
+
* (`b2b`: 1 account; `b2b_test`: 3), which is what the two-developer-environment
|
|
38
|
+
* / no-production state of the platform predicts. The rules above are therefore
|
|
39
|
+
* written for the environments this will meet later, not for a pair of rows
|
|
40
|
+
* anybody has today.
|
|
41
|
+
*/
|
|
42
|
+
export class Migration20260819T142837CustomerAccountsFoldEmailCase extends Migration {
|
|
43
|
+
async up() {
|
|
44
|
+
// One winner per folded address: a row that already holds it first
|
|
45
|
+
// (`email = folded` sorts true-first under `desc`), then the earliest
|
|
46
|
+
// created, then the smallest id so the choice is deterministic. The winner
|
|
47
|
+
// is only updated when it does not already hold the folded value, which is
|
|
48
|
+
// also why this statement cannot violate the unique index: an update
|
|
49
|
+
// happens only for a group in which no row holds the target address.
|
|
50
|
+
this.addSql(`
|
|
51
|
+
with ranked as (
|
|
52
|
+
select "id",
|
|
53
|
+
lower(btrim("email")) as folded,
|
|
54
|
+
row_number() over (
|
|
55
|
+
partition by lower(btrim("email"))
|
|
56
|
+
order by ("email" = lower(btrim("email"))) desc,
|
|
57
|
+
"created_at" asc,
|
|
58
|
+
"id" asc
|
|
59
|
+
) as rank
|
|
60
|
+
from "customer_accounts"
|
|
61
|
+
)
|
|
62
|
+
update "customer_accounts" ca
|
|
63
|
+
set "email" = r.folded
|
|
64
|
+
from ranked r
|
|
65
|
+
where r."id" = ca."id"
|
|
66
|
+
and r.rank = 1
|
|
67
|
+
and ca."email" <> r.folded;
|
|
68
|
+
`);
|
|
69
|
+
// Say out loud what was left behind. A migration that quietly leaves an
|
|
70
|
+
// account nobody can log into is the same class of defect as the one being
|
|
71
|
+
// repaired here.
|
|
72
|
+
this.addSql(`
|
|
73
|
+
do $$
|
|
74
|
+
declare
|
|
75
|
+
stranded integer;
|
|
76
|
+
begin
|
|
77
|
+
select count(*) into stranded
|
|
78
|
+
from "customer_accounts"
|
|
79
|
+
where "email" <> lower(btrim("email"));
|
|
80
|
+
if stranded > 0 then
|
|
81
|
+
raise warning
|
|
82
|
+
'customer_accounts: % row(s) kept a mixed-case e-mail because another account already holds the folded address. They cannot be signed into until an operator merges or renames them.',
|
|
83
|
+
stranded;
|
|
84
|
+
end if;
|
|
85
|
+
end $$;
|
|
86
|
+
`);
|
|
87
|
+
}
|
|
88
|
+
async down() {
|
|
89
|
+
// Deliberately empty. The original capitalisation is not recorded anywhere
|
|
90
|
+
// — this migration overwrites the only copy of it — so there is nothing to
|
|
91
|
+
// restore, and an `up` that folded a handful of rows must not be paired
|
|
92
|
+
// with a `down` that pretends otherwise. Rolling this back means rolling
|
|
93
|
+
// back the code that reads the folded form; the rows are then already in
|
|
94
|
+
// the shape the old code wrote, which is what it was folding to anyway.
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=20260819T142837_customer_accounts_fold_email_case.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260819T142837_customer_accounts_fold_email_case.js","sourceRoot":"","sources":["../../src/migrations/20260819T142837_customer_accounts_fold_email_case.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,OAAO,qDAAsD,SAAQ,SAAS;IACzE,KAAK,CAAC,EAAE;QACf,mEAAmE;QACnE,sEAAsE;QACtE,2EAA2E;QAC3E,2EAA2E;QAC3E,qEAAqE;QACrE,qEAAqE;QACrE,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;;;;;;;;KAkBX,CAAC,CAAC;QAEH,wEAAwE;QACxE,2EAA2E;QAC3E,iBAAiB;QACjB,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;;;;KAcX,CAAC,CAAC;IACL,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,2EAA2E;QAC3E,2EAA2E;QAC3E,wEAAwE;QACxE,yEAAyE;QACzE,yEAAyE;QACzE,wEAAwE;IAC1E,CAAC;CACF"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Drops `customer_accounts.two_factor_secret`, the superseded customer 2FA
|
|
4
|
+
* column.
|
|
5
|
+
*
|
|
6
|
+
* The column could never hold a value. `POST /api/v1/me/two-factor/enable`,
|
|
7
|
+
* the only writer, assembled `<32-char base32 secret>|<10 sha256 hashes>` —
|
|
8
|
+
* 682 characters — and the column is `varchar(64)`, on which PostgreSQL
|
|
9
|
+
* raises `22001` rather than truncating. So the route answered 500 for every
|
|
10
|
+
* customer from the day it was written, and the routes, the port and the
|
|
11
|
+
* service that owned it are deleted in the same merge request. The live
|
|
12
|
+
* customer 2FA surface is `mfa`'s `/api/v1/account/mfa/*`, whose own init
|
|
13
|
+
* migration already records these columns as SUPERSEDED.
|
|
14
|
+
*
|
|
15
|
+
* **Nothing is destroyed.** Measured on the dev database on 2026-08-25:
|
|
16
|
+
* `customer_accounts` holds 1 row, of which `count(two_factor_secret)` is 0.
|
|
17
|
+
* `deploy/` carries `example` and `acceptance` only — there is no client
|
|
18
|
+
* deployment and no production database. `down()` therefore re-creates the
|
|
19
|
+
* column's shape and nothing else, because there has never been a value to
|
|
20
|
+
* restore.
|
|
21
|
+
*
|
|
22
|
+
* `two_factor_confirmed_at` deliberately stays: it still feeds a published
|
|
23
|
+
* `twoFactorEnabled` field on six API responses and two admin screens, and
|
|
24
|
+
* what happens to that field is an open owner decision
|
|
25
|
+
* (`specs/087-tenant-scope-enforcement/superseded-2fa-analysis.md` §7).
|
|
26
|
+
*/
|
|
27
|
+
export declare class Migration20260825T124759CustomerAccountsDropLegacyTwoFactorSecret extends Migration {
|
|
28
|
+
up(): Promise<void>;
|
|
29
|
+
down(): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=20260825T124759_customer_accounts_drop_legacy_two_factor_secret.d.ts.map
|
package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260825T124759_customer_accounts_drop_legacy_two_factor_secret.d.ts","sourceRoot":"","sources":["../../src/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,iEAAkE,SAAQ,SAAS;IAC/E,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAInB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAGrC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Drops `customer_accounts.two_factor_secret`, the superseded customer 2FA
|
|
4
|
+
* column.
|
|
5
|
+
*
|
|
6
|
+
* The column could never hold a value. `POST /api/v1/me/two-factor/enable`,
|
|
7
|
+
* the only writer, assembled `<32-char base32 secret>|<10 sha256 hashes>` —
|
|
8
|
+
* 682 characters — and the column is `varchar(64)`, on which PostgreSQL
|
|
9
|
+
* raises `22001` rather than truncating. So the route answered 500 for every
|
|
10
|
+
* customer from the day it was written, and the routes, the port and the
|
|
11
|
+
* service that owned it are deleted in the same merge request. The live
|
|
12
|
+
* customer 2FA surface is `mfa`'s `/api/v1/account/mfa/*`, whose own init
|
|
13
|
+
* migration already records these columns as SUPERSEDED.
|
|
14
|
+
*
|
|
15
|
+
* **Nothing is destroyed.** Measured on the dev database on 2026-08-25:
|
|
16
|
+
* `customer_accounts` holds 1 row, of which `count(two_factor_secret)` is 0.
|
|
17
|
+
* `deploy/` carries `example` and `acceptance` only — there is no client
|
|
18
|
+
* deployment and no production database. `down()` therefore re-creates the
|
|
19
|
+
* column's shape and nothing else, because there has never been a value to
|
|
20
|
+
* restore.
|
|
21
|
+
*
|
|
22
|
+
* `two_factor_confirmed_at` deliberately stays: it still feeds a published
|
|
23
|
+
* `twoFactorEnabled` field on six API responses and two admin screens, and
|
|
24
|
+
* what happens to that field is an open owner decision
|
|
25
|
+
* (`specs/087-tenant-scope-enforcement/superseded-2fa-analysis.md` §7).
|
|
26
|
+
*/
|
|
27
|
+
export class Migration20260825T124759CustomerAccountsDropLegacyTwoFactorSecret extends Migration {
|
|
28
|
+
async up() {
|
|
29
|
+
this.addSql(`alter table "customer_accounts" drop column "two_factor_secret";`);
|
|
30
|
+
}
|
|
31
|
+
async down() {
|
|
32
|
+
this.addSql(`alter table "customer_accounts" add column "two_factor_secret" varchar(64) null;`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=20260825T124759_customer_accounts_drop_legacy_two_factor_secret.js.map
|
package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260825T124759_customer_accounts_drop_legacy_two_factor_secret.js","sourceRoot":"","sources":["../../src/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,OAAO,iEAAkE,SAAQ,SAAS;IACrF,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CAAC,kEAAkE,CAAC,CAAC;IAClF,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,kFAAkF,CAAC,CAAC;IAClG,CAAC;CACF"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* D-178 — `customer_accounts.organization_id` becomes `NOT NULL`.
|
|
4
|
+
*
|
|
5
|
+
* The column was relaxed by `20260611T140351_customer_accounts_organization_optional`
|
|
6
|
+
* for feature 026 US2 ("Customer accounts may exist without an Organization"), a
|
|
7
|
+
* design feature 051 replaced and D-178 confirms is dead: every transacting
|
|
8
|
+
* customer is backed by an Organization, an individual by a single-member
|
|
9
|
+
* **personal** one, and there is no "no-organization" scoping path
|
|
10
|
+
* (Constitution XI).
|
|
11
|
+
*
|
|
12
|
+
* **The constraint is the only refusal available.** MikroORM applies its tenant
|
|
13
|
+
* filter to `SELECT` / `UPDATE` / `DELETE` and not to `INSERT`, so no guard can
|
|
14
|
+
* stop a tenant-less account being written; the column can.
|
|
15
|
+
*
|
|
16
|
+
* ## Provision, then count, then refuse — never delete
|
|
17
|
+
*
|
|
18
|
+
* These rows are customer logins: a person's credentials, the owner of their
|
|
19
|
+
* order history and the parent of their address book. A migration that deleted
|
|
20
|
+
* one to satisfy a constraint would be deleting a customer, so this one
|
|
21
|
+
* provisions instead and raises on whatever it could not provision.
|
|
22
|
+
*
|
|
23
|
+
* Feature 051's backfill
|
|
24
|
+
* (`organizations/20260717T151403_organizations_personal_organizations`) ran the
|
|
25
|
+
* same provisioning once. It closed the population as of the moment it ran and
|
|
26
|
+
* closed no write path, so three producers went on filling it: standalone
|
|
27
|
+
* registration's two-flush window, federated sign-in (which provisioned no
|
|
28
|
+
* organisation at all), and the admin "un-assign" button. All three are closed
|
|
29
|
+
* in the merge request that carries this migration — without that, the
|
|
30
|
+
* constraint would convert three silent defects into three runtime failures.
|
|
31
|
+
*
|
|
32
|
+
* Two deliberate differences from that backfill:
|
|
33
|
+
*
|
|
34
|
+
* - **Soft-deleted accounts are included.** 051 skipped them with its own
|
|
35
|
+
* `and ca."deleted_at" is null`. A soft-deleted row still has to satisfy the
|
|
36
|
+
* constraint, and `customer_account.restored` would otherwise resurrect an
|
|
37
|
+
* account the platform cannot scope.
|
|
38
|
+
* - **The insert skips an account whose personal organisation already exists**,
|
|
39
|
+
* and a second statement links it. `tax_id` is derived from the account id
|
|
40
|
+
* and is globally unique, so an account that was provisioned once and then
|
|
41
|
+
* un-assigned would otherwise collide — the migration would abort on a
|
|
42
|
+
* constraint violation rather than on the sentence it wants to say.
|
|
43
|
+
*
|
|
44
|
+
* The provisioning mirrors `PersonalOrganizationService.provisionFor`: name from
|
|
45
|
+
* the trimmed "first last" or else the e-mail local part, `tax_id` the account
|
|
46
|
+
* UUID with the dashes stripped (32 hex characters — an individual has no
|
|
47
|
+
* company tax id), `vat_status = 'vat_exempt'`, `status = 'active'`,
|
|
48
|
+
* `is_personal = true`, and the placeholder registered address the column's
|
|
49
|
+
* `not null` demands.
|
|
50
|
+
*
|
|
51
|
+
* `organizations` is a hard `dependencies` entry of this module's manifest, so
|
|
52
|
+
* that module's tables exist by the time this runs.
|
|
53
|
+
*/
|
|
54
|
+
export declare class Migration20260825T141659CustomerAccountsOrganizationRequired extends Migration {
|
|
55
|
+
up(): Promise<void>;
|
|
56
|
+
down(): Promise<void>;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=20260825T141659_customer_accounts_organization_required.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260825T141659_customer_accounts_organization_required.d.ts","sourceRoot":"","sources":["../../src/migrations/20260825T141659_customer_accounts_organization_required.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,qBAAa,4DAA6D,SAAQ,SAAS;IAC1E,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IA0EnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAOrC"}
|