@endora-commerce/mod-credentials 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/credentials-client.d.ts +11 -0
- package/dist/admin/api/credentials-client.d.ts.map +1 -0
- package/dist/admin/api/credentials-client.js +38 -0
- package/dist/admin/api/credentials-client.js.map +1 -0
- package/dist/admin/components/ConfigurationForm.d.ts +22 -0
- package/dist/admin/components/ConfigurationForm.d.ts.map +1 -0
- package/dist/admin/components/ConfigurationForm.js +89 -0
- package/dist/admin/components/ConfigurationForm.js.map +1 -0
- package/dist/admin/index.d.ts +47 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +65 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/CredentialsNewPage.d.ts +19 -0
- package/dist/admin/pages/CredentialsNewPage.d.ts.map +1 -0
- package/dist/admin/pages/CredentialsNewPage.js +22 -0
- package/dist/admin/pages/CredentialsNewPage.js.map +1 -0
- package/dist/admin/pages/CredentialsPage.d.ts +22 -0
- package/dist/admin/pages/CredentialsPage.d.ts.map +1 -0
- package/dist/admin/pages/CredentialsPage.js +154 -0
- package/dist/admin/pages/CredentialsPage.js.map +1 -0
- package/dist/admin-ui/ConfigurationPreviewModal.d.ts +16 -0
- package/dist/admin-ui/ConfigurationPreviewModal.d.ts.map +1 -0
- package/dist/admin-ui/ConfigurationPreviewModal.js +15 -0
- package/dist/admin-ui/ConfigurationPreviewModal.js.map +1 -0
- package/dist/admin-ui/index.d.ts +57 -0
- package/dist/admin-ui/index.d.ts.map +1 -0
- package/dist/admin-ui/index.js +56 -0
- package/dist/admin-ui/index.js.map +1 -0
- package/dist/backend/commands/create-configuration.command.d.ts +20 -0
- package/dist/backend/commands/create-configuration.command.d.ts.map +1 -0
- package/dist/backend/commands/create-configuration.command.js +70 -0
- package/dist/backend/commands/create-configuration.command.js.map +1 -0
- package/dist/backend/commands/delete-configuration.command.d.ts +16 -0
- package/dist/backend/commands/delete-configuration.command.d.ts.map +1 -0
- package/dist/backend/commands/delete-configuration.command.js +48 -0
- package/dist/backend/commands/delete-configuration.command.js.map +1 -0
- package/dist/backend/commands/update-configuration.command.d.ts +20 -0
- package/dist/backend/commands/update-configuration.command.d.ts.map +1 -0
- package/dist/backend/commands/update-configuration.command.js +94 -0
- package/dist/backend/commands/update-configuration.command.js.map +1 -0
- package/dist/backend/entities/credential-configuration.entity.d.ts +30 -0
- package/dist/backend/entities/credential-configuration.entity.d.ts.map +1 -0
- package/dist/backend/entities/credential-configuration.entity.js +84 -0
- package/dist/backend/entities/credential-configuration.entity.js.map +1 -0
- package/dist/backend/index.d.ts +73 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +80 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/plugin.d.ts +43 -0
- package/dist/backend/plugin.d.ts.map +1 -0
- package/dist/backend/plugin.js +25 -0
- package/dist/backend/plugin.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +27 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +50 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/services/configuration-type-registry.d.ts +105 -0
- package/dist/backend/services/configuration-type-registry.d.ts.map +1 -0
- package/dist/backend/services/configuration-type-registry.js +111 -0
- package/dist/backend/services/configuration-type-registry.js.map +1 -0
- package/dist/backend/services/credentials.service.d.ts +63 -0
- package/dist/backend/services/credentials.service.d.ts.map +1 -0
- package/dist/backend/services/credentials.service.js +128 -0
- package/dist/backend/services/credentials.service.js.map +1 -0
- package/dist/backend/services/field-validator.d.ts +65 -0
- package/dist/backend/services/field-validator.d.ts.map +1 -0
- package/dist/backend/services/field-validator.js +165 -0
- package/dist/backend/services/field-validator.js.map +1 -0
- package/dist/backend/services/registry-singleton.d.ts +28 -0
- package/dist/backend/services/registry-singleton.d.ts.map +1 -0
- package/dist/backend/services/registry-singleton.js +29 -0
- package/dist/backend/services/registry-singleton.js.map +1 -0
- package/dist/backend/services/secret-value-codec.d.ts +55 -0
- package/dist/backend/services/secret-value-codec.d.ts.map +1 -0
- package/dist/backend/services/secret-value-codec.js +105 -0
- package/dist/backend/services/secret-value-codec.js.map +1 -0
- package/dist/backend/types/email-adapter.type.d.ts +10 -0
- package/dist/backend/types/email-adapter.type.d.ts.map +1 -0
- package/dist/backend/types/email-adapter.type.js +49 -0
- package/dist/backend/types/email-adapter.type.js.map +1 -0
- package/dist/backend/types/llm.type.d.ts +3 -0
- package/dist/backend/types/llm.type.d.ts.map +1 -0
- package/dist/backend/types/llm.type.js +35 -0
- package/dist/backend/types/llm.type.js.map +1 -0
- package/dist/manifest.d.ts +176 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +167 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260721T011509_credentials_init.d.ts +17 -0
- package/dist/migrations/20260721T011509_credentials_init.d.ts.map +1 -0
- package/dist/migrations/20260721T011509_credentials_init.js +36 -0
- package/dist/migrations/20260721T011509_credentials_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/credentials.md +202 -0
- package/i18n/en.json +47 -0
- package/i18n/pl.json +47 -0
- package/package.json +98 -0
- package/tailwind.css +16 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { FastifyRequest } from 'fastify';
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
3
|
+
import type { CommandBus } from '@endora-commerce/platform/commands';
|
|
4
|
+
import type { ModuleContext } from '@endora-commerce/platform/kernel';
|
|
5
|
+
import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
|
|
6
|
+
import type { ConfigurationTypeRegistry } from './services/configuration-type-registry.js';
|
|
7
|
+
import { CredentialsService, type CredentialsSettingsPort } from './services/credentials.service.js';
|
|
8
|
+
import { CredentialConfiguration } from './entities/credential-configuration.entity.js';
|
|
9
|
+
/**
|
|
10
|
+
* `credentials` — the module three others read secrets through (feature 072,
|
|
11
|
+
* wave 1).
|
|
12
|
+
*
|
|
13
|
+
* `search`, `newsletter` and `prompt_actions` resolve `credentialsService` for
|
|
14
|
+
* the `credential_ref` path, so it is a **port**: a module reaching for a
|
|
15
|
+
* decrypted secret while `credentials` is absent gets an explicit 503 rather
|
|
16
|
+
* than a service that half-answers.
|
|
17
|
+
*
|
|
18
|
+
* One input stays with the composition root: **`adminContextResolver`** answers
|
|
19
|
+
* "who is this admin", which production and the harness resolve differently.
|
|
20
|
+
* That difference is exactly what the root exists to hold.
|
|
21
|
+
*
|
|
22
|
+
* The secret key is read from the environment here rather than threaded
|
|
23
|
+
* through two roots: it is configuration, and both roots were passing the same
|
|
24
|
+
* `process.env` lookup through identical spread expressions.
|
|
25
|
+
*
|
|
26
|
+
* **`configurationTypeRegistry` came home in T143a.** Both roots registered the
|
|
27
|
+
* name — importing this module's singleton to do it — and then pushed four
|
|
28
|
+
* descriptors into it, two of which are this module's own. Which configuration
|
|
29
|
+
* types exist is decided by which modules a deployment ships, but that is a
|
|
30
|
+
* statement about the *contributors*, not about the registry: each of them
|
|
31
|
+
* declares its own type from its own boot hook now, and the registry is
|
|
32
|
+
* declared here, by the module that owns the class.
|
|
33
|
+
*
|
|
34
|
+
* It is `ctx.di.register` rather than `ctx.di.providePort`, deliberately. A
|
|
35
|
+
* gated port throws `MODULE_DISABLED` on resolution, and the two other
|
|
36
|
+
* contributors resolve this name from `ctx.onBoot` — so gating it would turn
|
|
37
|
+
* "an operator switched `credentials` off" into "the platform does not boot".
|
|
38
|
+
* Nothing leaks by leaving it ungated: a descriptor is inert data, and the
|
|
39
|
+
* service that reads secrets out of it (`credentialsService`) is a port and
|
|
40
|
+
* does fail closed.
|
|
41
|
+
*
|
|
42
|
+
* The instance stays the process-wide singleton rather than becoming one per
|
|
43
|
+
* composition, because it is the documented extension seam an external or
|
|
44
|
+
* overlay module reaches through from an install hook, where no container is
|
|
45
|
+
* available (see `services/registry-singleton.ts`, and
|
|
46
|
+
* `test/integration/credentials/registry-extensibility.test.ts`, which is that
|
|
47
|
+
* path under test).
|
|
48
|
+
*/
|
|
49
|
+
export interface CredentialsCradle {
|
|
50
|
+
readonly emFactory: () => EntityManager;
|
|
51
|
+
readonly commandBus: CommandBus;
|
|
52
|
+
readonly requireAdmin: RequireAdminFactory;
|
|
53
|
+
readonly configurationTypeRegistry: ConfigurationTypeRegistry;
|
|
54
|
+
readonly credentialsSettingsPort: CredentialsSettingsPort | undefined;
|
|
55
|
+
readonly adminContextResolver: (req: FastifyRequest) => {
|
|
56
|
+
adminUserId: string;
|
|
57
|
+
};
|
|
58
|
+
readonly credentialsService: CredentialsService;
|
|
59
|
+
}
|
|
60
|
+
export declare function registerModule(ctx: ModuleContext): void;
|
|
61
|
+
/**
|
|
62
|
+
* The module's persisted entity classes, on the `./backend` subpath, as one
|
|
63
|
+
* array and **no named class export** (D-168).
|
|
64
|
+
*
|
|
65
|
+
* This is the shape the platform reads when the package is *installed*: the
|
|
66
|
+
* boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
|
|
67
|
+
* and the static declaration reader (`scripts/lib/package-declarations.ts`),
|
|
68
|
+
* which is the third source of `check:module-boundary`'s `table→owner` map and
|
|
69
|
+
* the package pass of `check-entity-tenant-classification`. A missing array is
|
|
70
|
+
* answered with `[]` — zero entities registered, no error anywhere.
|
|
71
|
+
*/
|
|
72
|
+
export declare const entities: (typeof CredentialConfiguration)[];
|
|
73
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAErE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAE5E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AAI3F,OAAO,EACL,kBAAkB,EAClB,KAAK,uBAAuB,EAC7B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AAExF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,aAAa,CAAC;IACxC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,QAAQ,CAAC,yBAAyB,EAAE,yBAAyB,CAAC;IAC9D,QAAQ,CAAC,uBAAuB,EAAE,uBAAuB,GAAG,SAAS,CAAC;IACtE,QAAQ,CAAC,oBAAoB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAChF,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;CACjD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAiEvD;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,oCAEpB,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { lazyPort } from '@endora-commerce/platform/kernel';
|
|
2
|
+
import { registerCredentialsAdminRoutes } from './routes.admin.js';
|
|
3
|
+
import { configurationTypeRegistry } from './services/registry-singleton.js';
|
|
4
|
+
import { llmConfigurationType } from './types/llm.type.js';
|
|
5
|
+
import { emailAdapterConfigurationType } from './types/email-adapter.type.js';
|
|
6
|
+
import { CredentialsService, } from './services/credentials.service.js';
|
|
7
|
+
import { CredentialConfiguration } from './entities/credential-configuration.entity.js';
|
|
8
|
+
export function registerModule(ctx) {
|
|
9
|
+
ctx.di.register({
|
|
10
|
+
configurationTypeRegistry: ctx
|
|
11
|
+
.asFunction(() => configurationTypeRegistry)
|
|
12
|
+
.singleton(),
|
|
13
|
+
});
|
|
14
|
+
ctx.di.providePort('credentialsService', ctx
|
|
15
|
+
.asFunction(({ emFactory, commandBus }) => {
|
|
16
|
+
const key = process.env['SETTINGS_SECRET_ENCRYPTION_KEY'];
|
|
17
|
+
// Read at construction, and that is a real constraint rather than an
|
|
18
|
+
// oversight — see `ALLOWED_CAPTURES` in check-port-dependencies.ts.
|
|
19
|
+
// A presence test cannot be deferred: `lazyPort` would hand back a
|
|
20
|
+
// proxy that is always defined, so the "omit the property" branch
|
|
21
|
+
// below could never be taken. Both roots register this port two lines
|
|
22
|
+
// before they resolve `credentialsService`, and that ordering is what
|
|
23
|
+
// makes the read work. It stops being load-bearing when `settings`
|
|
24
|
+
// converts and provides the port itself.
|
|
25
|
+
const settings = ctx.cradle().credentialsSettingsPort;
|
|
26
|
+
// Spread-built so an absent key or port is an **omitted** property
|
|
27
|
+
// rather than an explicit `undefined`, which `exactOptionalPropertyTypes`
|
|
28
|
+
// treats as a different thing.
|
|
29
|
+
return new CredentialsService({
|
|
30
|
+
emFactory,
|
|
31
|
+
commandBus,
|
|
32
|
+
registry: lazyPort(ctx, 'configurationTypeRegistry'),
|
|
33
|
+
...(key === undefined ? {} : { secretEncryptionKey: key }),
|
|
34
|
+
...(settings === undefined ? {} : { settings }),
|
|
35
|
+
});
|
|
36
|
+
})
|
|
37
|
+
.singleton());
|
|
38
|
+
/**
|
|
39
|
+
* This module's own two configuration types (T143a).
|
|
40
|
+
*
|
|
41
|
+
* `ctx.onBoot` rather than a registration: the registry is *read* — by the
|
|
42
|
+
* type catalogue endpoint and by every credential write — and a registration
|
|
43
|
+
* declares without resolving. Boot hooks run once every module has
|
|
44
|
+
* registered and before any request is served, so a type is in the catalogue
|
|
45
|
+
* from the first one.
|
|
46
|
+
*/
|
|
47
|
+
ctx.onBoot(() => {
|
|
48
|
+
const registry = lazyPort(ctx, 'configurationTypeRegistry');
|
|
49
|
+
registry.register(llmConfigurationType);
|
|
50
|
+
registry.register(emailAdapterConfigurationType);
|
|
51
|
+
});
|
|
52
|
+
ctx.routes(async (app) => {
|
|
53
|
+
const { requireAdmin, adminContextResolver } = ctx.cradle();
|
|
54
|
+
await registerCredentialsAdminRoutes(app, {
|
|
55
|
+
// Lazily, even though the module owns this port: route *registration* runs
|
|
56
|
+
// inside `buildServer` whatever the module's effective state is, so
|
|
57
|
+
// destructuring the gate here would stop the next start instead of
|
|
58
|
+
// stopping the routes (D-40). The construction-time read of
|
|
59
|
+
// `credentialsSettingsPort` above is a different thing and stays.
|
|
60
|
+
service: lazyPort(ctx, 'credentialsService'),
|
|
61
|
+
requireAdmin,
|
|
62
|
+
resolveAdminContext: adminContextResolver,
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The module's persisted entity classes, on the `./backend` subpath, as one
|
|
68
|
+
* array and **no named class export** (D-168).
|
|
69
|
+
*
|
|
70
|
+
* This is the shape the platform reads when the package is *installed*: the
|
|
71
|
+
* boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
|
|
72
|
+
* and the static declaration reader (`scripts/lib/package-declarations.ts`),
|
|
73
|
+
* which is the third source of `check:module-boundary`'s `table→owner` map and
|
|
74
|
+
* the package pass of `check-entity-tenant-classification`. A missing array is
|
|
75
|
+
* answered with `[]` — zero entities registered, no error anywhere.
|
|
76
|
+
*/
|
|
77
|
+
export const entities = [
|
|
78
|
+
CredentialConfiguration,
|
|
79
|
+
];
|
|
80
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAE5D,OAAO,EAAE,8BAA8B,EAAE,MAAM,mBAAmB,CAAC;AAEnE,OAAO,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,EACL,kBAAkB,GAEnB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AAqDxF,MAAM,UAAU,cAAc,CAAC,GAAkB;IAC/C,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC;QACd,yBAAyB,EAAE,GAAG;aAC3B,UAAU,CAAC,GAA8B,EAAE,CAAC,yBAAyB,CAAC;aACtE,SAAS,EAAE;KACf,CAAC,CAAC;IAEH,GAAG,CAAC,EAAE,CAAC,WAAW,CAChB,oBAAoB,EACpB,GAAG;SACA,UAAU,CACT,CAAC,EAAE,SAAS,EAAE,UAAU,EAAqB,EAAE,EAAE;QAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;QAC1D,qEAAqE;QACrE,oEAAoE;QACpE,mEAAmE;QACnE,kEAAkE;QAClE,sEAAsE;QACtE,sEAAsE;QACtE,mEAAmE;QACnE,yCAAyC;QACzC,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAqB,CAAC,uBAAuB,CAAC;QACzE,mEAAmE;QACnE,0EAA0E;QAC1E,+BAA+B;QAC/B,OAAO,IAAI,kBAAkB,CAAC;YAC5B,SAAS;YACT,UAAU;YACV,QAAQ,EAAE,QAAQ,CAA4B,GAAG,EAAE,2BAA2B,CAAC;YAC/E,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,mBAAmB,EAAE,GAAG,EAAE,CAAC;YAC1D,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;SACO,CAAC,CAAC;IAC5D,CAAC,CACF;SACA,SAAS,EAAE,CACf,CAAC;IAEF;;;;;;;;OAQG;IACH,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE;QACd,MAAM,QAAQ,GAAG,QAAQ,CAA4B,GAAG,EAAE,2BAA2B,CAAC,CAAC;QACvF,QAAQ,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC;QACxC,QAAQ,CAAC,QAAQ,CAAC,6BAA6B,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACvB,MAAM,EAAE,YAAY,EAAE,oBAAoB,EAAE,GAAG,GAAG,CAAC,MAAM,EAAqB,CAAC;QAC/E,MAAM,8BAA8B,CAAC,GAAG,EAAE;YACxC,2EAA2E;YAC3E,oEAAoE;YACpE,mEAAmE;YACnE,4DAA4D;YAC5D,kEAAkE;YAClE,OAAO,EAAE,QAAQ,CAAqB,GAAG,EAAE,oBAAoB,CAAC;YAChE,YAAY;YACZ,mBAAmB,EAAE,oBAAoB;SAC1C,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,uBAAuB;CACxB,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyRequest } from 'fastify';
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
3
|
+
import type { CommandBus } from '@endora-commerce/platform/commands';
|
|
4
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
5
|
+
import type { ConfigurationTypeRegistry } from './services/configuration-type-registry.js';
|
|
6
|
+
import { CredentialsService, type CredentialsSettingsPort } from './services/credentials.service.js';
|
|
7
|
+
import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
|
|
8
|
+
/**
|
|
9
|
+
* Composition root for the credentials module — feature 058.
|
|
10
|
+
*
|
|
11
|
+
* The module owns a `credential_configurations` table and reads configuration
|
|
12
|
+
* types from the process-wide `ConfigurationTypeRegistry` (the cross-module
|
|
13
|
+
* extension seam). Core types (LLM, email adapter) are registered in
|
|
14
|
+
* `composition.ts` at boot (US1), never here.
|
|
15
|
+
*/
|
|
16
|
+
export type { CredentialsSettingsPort };
|
|
17
|
+
export interface CredentialsModuleOptions {
|
|
18
|
+
emFactory: () => EntityManager;
|
|
19
|
+
/** The settings port backing the delete-integrity guard (Principle I, FR-012). */
|
|
20
|
+
settings?: CredentialsSettingsPort;
|
|
21
|
+
permissionService: {
|
|
22
|
+
hasPermission(adminUserId: string, permission: string): Promise<boolean>;
|
|
23
|
+
};
|
|
24
|
+
requireAdmin: RequireAdminFactory;
|
|
25
|
+
resolveAdminContext: (req: FastifyRequest) => {
|
|
26
|
+
adminUserId: string;
|
|
27
|
+
};
|
|
28
|
+
commandBus: CommandBus;
|
|
29
|
+
configurationTypeRegistry: ConfigurationTypeRegistry;
|
|
30
|
+
auditLogService?: AuditPort;
|
|
31
|
+
/** Base64 AES-256 key for secret fields; shared with settings (research §R4). */
|
|
32
|
+
secretEncryptionKey?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface CredentialsModuleHandle {
|
|
35
|
+
configurationTypeRegistry: ConfigurationTypeRegistry;
|
|
36
|
+
service: CredentialsService;
|
|
37
|
+
}
|
|
38
|
+
export interface CredentialsModuleResult {
|
|
39
|
+
plugin: (app: FastifyInstance) => Promise<void>;
|
|
40
|
+
handle: CredentialsModuleHandle;
|
|
41
|
+
}
|
|
42
|
+
export declare function credentialsModule(options: CredentialsModuleOptions): CredentialsModuleResult;
|
|
43
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AAC3F,OAAO,EACL,kBAAkB,EAClB,KAAK,uBAAuB,EAC7B,MAAM,mCAAmC,CAAC;AAI3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAE5E;;;;;;;GAOG;AAEH,YAAY,EAAE,uBAAuB,EAAE,CAAC;AAExC,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,kFAAkF;IAClF,QAAQ,CAAC,EAAE,uBAAuB,CAAC;IACnC,iBAAiB,EAAE;QACjB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KAC1E,CAAC;IACF,YAAY,EAAE,mBAAmB,CAAC;IAClC,mBAAmB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,UAAU,EAAE,UAAU,CAAC;IACvB,yBAAyB,EAAE,yBAAyB,CAAC;IACrD,eAAe,CAAC,EAAE,SAAS,CAAC;IAC5B,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,uBAAuB;IACtC,yBAAyB,EAAE,yBAAyB,CAAC;IACrD,OAAO,EAAE,kBAAkB,CAAC;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,EAAE,uBAAuB,CAAC;CACjC;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,uBAAuB,CAsB5F"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CredentialsService, } from './services/credentials.service.js';
|
|
2
|
+
import { registerCredentialsAdminRoutes, } from './routes.admin.js';
|
|
3
|
+
export function credentialsModule(options) {
|
|
4
|
+
const service = new CredentialsService({
|
|
5
|
+
emFactory: options.emFactory,
|
|
6
|
+
commandBus: options.commandBus,
|
|
7
|
+
registry: options.configurationTypeRegistry,
|
|
8
|
+
secretEncryptionKey: options.secretEncryptionKey,
|
|
9
|
+
...(options.settings ? { settings: options.settings } : {}),
|
|
10
|
+
});
|
|
11
|
+
return {
|
|
12
|
+
handle: {
|
|
13
|
+
configurationTypeRegistry: options.configurationTypeRegistry,
|
|
14
|
+
service,
|
|
15
|
+
},
|
|
16
|
+
plugin: async (app) => {
|
|
17
|
+
await registerCredentialsAdminRoutes(app, {
|
|
18
|
+
service,
|
|
19
|
+
requireAdmin: options.requireAdmin,
|
|
20
|
+
resolveAdminContext: options.resolveAdminContext,
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,kBAAkB,GAEnB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,8BAA8B,GAC/B,MAAM,mBAAmB,CAAC;AAwC3B,MAAM,UAAU,iBAAiB,CAAC,OAAiC;IACjE,MAAM,OAAO,GAAG,IAAI,kBAAkB,CAAC;QACrC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,QAAQ,EAAE,OAAO,CAAC,yBAAyB;QAC3C,mBAAmB,EAAE,OAAO,CAAC,mBAAmB;QAChD,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5D,CAAC,CAAC;IAEH,OAAO;QACL,MAAM,EAAE;YACN,yBAAyB,EAAE,OAAO,CAAC,yBAAyB;YAC5D,OAAO;SACR;QACD,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACpB,MAAM,8BAA8B,CAAC,GAAG,EAAE;gBACxC,OAAO;gBACP,YAAY,EAAE,OAAO,CAAC,YAAY;gBAClC,mBAAmB,EAAE,OAAO,CAAC,mBAAmB;aACjD,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyRequest } from 'fastify';
|
|
2
|
+
import type { CredentialsService } from './services/credentials.service.js';
|
|
3
|
+
import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
|
|
4
|
+
/**
|
|
5
|
+
* Admin HTTP surface for the credentials module — feature 058
|
|
6
|
+
* (contracts/admin-api.md).
|
|
7
|
+
*
|
|
8
|
+
* GET /api/v1/admin/credentials/types
|
|
9
|
+
* GET /api/v1/admin/credentials (?type=)
|
|
10
|
+
* GET /api/v1/admin/credentials/:code
|
|
11
|
+
* POST /api/v1/admin/credentials
|
|
12
|
+
* PUT /api/v1/admin/credentials/:code
|
|
13
|
+
* DELETE /api/v1/admin/credentials/:code
|
|
14
|
+
* GET /api/v1/admin/credentials/:code/preview
|
|
15
|
+
*
|
|
16
|
+
* Reads gated by `credentials:read`, writes by `credentials:write`. Secrets are
|
|
17
|
+
* masked on every read; only the server-side `resolve` (US2) decrypts.
|
|
18
|
+
*/
|
|
19
|
+
export interface CredentialsRoutesDeps {
|
|
20
|
+
service: CredentialsService;
|
|
21
|
+
requireAdmin: RequireAdminFactory;
|
|
22
|
+
resolveAdminContext: (req: FastifyRequest) => {
|
|
23
|
+
adminUserId: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export declare function registerCredentialsAdminRoutes(app: FastifyInstance, deps: CredentialsRoutesDeps): Promise<void>;
|
|
27
|
+
//# 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":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAS/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAE5E;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,YAAY,EAAE,mBAAmB,CAAC;IAClC,mBAAmB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CACvE;AAED,wBAAsB,8BAA8B,CAClD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAwEf"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ConfigurationListQuerySchema, CreateConfigurationSchema, ERROR_CODES, UpdateConfigurationSchema, } from '@endora-commerce/contracts';
|
|
2
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
3
|
+
import { CREDENTIALS_READ_PERMISSION, CREDENTIALS_WRITE_PERMISSION } from '../manifest.js';
|
|
4
|
+
export async function registerCredentialsAdminRoutes(app, deps) {
|
|
5
|
+
const { service, requireAdmin } = deps;
|
|
6
|
+
const readGuard = requireAdmin(CREDENTIALS_READ_PERMISSION);
|
|
7
|
+
const writeGuard = requireAdmin(CREDENTIALS_WRITE_PERMISSION);
|
|
8
|
+
app.get('/api/v1/admin/credentials/types', { preHandler: readGuard }, async () => ({
|
|
9
|
+
types: service.describeTypes(),
|
|
10
|
+
}));
|
|
11
|
+
app.get('/api/v1/admin/credentials', { preHandler: readGuard }, async (request) => {
|
|
12
|
+
const q = ConfigurationListQuerySchema.parse(request.query ?? {});
|
|
13
|
+
return { configurations: await service.list(q.type) };
|
|
14
|
+
});
|
|
15
|
+
app.get('/api/v1/admin/credentials/:code', { preHandler: readGuard }, async (request) => {
|
|
16
|
+
const dto = await service.getByCode(request.params.code);
|
|
17
|
+
if (!dto) {
|
|
18
|
+
throw new HttpError(404, ERROR_CODES.CREDENTIAL_NOT_FOUND, 'Configuration not found.');
|
|
19
|
+
}
|
|
20
|
+
return dto;
|
|
21
|
+
});
|
|
22
|
+
// Preview target for the settings-field preview button — same masked shape as
|
|
23
|
+
// GET /:code (documented separately in the contract, served identically).
|
|
24
|
+
app.get('/api/v1/admin/credentials/:code/preview', { preHandler: readGuard }, async (request) => {
|
|
25
|
+
const dto = await service.getByCode(request.params.code);
|
|
26
|
+
if (!dto) {
|
|
27
|
+
throw new HttpError(404, ERROR_CODES.CREDENTIAL_NOT_FOUND, 'Configuration not found.');
|
|
28
|
+
}
|
|
29
|
+
return dto;
|
|
30
|
+
});
|
|
31
|
+
app.post('/api/v1/admin/credentials', { preHandler: writeGuard }, async (request, reply) => {
|
|
32
|
+
const body = CreateConfigurationSchema.parse(request.body);
|
|
33
|
+
const dto = await service.create(body);
|
|
34
|
+
return reply.code(201).send(dto);
|
|
35
|
+
});
|
|
36
|
+
app.put('/api/v1/admin/credentials/:code', { preHandler: writeGuard }, async (request) => {
|
|
37
|
+
// `typeCode` / `providerCode` are immutable after create (edge case).
|
|
38
|
+
const raw = (request.body ?? {});
|
|
39
|
+
if ('typeCode' in raw || 'providerCode' in raw) {
|
|
40
|
+
throw new HttpError(422, ERROR_CODES.CREDENTIAL_TYPE_IMMUTABLE, 'The type and provider cannot be changed after creation — create a new configuration instead.');
|
|
41
|
+
}
|
|
42
|
+
const body = UpdateConfigurationSchema.parse(request.body);
|
|
43
|
+
return service.update(request.params.code, body);
|
|
44
|
+
});
|
|
45
|
+
app.delete('/api/v1/admin/credentials/:code', { preHandler: writeGuard }, async (request, reply) => {
|
|
46
|
+
await service.delete(request.params.code);
|
|
47
|
+
return reply.code(204).send();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=routes.admin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.admin.js","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { ConfigurationTypeDescriptor, ConfigurationTypeRegistryPort } from '@endora-commerce/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* ConfigurationTypeRegistry (feature 058) — in-memory map of configuration-type
|
|
4
|
+
* code → descriptor. This is the documented extension point by which the
|
|
5
|
+
* platform and other/overlay modules contribute configuration types WITHOUT
|
|
6
|
+
* editing the credentials core (Principle XIV / XV).
|
|
7
|
+
*
|
|
8
|
+
* Shape copied from the payment/shipping adapter-registry pattern
|
|
9
|
+
* (`payment_methods/services/payment-adapter-registry.ts`): `register` / `get` /
|
|
10
|
+
* `resolve` / `list`, last-writer-wins + warn on a duplicate code so a
|
|
11
|
+
* re-registration during a hot reload or re-enable does not throw.
|
|
12
|
+
*
|
|
13
|
+
* The credentials service reads a descriptor only to (a) render fields, (b)
|
|
14
|
+
* derive the write-validator, (c) know which fields are `secret`. It MUST NOT
|
|
15
|
+
* branch on a specific `code` / `providerCode` for business meaning — provider
|
|
16
|
+
* meaning lives with the consumer (Principle XIV).
|
|
17
|
+
*
|
|
18
|
+
* **The absent-contributor policy this registry states (issue #129): skip.**
|
|
19
|
+
* The push is ungated — a contributor declares its type from a boot hook, which
|
|
20
|
+
* runs whatever the module's effective state is, and gating the push would turn
|
|
21
|
+
* a deactivation into a permanently missing type — so the presence question is
|
|
22
|
+
* answered here, at enumeration, keyed on `descriptor.ownerModule`.
|
|
23
|
+
*
|
|
24
|
+
* A configuration type is a *surface*: it is what the credentials screen offers
|
|
25
|
+
* to configure and what a write is validated against, so a capability an
|
|
26
|
+
* operator switched off must not be on offer and must not be creatable
|
|
27
|
+
* (Principle XVII). `get`, `resolve`, `list` and `describe` therefore answer as
|
|
28
|
+
* if an absent contributor's type were not registered.
|
|
29
|
+
*
|
|
30
|
+
* `entry`, `ownerOf`, `isRegistered` and `listAll` deliberately do not filter.
|
|
31
|
+
* Switching a module off is not uninstalling it: the configurations an operator
|
|
32
|
+
* already stored keep their rows, so the paths that *render* one and the paths
|
|
33
|
+
* that redact one into an audit snapshot need the field shape whatever the
|
|
34
|
+
* contributor's state, and the admin surface needs the owner's id to say which
|
|
35
|
+
* module is off. Dropping the descriptor there would report a stored credential
|
|
36
|
+
* as having lost its type — the state `unregister` produces, and a different
|
|
37
|
+
* thing entirely.
|
|
38
|
+
*
|
|
39
|
+
* The probe is deliberately **tri-state**, and that is not a convenience: this
|
|
40
|
+
* registry is the seam an overlay or external module pushes a type through, so
|
|
41
|
+
* `ownerModule` may be a string no manifest declares. `isPresent` collapses
|
|
42
|
+
* "switched off" and "not a module" into one `false` — right for a gating seam,
|
|
43
|
+
* wrong here, where it would delete the extension point. `undefined` means the
|
|
44
|
+
* platform says nothing about that id, and the entry is honoured.
|
|
45
|
+
*/
|
|
46
|
+
export interface RegistryLogger {
|
|
47
|
+
warn(message: string): void;
|
|
48
|
+
}
|
|
49
|
+
/** Thrown by `resolve` when a type code is not registered (maps to 400). */
|
|
50
|
+
export declare class ConfigurationTypeUnknown extends Error {
|
|
51
|
+
readonly name = "ConfigurationTypeUnknown";
|
|
52
|
+
readonly code: "CREDENTIAL_TYPE_UNKNOWN";
|
|
53
|
+
constructor(typeCode: string);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Whether the platform holds `moduleId` present. `undefined` when the id is not
|
|
57
|
+
* a module the platform knows anything about — see the class doc comment.
|
|
58
|
+
*/
|
|
59
|
+
export type ModulePresenceProbe = (moduleId: string) => boolean | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* `implements` the shape feature 075's Phase P published — the two integration
|
|
62
|
+
* modules read the published type, and `tsc` refuses the day a method here
|
|
63
|
+
* stops matching.
|
|
64
|
+
*/
|
|
65
|
+
export declare class ConfigurationTypeRegistry implements ConfigurationTypeRegistryPort {
|
|
66
|
+
private readonly log;
|
|
67
|
+
private readonly presenceOfModule;
|
|
68
|
+
private readonly types;
|
|
69
|
+
/**
|
|
70
|
+
* @param log duplicate-code warnings.
|
|
71
|
+
* @param presenceOfModule the effective-state probe. Defaults to "the platform
|
|
72
|
+
* says nothing", so a registry built for a unit test or a bare harness
|
|
73
|
+
* behaves exactly as it did before this filter existed; the process
|
|
74
|
+
* singleton wires it to the kernel's effective state.
|
|
75
|
+
*/
|
|
76
|
+
constructor(log?: RegistryLogger, presenceOfModule?: ModulePresenceProbe);
|
|
77
|
+
register(descriptor: ConfigurationTypeDescriptor): void;
|
|
78
|
+
unregister(code: string): void;
|
|
79
|
+
/** Whether any module has contributed this code — presence-blind. */
|
|
80
|
+
isRegistered(code: string): boolean;
|
|
81
|
+
/** The contributed descriptor, presence-blind. Admin rendering and audit read this. */
|
|
82
|
+
entry(code: string): ConfigurationTypeDescriptor | undefined;
|
|
83
|
+
/** The module that contributed `code`, or `null` when nobody did. */
|
|
84
|
+
ownerOf(code: string): string | null;
|
|
85
|
+
/** Registered AND its contributor effectively present. */
|
|
86
|
+
isAvailable(code: string): boolean;
|
|
87
|
+
/** Every contributed descriptor, presence-blind (stable order of insertion). */
|
|
88
|
+
listAll(): ConfigurationTypeDescriptor[];
|
|
89
|
+
/** The descriptor, or `undefined` when unregistered or its contributor is absent. */
|
|
90
|
+
get(code: string): ConfigurationTypeDescriptor | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* The descriptor, or a throw. An unregistered code is a client error and stays
|
|
93
|
+
* {@link ConfigurationTypeUnknown}; a registered one whose contributor is
|
|
94
|
+
* absent is the ordinary 503 envelope, so an operator is told which module is
|
|
95
|
+
* off rather than that a type they can see has become unknown.
|
|
96
|
+
*/
|
|
97
|
+
resolve(code: string): ConfigurationTypeDescriptor;
|
|
98
|
+
/** Descriptors whose contributor is present (stable order of insertion). */
|
|
99
|
+
list(): ConfigurationTypeDescriptor[];
|
|
100
|
+
/** Admin-safe view powering `GET /api/v1/admin/credentials/types`. */
|
|
101
|
+
describe(): ConfigurationTypeDescriptor[];
|
|
102
|
+
/** Absent only when the platform knows the contributor and holds it off. */
|
|
103
|
+
private contributorAbsent;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=configuration-type-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configuration-type-registry.d.ts","sourceRoot":"","sources":["../../../src/backend/services/configuration-type-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,2BAA2B,EAC3B,6BAA6B,EAC9B,MAAM,4BAA4B,CAAC;AAIpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAMD,4EAA4E;AAC5E,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,SAAkB,IAAI,8BAA8B;IACpD,QAAQ,CAAC,IAAI,4BAAuC;gBACxC,QAAQ,EAAE,MAAM;CAG7B;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAAC;AAE5E;;;;GAIG;AACH,qBAAa,yBAA0B,YAAW,6BAA6B;IAW3E,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAXnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkD;IAExE;;;;;;OAMG;gBAEgB,GAAG,GAAE,cAA8B,EACnC,gBAAgB,GAAE,mBAAqC;IAG1E,QAAQ,CAAC,UAAU,EAAE,2BAA2B,GAAG,IAAI;IAmBvD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI9B,qEAAqE;IACrE,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAInC,uFAAuF;IACvF,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,2BAA2B,GAAG,SAAS;IAI5D,qEAAqE;IACrE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIpC,0DAA0D;IAC1D,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAKlC,gFAAgF;IAChF,OAAO,IAAI,2BAA2B,EAAE;IAIxC,qFAAqF;IACrF,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,2BAA2B,GAAG,SAAS;IAM1D;;;;;OAKG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,2BAA2B;IAWlD,4EAA4E;IAC5E,IAAI,IAAI,2BAA2B,EAAE;IAIrC,sEAAsE;IACtE,QAAQ,IAAI,2BAA2B,EAAE;IAIzC,4EAA4E;IAC5E,OAAO,CAAC,iBAAiB;CAG1B"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { ERROR_CODES } from '@endora-commerce/contracts';
|
|
2
|
+
import { ModuleDisabledError } from '@endora-commerce/platform/kernel';
|
|
3
|
+
const consoleLogger = {
|
|
4
|
+
warn: (message) => console.warn(message),
|
|
5
|
+
};
|
|
6
|
+
/** Thrown by `resolve` when a type code is not registered (maps to 400). */
|
|
7
|
+
export class ConfigurationTypeUnknown extends Error {
|
|
8
|
+
name = 'ConfigurationTypeUnknown';
|
|
9
|
+
code = ERROR_CODES.CREDENTIAL_TYPE_UNKNOWN;
|
|
10
|
+
constructor(typeCode) {
|
|
11
|
+
super(`ConfigurationTypeRegistry: no configuration type registered for code "${typeCode}".`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* `implements` the shape feature 075's Phase P published — the two integration
|
|
16
|
+
* modules read the published type, and `tsc` refuses the day a method here
|
|
17
|
+
* stops matching.
|
|
18
|
+
*/
|
|
19
|
+
export class ConfigurationTypeRegistry {
|
|
20
|
+
log;
|
|
21
|
+
presenceOfModule;
|
|
22
|
+
types = new Map();
|
|
23
|
+
/**
|
|
24
|
+
* @param log duplicate-code warnings.
|
|
25
|
+
* @param presenceOfModule the effective-state probe. Defaults to "the platform
|
|
26
|
+
* says nothing", so a registry built for a unit test or a bare harness
|
|
27
|
+
* behaves exactly as it did before this filter existed; the process
|
|
28
|
+
* singleton wires it to the kernel's effective state.
|
|
29
|
+
*/
|
|
30
|
+
constructor(log = consoleLogger, presenceOfModule = () => undefined) {
|
|
31
|
+
this.log = log;
|
|
32
|
+
this.presenceOfModule = presenceOfModule;
|
|
33
|
+
}
|
|
34
|
+
register(descriptor) {
|
|
35
|
+
const existing = this.types.get(descriptor.code);
|
|
36
|
+
// Re-registering the *same* descriptor overwrites nothing, so it warns
|
|
37
|
+
// about nothing. That distinction started to matter in T143a, when the four
|
|
38
|
+
// core registrations moved out of the composition roots and into the boot
|
|
39
|
+
// hooks of the modules that own the types: this registry is process-wide
|
|
40
|
+
// (it is the seam an overlay module's install hook imports), while a test
|
|
41
|
+
// run composes it several hundred times, so each of those hooks re-declares
|
|
42
|
+
// an identical descriptor once per composition. A genuine override — a
|
|
43
|
+
// different descriptor under a code someone already claimed — still warns.
|
|
44
|
+
if (existing === descriptor)
|
|
45
|
+
return;
|
|
46
|
+
if (existing !== undefined) {
|
|
47
|
+
this.log.warn(`ConfigurationTypeRegistry: type "${descriptor.code}" re-registered; overwriting previous registration.`);
|
|
48
|
+
}
|
|
49
|
+
this.types.set(descriptor.code, descriptor);
|
|
50
|
+
}
|
|
51
|
+
unregister(code) {
|
|
52
|
+
this.types.delete(code);
|
|
53
|
+
}
|
|
54
|
+
/** Whether any module has contributed this code — presence-blind. */
|
|
55
|
+
isRegistered(code) {
|
|
56
|
+
return this.types.has(code);
|
|
57
|
+
}
|
|
58
|
+
/** The contributed descriptor, presence-blind. Admin rendering and audit read this. */
|
|
59
|
+
entry(code) {
|
|
60
|
+
return this.types.get(code);
|
|
61
|
+
}
|
|
62
|
+
/** The module that contributed `code`, or `null` when nobody did. */
|
|
63
|
+
ownerOf(code) {
|
|
64
|
+
return this.types.get(code)?.ownerModule ?? null;
|
|
65
|
+
}
|
|
66
|
+
/** Registered AND its contributor effectively present. */
|
|
67
|
+
isAvailable(code) {
|
|
68
|
+
const descriptor = this.types.get(code);
|
|
69
|
+
return descriptor !== undefined && !this.contributorAbsent(descriptor);
|
|
70
|
+
}
|
|
71
|
+
/** Every contributed descriptor, presence-blind (stable order of insertion). */
|
|
72
|
+
listAll() {
|
|
73
|
+
return [...this.types.values()];
|
|
74
|
+
}
|
|
75
|
+
/** The descriptor, or `undefined` when unregistered or its contributor is absent. */
|
|
76
|
+
get(code) {
|
|
77
|
+
const descriptor = this.types.get(code);
|
|
78
|
+
if (!descriptor || this.contributorAbsent(descriptor))
|
|
79
|
+
return undefined;
|
|
80
|
+
return descriptor;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The descriptor, or a throw. An unregistered code is a client error and stays
|
|
84
|
+
* {@link ConfigurationTypeUnknown}; a registered one whose contributor is
|
|
85
|
+
* absent is the ordinary 503 envelope, so an operator is told which module is
|
|
86
|
+
* off rather than that a type they can see has become unknown.
|
|
87
|
+
*/
|
|
88
|
+
resolve(code) {
|
|
89
|
+
const descriptor = this.types.get(code);
|
|
90
|
+
if (!descriptor) {
|
|
91
|
+
throw new ConfigurationTypeUnknown(code);
|
|
92
|
+
}
|
|
93
|
+
if (this.contributorAbsent(descriptor)) {
|
|
94
|
+
throw new ModuleDisabledError(descriptor.ownerModule);
|
|
95
|
+
}
|
|
96
|
+
return descriptor;
|
|
97
|
+
}
|
|
98
|
+
/** Descriptors whose contributor is present (stable order of insertion). */
|
|
99
|
+
list() {
|
|
100
|
+
return this.listAll().filter((descriptor) => !this.contributorAbsent(descriptor));
|
|
101
|
+
}
|
|
102
|
+
/** Admin-safe view powering `GET /api/v1/admin/credentials/types`. */
|
|
103
|
+
describe() {
|
|
104
|
+
return this.list();
|
|
105
|
+
}
|
|
106
|
+
/** Absent only when the platform knows the contributor and holds it off. */
|
|
107
|
+
contributorAbsent(descriptor) {
|
|
108
|
+
return this.presenceOfModule(descriptor.ownerModule) === false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=configuration-type-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configuration-type-registry.js","sourceRoot":"","sources":["../../../src/backend/services/configuration-type-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import { type ConfigurationDto, type ConfigurationReference, type ConfigurationTypeDescriptor, type CreateConfiguration, type ResolveResult, type UpdateConfiguration } from '@endora-commerce/contracts';
|
|
3
|
+
import type { CommandBus } from '@endora-commerce/platform/commands';
|
|
4
|
+
import type { ConfigurationTypeRegistry } from './configuration-type-registry.js';
|
|
5
|
+
/**
|
|
6
|
+
* Narrow settings port — the ONLY channel through which credentials reaches the
|
|
7
|
+
* settings module (Principle I). Backs the delete-integrity lookup (FR-012).
|
|
8
|
+
*/
|
|
9
|
+
export interface CredentialsSettingsPort {
|
|
10
|
+
listReferencesToConfiguration(configurationCode: string): Promise<ConfigurationReference[]>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* CredentialsService (feature 058) — the module's read + orchestration surface.
|
|
14
|
+
*
|
|
15
|
+
* All writes go through the Command Bus (Principle XIII); every read returns a
|
|
16
|
+
* MASKED DTO (secrets never leave as plaintext, SC-003 / FR-005). A configuration
|
|
17
|
+
* whose `typeCode` is no longer registered is surfaced `inert` (read-only,
|
|
18
|
+
* FR-016).
|
|
19
|
+
*
|
|
20
|
+
* Principle XIV — the core is type-agnostic: it reads a descriptor ONLY to
|
|
21
|
+
* render fields, derive the write-validator, and learn which fields are
|
|
22
|
+
* `secret`. It MUST NOT branch on a specific `typeCode`/`providerCode` for
|
|
23
|
+
* business meaning (provider meaning lives with the consumer). This invariant
|
|
24
|
+
* is grep-asserted in `test/integration/credentials/registry-extensibility.test.ts`.
|
|
25
|
+
*/
|
|
26
|
+
export interface CredentialsServiceDeps {
|
|
27
|
+
emFactory: () => EntityManager;
|
|
28
|
+
commandBus: CommandBus;
|
|
29
|
+
registry: ConfigurationTypeRegistry;
|
|
30
|
+
secretEncryptionKey: string | undefined;
|
|
31
|
+
/** Wired in US2 — backs the delete-integrity guard (FR-012). */
|
|
32
|
+
settings?: CredentialsSettingsPort;
|
|
33
|
+
}
|
|
34
|
+
export declare class CredentialsService {
|
|
35
|
+
private readonly deps;
|
|
36
|
+
constructor(deps: CredentialsServiceDeps);
|
|
37
|
+
/** Registered configuration types powering the admin type picker + form. */
|
|
38
|
+
describeTypes(): ConfigurationTypeDescriptor[];
|
|
39
|
+
list(typeFilter?: string): Promise<ConfigurationDto[]>;
|
|
40
|
+
getByCode(code: string): Promise<ConfigurationDto | null>;
|
|
41
|
+
create(data: CreateConfiguration): Promise<ConfigurationDto>;
|
|
42
|
+
update(code: string, data: UpdateConfiguration): Promise<ConfigurationDto>;
|
|
43
|
+
delete(code: string): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Server-side resolution (feature 058 US2) — the single path that returns
|
|
46
|
+
* DECRYPTED secret values, for a consumer's in-memory use only. Never exposed
|
|
47
|
+
* over HTTP. Fails closed: an unset reference is `not_configured`; a
|
|
48
|
+
* deleted/inert one is `unavailable` — never a foreign configuration
|
|
49
|
+
* (FR-012 / SC-005).
|
|
50
|
+
*/
|
|
51
|
+
resolve(configurationCode: string): Promise<ResolveResult>;
|
|
52
|
+
/**
|
|
53
|
+
* Map an entity to a masked DTO, marking unregistered-type rows as inert.
|
|
54
|
+
*
|
|
55
|
+
* The registry's **diagnostic** read (issue #129): a stored configuration
|
|
56
|
+
* whose contributing module is merely switched off keeps its label and its
|
|
57
|
+
* field shape, because deactivation drops no data and an inert row would say
|
|
58
|
+
* the type is gone. `resolve` above stays on the acting read, so nothing
|
|
59
|
+
* decrypted leaves for the same configuration.
|
|
60
|
+
*/
|
|
61
|
+
private toDto;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=credentials.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/credentials.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EAChC,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAGrE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAOlF;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC,6BAA6B,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAAC;CAC7F;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,yBAAyB,CAAC;IACpC,mBAAmB,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,uBAAuB,CAAC;CACpC;AAED,qBAAa,kBAAkB;IACjB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,sBAAsB;IAEzD,4EAA4E;IAC5E,aAAa,IAAI,2BAA2B,EAAE;IAIxC,IAAI,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAOtD,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAMzD,MAAM,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAW5D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAY1E,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBzC;;;;;;OAMG;IACG,OAAO,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAgChE;;;;;;;;OAQG;IACH,OAAO,CAAC,KAAK;CAoBd"}
|