@open-rlb/nestjs-amqp 2.1.1 → 2.2.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/package.json +6 -5
- package/schematics/add-auth-provider/index.d.ts +3 -0
- package/schematics/add-auth-provider/index.js +60 -0
- package/schematics/add-auth-provider/index.js.map +1 -0
- package/schematics/add-auth-provider/index.spec.ts +83 -0
- package/schematics/add-auth-provider/index.ts +83 -0
- package/schematics/add-auth-provider/schema.d.ts +18 -0
- package/schematics/add-auth-provider/schema.js +3 -0
- package/schematics/add-auth-provider/schema.js.map +1 -0
- package/schematics/add-auth-provider/schema.json +59 -0
- package/schematics/add-auth-provider/schema.ts +35 -0
- package/schematics/add-exchange/index.d.ts +3 -0
- package/schematics/add-exchange/index.js +49 -0
- package/schematics/add-exchange/index.js.map +1 -0
- package/schematics/add-exchange/index.spec.ts +65 -0
- package/schematics/add-exchange/index.ts +65 -0
- package/schematics/add-exchange/schema.d.ts +14 -0
- package/schematics/add-exchange/schema.js +3 -0
- package/schematics/add-exchange/schema.js.map +1 -0
- package/schematics/add-exchange/schema.json +50 -0
- package/schematics/add-exchange/schema.ts +26 -0
- package/schematics/add-exchange-binding/index.d.ts +3 -0
- package/schematics/add-exchange-binding/index.js +53 -0
- package/schematics/add-exchange-binding/index.js.map +1 -0
- package/schematics/add-exchange-binding/index.spec.ts +73 -0
- package/schematics/add-exchange-binding/index.ts +64 -0
- package/schematics/add-exchange-binding/schema.d.ts +8 -0
- package/schematics/add-exchange-binding/schema.js +3 -0
- package/schematics/add-exchange-binding/schema.js.map +1 -0
- package/schematics/add-exchange-binding/schema.json +36 -0
- package/schematics/add-exchange-binding/schema.ts +14 -0
- package/schematics/add-gateway/index.d.ts +4 -0
- package/schematics/add-gateway/index.js +105 -0
- package/schematics/add-gateway/index.js.map +1 -0
- package/schematics/add-gateway/index.spec.ts +163 -0
- package/schematics/add-gateway/index.ts +141 -0
- package/schematics/add-gateway/paths.d.ts +12 -0
- package/schematics/add-gateway/paths.js +87 -0
- package/schematics/add-gateway/paths.js.map +1 -0
- package/schematics/add-gateway/paths.ts +103 -0
- package/schematics/add-gateway/schema.d.ts +13 -0
- package/schematics/add-gateway/schema.js +3 -0
- package/schematics/add-gateway/schema.js.map +1 -0
- package/schematics/add-gateway/schema.json +63 -0
- package/schematics/add-gateway/schema.ts +24 -0
- package/schematics/add-gateway/wiring.d.ts +14 -0
- package/schematics/add-gateway/wiring.js +102 -0
- package/schematics/add-gateway/wiring.js.map +1 -0
- package/schematics/add-gateway/wiring.ts +115 -0
- package/schematics/add-metrics-topic/index.d.ts +3 -0
- package/schematics/add-metrics-topic/index.js +31 -0
- package/schematics/add-metrics-topic/index.js.map +1 -0
- package/schematics/add-metrics-topic/index.spec.ts +79 -0
- package/schematics/add-metrics-topic/index.ts +49 -0
- package/schematics/add-metrics-topic/schema.d.ts +10 -0
- package/schematics/add-metrics-topic/schema.js +3 -0
- package/schematics/add-metrics-topic/schema.js.map +1 -0
- package/schematics/add-metrics-topic/schema.json +48 -0
- package/schematics/add-metrics-topic/schema.ts +18 -0
- package/schematics/add-queue/index.d.ts +3 -0
- package/schematics/add-queue/index.js +48 -0
- package/schematics/add-queue/index.js.map +1 -0
- package/schematics/add-queue/index.spec.ts +75 -0
- package/schematics/add-queue/index.ts +74 -0
- package/schematics/add-queue/schema.d.ts +17 -0
- package/schematics/add-queue/schema.js +3 -0
- package/schematics/add-queue/schema.js.map +1 -0
- package/schematics/add-queue/schema.json +54 -0
- package/schematics/add-queue/schema.ts +32 -0
- package/schematics/add-reply-queue/index.d.ts +3 -0
- package/schematics/add-reply-queue/index.js +26 -0
- package/schematics/add-reply-queue/index.js.map +1 -0
- package/schematics/add-reply-queue/index.spec.ts +69 -0
- package/schematics/add-reply-queue/index.ts +33 -0
- package/schematics/add-reply-queue/schema.d.ts +6 -0
- package/schematics/add-reply-queue/schema.js +3 -0
- package/schematics/add-reply-queue/schema.js.map +1 -0
- package/schematics/add-reply-queue/schema.json +28 -0
- package/schematics/add-reply-queue/schema.ts +10 -0
- package/schematics/add-route/index.d.ts +3 -0
- package/schematics/add-route/index.js +57 -0
- package/schematics/add-route/index.js.map +1 -0
- package/schematics/add-route/index.spec.ts +75 -0
- package/schematics/add-route/index.ts +74 -0
- package/schematics/add-route/schema.d.ts +22 -0
- package/schematics/add-route/schema.js +3 -0
- package/schematics/add-route/schema.js.map +1 -0
- package/schematics/add-route/schema.json +74 -0
- package/schematics/add-route/schema.ts +43 -0
- package/schematics/add-topic/index.d.ts +3 -0
- package/schematics/add-topic/index.js +69 -0
- package/schematics/add-topic/index.js.map +1 -0
- package/schematics/add-topic/index.spec.ts +100 -0
- package/schematics/add-topic/index.ts +104 -0
- package/schematics/add-topic/schema.d.ts +18 -0
- package/schematics/add-topic/schema.js +3 -0
- package/schematics/add-topic/schema.js.map +1 -0
- package/schematics/add-topic/schema.json +63 -0
- package/schematics/add-topic/schema.ts +34 -0
- package/schematics/add-ws-event/index.d.ts +3 -0
- package/schematics/add-ws-event/index.js +53 -0
- package/schematics/add-ws-event/index.js.map +1 -0
- package/schematics/add-ws-event/index.spec.ts +85 -0
- package/schematics/add-ws-event/index.ts +72 -0
- package/schematics/add-ws-event/schema.d.ts +17 -0
- package/schematics/add-ws-event/schema.js +3 -0
- package/schematics/add-ws-event/schema.js.map +1 -0
- package/schematics/add-ws-event/schema.json +53 -0
- package/schematics/add-ws-event/schema.ts +33 -0
- package/schematics/collection.json +81 -1
- package/schematics/configure-broker/index.d.ts +3 -0
- package/schematics/configure-broker/index.js +41 -0
- package/schematics/configure-broker/index.js.map +1 -0
- package/schematics/configure-broker/index.spec.ts +62 -0
- package/schematics/configure-broker/index.ts +51 -0
- package/schematics/configure-broker/schema.d.ts +11 -0
- package/schematics/configure-broker/schema.js +3 -0
- package/schematics/configure-broker/schema.js.map +1 -0
- package/schematics/configure-broker/schema.json +47 -0
- package/schematics/configure-broker/schema.ts +20 -0
- package/schematics/enable-load-config/index.d.ts +3 -0
- package/schematics/enable-load-config/index.js +32 -0
- package/schematics/enable-load-config/index.js.map +1 -0
- package/schematics/enable-load-config/index.spec.ts +61 -0
- package/schematics/enable-load-config/index.ts +39 -0
- package/schematics/enable-load-config/schema.d.ts +8 -0
- package/schematics/enable-load-config/schema.js +3 -0
- package/schematics/enable-load-config/schema.js.map +1 -0
- package/schematics/enable-load-config/schema.json +36 -0
- package/schematics/enable-load-config/schema.ts +14 -0
- package/schematics/enable-retry/index.d.ts +3 -0
- package/schematics/enable-retry/index.js +51 -0
- package/schematics/enable-retry/index.js.map +1 -0
- package/schematics/enable-retry/index.spec.ts +73 -0
- package/schematics/enable-retry/index.ts +62 -0
- package/schematics/enable-retry/schema.d.ts +13 -0
- package/schematics/enable-retry/schema.js +3 -0
- package/schematics/enable-retry/schema.js.map +1 -0
- package/schematics/enable-retry/schema.json +64 -0
- package/schematics/enable-retry/schema.ts +24 -0
- package/schematics/enable-route-discovery/index.d.ts +3 -0
- package/schematics/enable-route-discovery/index.js +36 -0
- package/schematics/enable-route-discovery/index.js.map +1 -0
- package/schematics/enable-route-discovery/index.spec.ts +55 -0
- package/schematics/enable-route-discovery/index.ts +48 -0
- package/schematics/enable-route-discovery/schema.d.ts +9 -0
- package/schematics/enable-route-discovery/schema.js +3 -0
- package/schematics/enable-route-discovery/schema.js.map +1 -0
- package/schematics/enable-route-discovery/schema.json +44 -0
- package/schematics/enable-route-discovery/schema.ts +16 -0
- package/schematics/harden-gateway/index.d.ts +3 -0
- package/schematics/harden-gateway/index.js +93 -0
- package/schematics/harden-gateway/index.js.map +1 -0
- package/schematics/harden-gateway/index.spec.ts +105 -0
- package/schematics/harden-gateway/index.ts +108 -0
- package/schematics/harden-gateway/schema.d.ts +11 -0
- package/schematics/harden-gateway/schema.js +3 -0
- package/schematics/harden-gateway/schema.js.map +1 -0
- package/schematics/harden-gateway/schema.json +47 -0
- package/schematics/harden-gateway/schema.ts +20 -0
- package/schematics/nest-add/index.js +55 -832
- package/schematics/nest-add/index.js.map +1 -1
- package/schematics/nest-add/index.spec.ts +73 -0
- package/schematics/nest-add/index.ts +76 -917
- package/schematics/nest-add/init.schema.d.ts +1 -0
- package/schematics/nest-add/init.schema.ts +3 -0
- package/schematics/nest-add/schema.json +4 -43
- package/schematics/set-connection-name/index.d.ts +3 -0
- package/schematics/set-connection-name/index.js +34 -0
- package/schematics/set-connection-name/index.js.map +1 -0
- package/schematics/set-connection-name/index.spec.ts +48 -0
- package/schematics/set-connection-name/index.ts +42 -0
- package/schematics/set-connection-name/schema.d.ts +5 -0
- package/schematics/set-connection-name/schema.js +3 -0
- package/schematics/set-connection-name/schema.js.map +1 -0
- package/schematics/set-connection-name/schema.json +24 -0
- package/schematics/set-connection-name/schema.ts +8 -0
- package/schematics/utils/broker-yaml.util.d.ts +53 -0
- package/schematics/utils/broker-yaml.util.js +91 -0
- package/schematics/utils/broker-yaml.util.js.map +1 -0
- package/schematics/utils/broker-yaml.util.ts +172 -0
- package/schematics/utils/nest-wiring.util.d.ts +30 -0
- package/schematics/utils/nest-wiring.util.js +170 -0
- package/schematics/utils/nest-wiring.util.js.map +1 -0
- package/schematics/utils/nest-wiring.util.ts +211 -0
- package/schematics/utils/schematic-prompt.util.d.ts +7 -0
- package/schematics/utils/schematic-prompt.util.js +57 -0
- package/schematics/utils/schematic-prompt.util.js.map +1 -0
- package/schematics/utils/schematic-prompt.util.ts +82 -0
- package/schematics/utils/yaml-config.util.d.ts +22 -0
- package/schematics/utils/yaml-config.util.js +119 -0
- package/schematics/utils/yaml-config.util.js.map +1 -0
- package/schematics/utils/yaml-config.util.ts +167 -0
- /package/schematics/{nest-add → add-gateway}/files/acl/src/cache/in-memory-acl-store.ts +0 -0
- /package/schematics/{nest-add → add-gateway}/files/acl/src/modules/database/repository/acl.repository.ts +0 -0
- /package/schematics/{nest-add → add-gateway}/files/db-core/src/modules/database/repository/in-memory-collection.ts +0 -0
- /package/schematics/{nest-add → add-gateway}/files/gateway-admin/src/modules/database/repository/gateway.repository.ts +0 -0
- /package/schematics/{nest-add → add-gateway}/files/gateway-admin/src/modules/database/repository/route-sync.repository.ts +0 -0
|
@@ -2,862 +2,85 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.main = main;
|
|
4
4
|
const schematics_1 = require("@angular-devkit/schematics");
|
|
5
|
-
const jsonc_parser_1 = require("jsonc-parser");
|
|
6
5
|
const path_1 = require("path");
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
function
|
|
11
|
-
return {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
async function resolveSelections(o, context) {
|
|
22
|
-
const project = (o.project || 'my-service').toString();
|
|
23
|
-
const d = defaultNames(project);
|
|
24
|
-
const flagsProvided = o.gatewayConfig !== undefined || (Array.isArray(o.features) && o.features.length > 0);
|
|
25
|
-
const canPrompt = !!process.stdout && !!process.stdout.isTTY && !process.env.CI && !flagsProvided;
|
|
26
|
-
let prompts;
|
|
27
|
-
if (canPrompt) {
|
|
28
|
-
try {
|
|
29
|
-
prompts = require('@inquirer/prompts');
|
|
30
|
-
}
|
|
31
|
-
catch {
|
|
32
|
-
context.logger.warn('[nest-add] @inquirer/prompts not found; falling back to flags/defaults (non-interactive).');
|
|
33
|
-
prompts = undefined;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
if (!prompts) {
|
|
37
|
-
const features = new Set((o.features || []).map((f) => String(f).trim()));
|
|
38
|
-
const gatewayConfig = o.gatewayConfig === true;
|
|
39
|
-
return {
|
|
40
|
-
gatewayConfig,
|
|
41
|
-
acl: gatewayConfig && features.has('acl'),
|
|
42
|
-
admin: gatewayConfig && features.has('gateway-admin'),
|
|
43
|
-
routeReception: gatewayConfig && features.has('route-reception'),
|
|
44
|
-
autoPublish: !gatewayConfig && features.has('auto-config-publish'),
|
|
45
|
-
skills: o.skills !== false,
|
|
46
|
-
names: {
|
|
47
|
-
exchange: o.exchange || d.exchange,
|
|
48
|
-
aclQueue: o.aclQueue || d.aclQueue,
|
|
49
|
-
adminQueue: o.adminQueue || d.adminQueue,
|
|
50
|
-
controlTopic: o.controlTopic || d.controlTopic,
|
|
51
|
-
routeExchange: o.routeExchange || d.routeExchange,
|
|
52
|
-
routeQueue: o.routeQueue || d.routeQueue,
|
|
53
|
-
serviceName: o.serviceName || d.serviceName,
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
const { confirm, checkbox, input } = prompts;
|
|
58
|
-
const names = { ...d };
|
|
59
|
-
let acl = false, admin = false, routeReception = false, autoPublish = false;
|
|
60
|
-
const gatewayConfig = await confirm({
|
|
61
|
-
message: 'Create a gateway (HTTP/WebSocket) configuration?',
|
|
62
|
-
default: false,
|
|
63
|
-
});
|
|
64
|
-
if (gatewayConfig) {
|
|
65
|
-
const picked = await checkbox({
|
|
66
|
-
message: 'Select gateway features to include',
|
|
67
|
-
choices: [
|
|
68
|
-
{ name: 'ACL — role-based authorization + management', value: 'acl' },
|
|
69
|
-
{ name: 'Gateway admin + auth — DB-managed routes, auth-providers, metrics', value: 'gateway-admin' },
|
|
70
|
-
{ name: 'Route reception — apply routes auto-published by microservices', value: 'route-reception' },
|
|
71
|
-
],
|
|
72
|
-
});
|
|
73
|
-
acl = picked.includes('acl');
|
|
74
|
-
admin = picked.includes('gateway-admin');
|
|
75
|
-
routeReception = picked.includes('route-reception');
|
|
76
|
-
if (acl || admin || routeReception) {
|
|
77
|
-
names.exchange = await input({ message: 'Main AMQP exchange name', default: d.exchange });
|
|
78
|
-
}
|
|
79
|
-
if (acl) {
|
|
80
|
-
names.aclQueue = await input({ message: 'Queue backing the rlb-acl topic', default: d.aclQueue });
|
|
81
|
-
}
|
|
82
|
-
if (admin || routeReception) {
|
|
83
|
-
names.adminQueue = await input({ message: 'Queue backing the rlb-gateway-admin topic', default: d.adminQueue });
|
|
84
|
-
names.controlTopic = await input({ message: 'Broadcast control/reload topic name', default: d.controlTopic });
|
|
85
|
-
}
|
|
86
|
-
if (routeReception) {
|
|
87
|
-
names.routeExchange = await input({ message: 'Route-discovery exchange (must match the publishers)', default: d.routeExchange });
|
|
88
|
-
names.routeQueue = await input({ message: 'Route-sync queue', default: d.routeQueue });
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
const picked = await checkbox({
|
|
93
|
-
message: 'Select microservice features to include',
|
|
94
|
-
choices: [
|
|
95
|
-
{ name: 'Auto-send config at startup — publish this service’s @BrokerHTTP routes to the gateway on boot', value: 'auto-config-publish' },
|
|
96
|
-
],
|
|
6
|
+
const nest_wiring_util_1 = require("../utils/nest-wiring.util");
|
|
7
|
+
const schematic_prompt_util_1 = require("../utils/schematic-prompt.util");
|
|
8
|
+
const yaml_config_util_1 = require("../utils/yaml-config.util");
|
|
9
|
+
function main(options) {
|
|
10
|
+
return async (tree, context) => {
|
|
11
|
+
const prompts = (0, schematic_prompt_util_1.loadPrompts)(context, options.skills !== undefined);
|
|
12
|
+
const skills = options.skills ?? (await (0, schematic_prompt_util_1.askConfirm)(prompts, 'Add the Claude skills / project docs into .claude/skills?', true));
|
|
13
|
+
ensureBaseConfig(tree, options.config, context);
|
|
14
|
+
(0, nest_wiring_util_1.createConfigLoader)(tree);
|
|
15
|
+
const modulePath = (0, nest_wiring_util_1.wireAppModule)(tree, {
|
|
16
|
+
libSymbols: (0, nest_wiring_util_1.coreLibSymbols)(),
|
|
17
|
+
importLines: (0, nest_wiring_util_1.coreImportLines)(),
|
|
18
|
+
entries: [(0, nest_wiring_util_1.configModuleEntry)(), (0, nest_wiring_util_1.brokerModuleEntry)()],
|
|
97
19
|
});
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
names.serviceName = await input({ message: 'Service name (route ownership + AMQP connection_name)', default: d.serviceName });
|
|
101
|
-
names.routeExchange = await input({ message: 'Route-discovery exchange', default: d.routeExchange });
|
|
102
|
-
names.routeQueue = await input({ message: 'Route-sync queue', default: d.routeQueue });
|
|
20
|
+
if (!modulePath) {
|
|
21
|
+
context.logger.warn('[nest-add] app.module.ts not found — wire ConfigModule + BrokerModule.forRootAsync manually.');
|
|
103
22
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
23
|
+
(0, nest_wiring_util_1.addDeps)(tree, { '@nestjs/config': '^4.0.4', 'js-yaml': '^4.1.0' });
|
|
24
|
+
context.logger.info('[nest-add] core ready. Use `add-gateway`, `add-topic`, `add-route`, `enable-retry`, … to add features.');
|
|
25
|
+
return skills ? copySkills() : (0, schematics_1.noop)();
|
|
26
|
+
};
|
|
107
27
|
}
|
|
108
|
-
function
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
type: "direct"
|
|
126
|
-
createExchangeIfNotExists: true
|
|
127
|
-
options:
|
|
128
|
-
durable: true`);
|
|
129
|
-
}
|
|
130
|
-
exchanges.push(` - name: example.fanout
|
|
131
|
-
type: "fanout"
|
|
132
|
-
createExchangeIfNotExists: true
|
|
133
|
-
options:
|
|
134
|
-
durable: true
|
|
135
|
-
autoDelete: false
|
|
136
|
-
internal: false`);
|
|
137
|
-
const queues = [];
|
|
138
|
-
if (sel.acl) {
|
|
139
|
-
queues.push(` - name: ${n.aclQueue}
|
|
140
|
-
exchange: ${n.exchange}
|
|
141
|
-
routingKey: ${n.aclQueue}
|
|
142
|
-
createQueueIfNotExists: true
|
|
143
|
-
options:
|
|
144
|
-
durable: true`);
|
|
145
|
-
}
|
|
146
|
-
if (anyAdmin) {
|
|
147
|
-
queues.push(` - name: ${n.adminQueue}
|
|
148
|
-
exchange: ${n.exchange}
|
|
149
|
-
routingKey: ${n.adminQueue}
|
|
150
|
-
createQueueIfNotExists: true
|
|
151
|
-
options:
|
|
152
|
-
durable: true`);
|
|
153
|
-
}
|
|
154
|
-
queues.push(` - name: example.queue
|
|
155
|
-
exchange: example.fanout
|
|
156
|
-
routingKey: example.queue
|
|
157
|
-
createQueueIfNotExists: true
|
|
158
|
-
options:
|
|
159
|
-
durable: true
|
|
160
|
-
autoDelete: false
|
|
161
|
-
exclusive: false`);
|
|
162
|
-
const topics = [];
|
|
163
|
-
if (sel.acl) {
|
|
164
|
-
topics.push(` # Fixed topic name (decorator-bound in the lib): the ACL handlers bind to 'rlb-acl'.
|
|
165
|
-
- name: rlb-acl
|
|
166
|
-
mode: rpc
|
|
167
|
-
queue: ${n.aclQueue}
|
|
168
|
-
exchange: ${n.exchange}
|
|
169
|
-
routingKey: ${n.aclQueue}`);
|
|
170
|
-
}
|
|
171
|
-
if (anyAdmin) {
|
|
172
|
-
topics.push(` # Fixed topic name (decorator-bound): the gateway-admin handlers bind to 'rlb-gateway-admin'.
|
|
173
|
-
- name: rlb-gateway-admin
|
|
174
|
-
mode: rpc
|
|
175
|
-
queue: ${n.adminQueue}
|
|
176
|
-
exchange: ${n.exchange}
|
|
177
|
-
routingKey: ${n.adminQueue}
|
|
178
|
-
# Broadcast control topic: the gateway rebuilds its routes at runtime on a 'gw-reload'.
|
|
179
|
-
- name: ${n.controlTopic}
|
|
180
|
-
mode: broadcast
|
|
181
|
-
exchange: ${n.exchange}
|
|
182
|
-
routingKey: ${n.controlTopic}`);
|
|
183
|
-
}
|
|
184
|
-
topics.push(` - name: example.topic
|
|
185
|
-
exchange: example.fanout
|
|
186
|
-
routingKey: "example.topic"
|
|
187
|
-
mode: event`);
|
|
188
|
-
let yaml = `app:
|
|
28
|
+
function ensureBaseConfig(tree, preferred, context) {
|
|
29
|
+
const configPath = (0, yaml_config_util_1.findConfigYaml)(tree, preferred);
|
|
30
|
+
if (!tree.exists(configPath)) {
|
|
31
|
+
tree.create(configPath, BASE_CONFIG_YAML);
|
|
32
|
+
context.logger.info(`[nest-add] created ${configPath}.`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const { doc } = (0, yaml_config_util_1.readConfigDoc)(tree, configPath);
|
|
36
|
+
if (!doc.hasIn(['app']))
|
|
37
|
+
(0, yaml_config_util_1.setIn)(doc, ['app'], { port: 80, host: '0.0.0.0', environment: 'development' });
|
|
38
|
+
if (!doc.hasIn(['broker']))
|
|
39
|
+
(0, yaml_config_util_1.setIn)(doc, ['broker'], { name: 'rabbitmq', uri: '<AMQP_URI>' });
|
|
40
|
+
if (!doc.hasIn(['topics']))
|
|
41
|
+
(0, yaml_config_util_1.setIn)(doc, ['topics'], []);
|
|
42
|
+
(0, yaml_config_util_1.writeConfigDoc)(tree, configPath, doc);
|
|
43
|
+
}
|
|
44
|
+
const BASE_CONFIG_YAML = `app:
|
|
189
45
|
port: 80
|
|
190
46
|
host: 0.0.0.0
|
|
191
47
|
environment: "development"
|
|
192
48
|
|
|
193
|
-
auth-providers: []
|
|
194
|
-
|
|
195
49
|
broker:
|
|
196
50
|
name: "rabbitmq"
|
|
197
51
|
uri: "<AMQP_URI>"
|
|
198
|
-
defaultSubscribeErrorBehavior: "ack"
|
|
199
|
-
defaultPublishErrorBehavior: "reject"${routeDiscoveryPub}
|
|
200
52
|
connectionManagerOptions:
|
|
201
53
|
heartbeatIntervalInSeconds: 60
|
|
202
54
|
reconnectTimeInSeconds: 60
|
|
203
|
-
connectionOptions
|
|
55
|
+
connectionOptions:
|
|
56
|
+
clientProperties:
|
|
57
|
+
# Logical name — the library appends -<hostname>-<pid> per instance.
|
|
58
|
+
connection_name: "<APP_NAME>"
|
|
204
59
|
credentials:
|
|
205
60
|
mechanism: PLAIN
|
|
206
61
|
username: "<AMQP_USERNAME>"
|
|
207
62
|
password: "<AMQP_PASSWORD>"
|
|
208
63
|
exchanges:
|
|
209
|
-
|
|
64
|
+
- name: example.fanout
|
|
65
|
+
type: "fanout"
|
|
66
|
+
createExchangeIfNotExists: true
|
|
67
|
+
options:
|
|
68
|
+
durable: true
|
|
210
69
|
queues:
|
|
211
|
-
|
|
70
|
+
- name: example.queue
|
|
71
|
+
exchange: example.fanout
|
|
72
|
+
routingKey: example.queue
|
|
73
|
+
createQueueIfNotExists: true
|
|
74
|
+
options:
|
|
75
|
+
durable: true
|
|
212
76
|
|
|
213
77
|
topics:
|
|
214
|
-
|
|
78
|
+
- name: example.topic
|
|
79
|
+
exchange: example.fanout
|
|
80
|
+
routingKey: "example.topic"
|
|
81
|
+
mode: event
|
|
215
82
|
`;
|
|
216
|
-
if (sel.gatewayConfig) {
|
|
217
|
-
yaml += '\n' + buildGatewayBlock(sel);
|
|
218
|
-
}
|
|
219
|
-
return yaml;
|
|
220
|
-
}
|
|
221
|
-
function buildGatewayBlock(sel) {
|
|
222
|
-
const n = sel.names;
|
|
223
|
-
const anyAdmin = sel.admin || sel.routeReception;
|
|
224
|
-
const paths = [];
|
|
225
|
-
if (sel.acl)
|
|
226
|
-
paths.push(ACL_PATHS);
|
|
227
|
-
if (sel.admin)
|
|
228
|
-
paths.push(adminPaths(n.controlTopic));
|
|
229
|
-
paths.push(` - name: example-path
|
|
230
|
-
method: POST
|
|
231
|
-
dataSource: body
|
|
232
|
-
path: /example
|
|
233
|
-
topic: example.topic
|
|
234
|
-
action: example-action
|
|
235
|
-
mode: event`);
|
|
236
|
-
let block = `gateway:
|
|
237
|
-
events: []
|
|
238
|
-
ws:
|
|
239
|
-
heartbeatIntervalMs: 30000
|
|
240
|
-
# Auth is declared per-event (events[].auth / requireAuth / actions / scopeClaim).
|
|
241
|
-
paths:
|
|
242
|
-
${paths.join('\n')}`;
|
|
243
|
-
if (anyAdmin) {
|
|
244
|
-
block += `
|
|
245
|
-
# Load DB-managed routes at boot AND on every runtime reload (ordered static-before-param).
|
|
246
|
-
loadConfig:
|
|
247
|
-
paths:
|
|
248
|
-
topic: rlb-gateway-admin
|
|
249
|
-
action: gw-path-export
|
|
250
|
-
# Broadcast topic that triggers a runtime route rebuild (see topics: ${n.controlTopic}).
|
|
251
|
-
reloadTopic: ${n.controlTopic}
|
|
252
|
-
# Per-request metrics auto-emitted to the gateway-admin metrics handler.
|
|
253
|
-
metrics:
|
|
254
|
-
topic: rlb-gateway-admin
|
|
255
|
-
action: gw-metrics-track`;
|
|
256
|
-
}
|
|
257
|
-
return block + '\n';
|
|
258
|
-
}
|
|
259
|
-
const ACL_PATHS = ` # --- ACL management: actions (name is the key — PUT upserts, GET lists, DELETE by name) ---
|
|
260
|
-
- name: acl-action-list
|
|
261
|
-
method: GET
|
|
262
|
-
path: /acl/actions
|
|
263
|
-
dataSource: query
|
|
264
|
-
topic: rlb-acl
|
|
265
|
-
action: acl-action-list
|
|
266
|
-
mode: rpc
|
|
267
|
-
- name: acl-action-get
|
|
268
|
-
method: GET
|
|
269
|
-
path: /acl/actions/get
|
|
270
|
-
dataSource: query
|
|
271
|
-
topic: rlb-acl
|
|
272
|
-
action: acl-action-get
|
|
273
|
-
mode: rpc
|
|
274
|
-
- name: acl-action-upsert
|
|
275
|
-
method: PUT
|
|
276
|
-
path: /acl/actions
|
|
277
|
-
dataSource: body
|
|
278
|
-
topic: rlb-acl
|
|
279
|
-
action: acl-action-update
|
|
280
|
-
mode: rpc
|
|
281
|
-
- name: acl-action-delete
|
|
282
|
-
method: DELETE
|
|
283
|
-
path: /acl/actions
|
|
284
|
-
dataSource: body
|
|
285
|
-
topic: rlb-acl
|
|
286
|
-
action: acl-action-delete
|
|
287
|
-
mode: rpc
|
|
288
|
-
- name: acl-action-search # query: ?q=&limit=
|
|
289
|
-
method: GET
|
|
290
|
-
path: /acl/actions/search
|
|
291
|
-
dataSource: query
|
|
292
|
-
topic: rlb-acl
|
|
293
|
-
action: acl-action-search
|
|
294
|
-
mode: rpc
|
|
295
|
-
# --- ACL management: roles ---
|
|
296
|
-
- name: acl-role-list
|
|
297
|
-
method: GET
|
|
298
|
-
path: /acl/roles
|
|
299
|
-
dataSource: query
|
|
300
|
-
topic: rlb-acl
|
|
301
|
-
action: acl-role-list
|
|
302
|
-
mode: rpc
|
|
303
|
-
- name: acl-role-get
|
|
304
|
-
method: GET
|
|
305
|
-
path: /acl/roles/get
|
|
306
|
-
dataSource: query
|
|
307
|
-
topic: rlb-acl
|
|
308
|
-
action: acl-role-get
|
|
309
|
-
mode: rpc
|
|
310
|
-
- name: acl-role-upsert
|
|
311
|
-
method: PUT
|
|
312
|
-
path: /acl/roles
|
|
313
|
-
dataSource: body
|
|
314
|
-
topic: rlb-acl
|
|
315
|
-
action: acl-role-update
|
|
316
|
-
mode: rpc
|
|
317
|
-
- name: acl-role-delete
|
|
318
|
-
method: DELETE
|
|
319
|
-
path: /acl/roles
|
|
320
|
-
dataSource: body
|
|
321
|
-
topic: rlb-acl
|
|
322
|
-
action: acl-role-delete
|
|
323
|
-
mode: rpc
|
|
324
|
-
- name: acl-role-search # query: ?q=&limit=
|
|
325
|
-
method: GET
|
|
326
|
-
path: /acl/roles/search
|
|
327
|
-
dataSource: query
|
|
328
|
-
topic: rlb-acl
|
|
329
|
-
action: acl-role-search
|
|
330
|
-
mode: rpc
|
|
331
|
-
# --- ACL grants (per-user; resourceId/companyId optional) ---
|
|
332
|
-
- name: acl-grant
|
|
333
|
-
method: POST
|
|
334
|
-
path: /acl/grants
|
|
335
|
-
dataSource: body
|
|
336
|
-
topic: rlb-acl
|
|
337
|
-
action: acl-grant
|
|
338
|
-
mode: rpc
|
|
339
|
-
- name: acl-revoke
|
|
340
|
-
method: DELETE
|
|
341
|
-
path: /acl/grants
|
|
342
|
-
dataSource: body
|
|
343
|
-
topic: rlb-acl
|
|
344
|
-
action: acl-revoke
|
|
345
|
-
mode: rpc
|
|
346
|
-
- name: acl-grant-search # access search; query: ?q=&limit=
|
|
347
|
-
method: GET
|
|
348
|
-
path: /acl/grants/search
|
|
349
|
-
dataSource: query
|
|
350
|
-
topic: rlb-acl
|
|
351
|
-
action: acl-grant-search
|
|
352
|
-
mode: rpc
|
|
353
|
-
# --- ACL check (GET → 200 with true/false) ---
|
|
354
|
-
# checkAction(userId, {companyId?, resourceId?}, action): true if the user holds the action
|
|
355
|
-
# via any role on the EXACT (companyId, resourceId). companyId/resourceId are optional;
|
|
356
|
-
# when both are absent the check matches resource-less grants only (no wildcard).
|
|
357
|
-
- name: acl-check-action
|
|
358
|
-
method: GET
|
|
359
|
-
path: /acl/check
|
|
360
|
-
dataSource: query
|
|
361
|
-
topic: rlb-acl
|
|
362
|
-
action: acl-check-action
|
|
363
|
-
mode: rpc
|
|
364
|
-
# Lists the caller's accessible resources. Add an 'auth: <provider>' line once you declare an auth-provider.
|
|
365
|
-
- name: acl-list-resources-by-user
|
|
366
|
-
method: GET
|
|
367
|
-
path: /acl/resources
|
|
368
|
-
dataSource: query
|
|
369
|
-
topic: rlb-acl
|
|
370
|
-
action: acl-list-resources-by-user
|
|
371
|
-
mode: rpc`;
|
|
372
|
-
function adminPaths(controlTopic) {
|
|
373
|
-
return ` # --- gateway-admin: health + DB routes + auth-providers + metrics ---
|
|
374
|
-
- name: health
|
|
375
|
-
method: GET
|
|
376
|
-
path: /health
|
|
377
|
-
dataSource: query
|
|
378
|
-
topic: rlb-gateway-admin
|
|
379
|
-
action: gw-health
|
|
380
|
-
mode: rpc
|
|
381
|
-
- name: gw-path-create
|
|
382
|
-
method: POST
|
|
383
|
-
path: /admin/paths
|
|
384
|
-
dataSource: body
|
|
385
|
-
topic: rlb-gateway-admin
|
|
386
|
-
action: gw-path-create
|
|
387
|
-
mode: rpc
|
|
388
|
-
- name: gw-path-list
|
|
389
|
-
method: GET
|
|
390
|
-
path: /admin/paths
|
|
391
|
-
dataSource: query
|
|
392
|
-
topic: rlb-gateway-admin
|
|
393
|
-
action: gw-path-list
|
|
394
|
-
mode: rpc
|
|
395
|
-
- name: gw-path-search # query: ?q=&limit=
|
|
396
|
-
method: GET
|
|
397
|
-
path: /admin/paths/search
|
|
398
|
-
dataSource: query
|
|
399
|
-
topic: rlb-gateway-admin
|
|
400
|
-
action: gw-path-search
|
|
401
|
-
mode: rpc
|
|
402
|
-
- name: gw-path-export
|
|
403
|
-
method: GET
|
|
404
|
-
path: /admin/paths/export
|
|
405
|
-
dataSource: query
|
|
406
|
-
topic: rlb-gateway-admin
|
|
407
|
-
action: gw-path-export
|
|
408
|
-
mode: rpc
|
|
409
|
-
- name: gw-path-update
|
|
410
|
-
method: PUT
|
|
411
|
-
path: /admin/paths
|
|
412
|
-
dataSource: body
|
|
413
|
-
topic: rlb-gateway-admin
|
|
414
|
-
action: gw-path-update
|
|
415
|
-
mode: rpc
|
|
416
|
-
- name: gw-path-get
|
|
417
|
-
method: GET
|
|
418
|
-
path: /admin/paths/get
|
|
419
|
-
dataSource: query
|
|
420
|
-
topic: rlb-gateway-admin
|
|
421
|
-
action: gw-path-get
|
|
422
|
-
mode: rpc
|
|
423
|
-
- name: gw-path-delete
|
|
424
|
-
method: DELETE
|
|
425
|
-
path: /admin/paths
|
|
426
|
-
dataSource: body
|
|
427
|
-
topic: rlb-gateway-admin
|
|
428
|
-
action: gw-path-delete
|
|
429
|
-
mode: rpc
|
|
430
|
-
# Route-change journal (who changed what): actor=system for auto-discovery, userId for manual CRUD.
|
|
431
|
-
- name: gw-route-log-list
|
|
432
|
-
method: GET
|
|
433
|
-
path: /admin/route-log
|
|
434
|
-
dataSource: query
|
|
435
|
-
topic: rlb-gateway-admin
|
|
436
|
-
action: gw-route-log-list
|
|
437
|
-
mode: rpc
|
|
438
|
-
- name: gw-route-log-search # filtered+paginated; query: ?actor=&service=&event=&routeKey=&from=&to=&page=&limit=
|
|
439
|
-
method: GET
|
|
440
|
-
path: /admin/route-log/search
|
|
441
|
-
dataSource: query
|
|
442
|
-
topic: rlb-gateway-admin
|
|
443
|
-
action: gw-route-log-search
|
|
444
|
-
mode: rpc
|
|
445
|
-
- name: gw-auth-list
|
|
446
|
-
method: GET
|
|
447
|
-
path: /admin/auth
|
|
448
|
-
dataSource: query
|
|
449
|
-
topic: rlb-gateway-admin
|
|
450
|
-
action: gw-auth-list
|
|
451
|
-
mode: rpc
|
|
452
|
-
- name: gw-auth-search # query: ?q=&limit=
|
|
453
|
-
method: GET
|
|
454
|
-
path: /admin/auth/search
|
|
455
|
-
dataSource: query
|
|
456
|
-
topic: rlb-gateway-admin
|
|
457
|
-
action: gw-auth-search
|
|
458
|
-
mode: rpc
|
|
459
|
-
- name: gw-auth-upsert
|
|
460
|
-
method: PUT
|
|
461
|
-
path: /admin/auth
|
|
462
|
-
dataSource: body
|
|
463
|
-
topic: rlb-gateway-admin
|
|
464
|
-
action: gw-auth-update
|
|
465
|
-
mode: rpc
|
|
466
|
-
- name: gw-auth-get
|
|
467
|
-
method: GET
|
|
468
|
-
path: /admin/auth/get
|
|
469
|
-
dataSource: query
|
|
470
|
-
topic: rlb-gateway-admin
|
|
471
|
-
action: gw-auth-get
|
|
472
|
-
mode: rpc
|
|
473
|
-
- name: gw-auth-delete
|
|
474
|
-
method: DELETE
|
|
475
|
-
path: /admin/auth
|
|
476
|
-
dataSource: body
|
|
477
|
-
topic: rlb-gateway-admin
|
|
478
|
-
action: gw-auth-delete
|
|
479
|
-
mode: rpc
|
|
480
|
-
- name: gw-metrics-get
|
|
481
|
-
method: GET
|
|
482
|
-
path: /admin/metrics
|
|
483
|
-
dataSource: query
|
|
484
|
-
topic: rlb-gateway-admin
|
|
485
|
-
action: gw-metrics-get
|
|
486
|
-
mode: rpc
|
|
487
|
-
- name: gw-metrics-series
|
|
488
|
-
method: GET
|
|
489
|
-
path: /admin/metrics/series
|
|
490
|
-
dataSource: query
|
|
491
|
-
topic: rlb-gateway-admin
|
|
492
|
-
action: gw-metrics-series
|
|
493
|
-
mode: rpc
|
|
494
|
-
- name: gw-metrics-points
|
|
495
|
-
method: GET
|
|
496
|
-
path: /admin/metrics/points
|
|
497
|
-
dataSource: query
|
|
498
|
-
topic: rlb-gateway-admin
|
|
499
|
-
action: gw-metrics-points
|
|
500
|
-
mode: rpc
|
|
501
|
-
- name: gw-metrics-summary # dashboard overview; query: ?from=&to=&method=&route=&name=&topN=
|
|
502
|
-
method: GET
|
|
503
|
-
path: /admin/metrics/summary
|
|
504
|
-
dataSource: query
|
|
505
|
-
topic: rlb-gateway-admin
|
|
506
|
-
action: gw-metrics-summary
|
|
507
|
-
mode: rpc
|
|
508
|
-
- name: gw-metrics-prometheus # Prometheus text exposition (returned as text/plain)
|
|
509
|
-
method: GET
|
|
510
|
-
path: /admin/metrics/prometheus
|
|
511
|
-
dataSource: query
|
|
512
|
-
topic: rlb-gateway-admin
|
|
513
|
-
action: gw-metrics-prometheus
|
|
514
|
-
mode: rpc
|
|
515
|
-
headers:
|
|
516
|
-
Content-Type: text/plain; version=0.0.4
|
|
517
|
-
- name: gw-metrics-rollups # long-term downsampled aggregates; query: ?from=&to=&method=&route=
|
|
518
|
-
method: GET
|
|
519
|
-
path: /admin/metrics/rollups
|
|
520
|
-
dataSource: query
|
|
521
|
-
topic: rlb-gateway-admin
|
|
522
|
-
action: gw-metrics-rollups
|
|
523
|
-
mode: rpc
|
|
524
|
-
- name: gw-metrics-track
|
|
525
|
-
method: POST
|
|
526
|
-
path: /admin/metrics/track
|
|
527
|
-
dataSource: body
|
|
528
|
-
topic: rlb-gateway-admin
|
|
529
|
-
action: gw-metrics-track
|
|
530
|
-
mode: event
|
|
531
|
-
- name: gw-reload
|
|
532
|
-
method: POST
|
|
533
|
-
path: /admin/reload
|
|
534
|
-
dataSource: body
|
|
535
|
-
topic: ${controlTopic}
|
|
536
|
-
action: gw-reload
|
|
537
|
-
mode: event
|
|
538
|
-
- name: gw-auth-reload # deliberate: reload DB auth-providers into RAM (separate from gw-reload)
|
|
539
|
-
method: POST
|
|
540
|
-
path: /admin/auth/reload
|
|
541
|
-
dataSource: body
|
|
542
|
-
topic: ${controlTopic}
|
|
543
|
-
action: gw-auth-reload
|
|
544
|
-
mode: event`;
|
|
545
|
-
}
|
|
546
|
-
function buildImportStatements(sel) {
|
|
547
|
-
const libSymbols = ['AppConfig', 'BrokerModule', 'BrokerTopic', 'RabbitMQConfig'];
|
|
548
|
-
if (sel.gatewayConfig)
|
|
549
|
-
libSymbols.push('GatewayConfig', 'HandlerAuthConfig', 'ProxyModule');
|
|
550
|
-
if (sel.acl)
|
|
551
|
-
libSymbols.push('AclActionRepository', 'AclGrantRepository', 'AclModule', 'AclRoleRepository', 'AclService', 'RLB_ACL_CACHE_STORE', 'RLB_GTW_ACL_ROLE_SERVICE');
|
|
552
|
-
if (sel.admin || sel.routeReception)
|
|
553
|
-
libSymbols.push('AuthProviderRepository', 'GatewayAdminModule', 'HttpMetricRepository', 'HttpPathRepository', 'RouteSyncLogRepository');
|
|
554
|
-
if (sel.admin && sel.gatewayConfig)
|
|
555
|
-
libSymbols.push('RLB_GTW_AUTH_PROVIDER_SOURCE');
|
|
556
|
-
const lib = `import { ${[...new Set(libSymbols)].sort().join(', ')} } from '@open-rlb/nestjs-amqp';`;
|
|
557
|
-
const lines = [lib, `import { ConfigModule, ConfigService } from '@nestjs/config';`, `import yamlConfig from './config/config.loader';`];
|
|
558
|
-
if (sel.gatewayConfig)
|
|
559
|
-
lines.push(`import { HttpModule } from '@nestjs/axios';`);
|
|
560
|
-
if (sel.acl) {
|
|
561
|
-
lines.push(`import { InMemoryAclActionRepository, InMemoryAclGrantRepository, InMemoryAclRoleRepository } from './modules/database/repository/acl.repository';`);
|
|
562
|
-
lines.push(`import { InMemoryAclStore } from './cache/in-memory-acl-store';`);
|
|
563
|
-
}
|
|
564
|
-
if (sel.admin || sel.routeReception) {
|
|
565
|
-
lines.push(`import { InMemoryAuthProviderRepository, InMemoryHttpMetricRepository, InMemoryHttpPathRepository } from './modules/database/repository/gateway.repository';`);
|
|
566
|
-
lines.push(`import { InMemoryRouteSyncLogRepository } from './modules/database/repository/route-sync.repository';`);
|
|
567
|
-
}
|
|
568
|
-
return lines.join('\n');
|
|
569
|
-
}
|
|
570
|
-
function brokerForRootAsync() {
|
|
571
|
-
return `BrokerModule.forRootAsync({
|
|
572
|
-
imports: [ConfigModule],
|
|
573
|
-
inject: [ConfigService],
|
|
574
|
-
useFactory: async (configService: ConfigService) => ({
|
|
575
|
-
options: configService.get<RabbitMQConfig>('broker')!,
|
|
576
|
-
topics: configService.get<BrokerTopic[]>('topics')!,
|
|
577
|
-
appOptions: configService.get<AppConfig>('app'),
|
|
578
|
-
})
|
|
579
|
-
})`;
|
|
580
|
-
}
|
|
581
|
-
function proxyForRootAsync(sel) {
|
|
582
|
-
const provs = [];
|
|
583
|
-
if (sel.acl)
|
|
584
|
-
provs.push(` // Action-gated paths resolve the caller's identity via AclService (in-process, no broker hop).
|
|
585
|
-
{ provide: RLB_GTW_ACL_ROLE_SERVICE, useExisting: AclService },`);
|
|
586
|
-
if (sel.admin)
|
|
587
|
-
provs.push(` // DB auth-provider source for the runtime registry; activated by a deliberate gw-auth-reload.
|
|
588
|
-
{ provide: RLB_GTW_AUTH_PROVIDER_SOURCE, useExisting: InMemoryAuthProviderRepository },`);
|
|
589
|
-
const providers = provs.length ? `[\n${provs.join('\n')}\n ]` : `[]`;
|
|
590
|
-
return `ProxyModule.forRootAsync({
|
|
591
|
-
imports: [ConfigModule],
|
|
592
|
-
inject: [ConfigService],
|
|
593
|
-
useFactory: (configService: ConfigService) => ({
|
|
594
|
-
authOptions: configService.get<HandlerAuthConfig[]>('auth-providers'),
|
|
595
|
-
gatewayOptions: configService.get<GatewayConfig>('gateway'),
|
|
596
|
-
}),
|
|
597
|
-
providers: ${providers},
|
|
598
|
-
})`;
|
|
599
|
-
}
|
|
600
|
-
function aclForRoot() {
|
|
601
|
-
return `AclModule.forRoot(
|
|
602
|
-
[
|
|
603
|
-
InMemoryAclActionRepository,
|
|
604
|
-
{ provide: AclActionRepository, useExisting: InMemoryAclActionRepository },
|
|
605
|
-
InMemoryAclRoleRepository,
|
|
606
|
-
{ provide: AclRoleRepository, useExisting: InMemoryAclRoleRepository },
|
|
607
|
-
InMemoryAclGrantRepository,
|
|
608
|
-
{ provide: AclGrantRepository, useExisting: InMemoryAclGrantRepository },
|
|
609
|
-
InMemoryAclStore,
|
|
610
|
-
{ provide: RLB_ACL_CACHE_STORE, useExisting: InMemoryAclStore },
|
|
611
|
-
],
|
|
612
|
-
{ cache: { ramTtlMs: 30000, l2TtlSec: 600 } },
|
|
613
|
-
)`;
|
|
614
|
-
}
|
|
615
|
-
function gatewayAdminForRoot(sel) {
|
|
616
|
-
const options = sel.routeReception
|
|
617
|
-
? `,
|
|
618
|
-
{
|
|
619
|
-
// Consumer-side route-discovery — names MUST match the publishers' broker.routeDiscovery.
|
|
620
|
-
routeDiscovery: { exchange: '${sel.names.routeExchange}', queue: '${sel.names.routeQueue}' },
|
|
621
|
-
}`
|
|
622
|
-
: '';
|
|
623
|
-
return `GatewayAdminModule.forRoot(
|
|
624
|
-
[
|
|
625
|
-
InMemoryHttpPathRepository,
|
|
626
|
-
{ provide: HttpPathRepository, useExisting: InMemoryHttpPathRepository },
|
|
627
|
-
InMemoryAuthProviderRepository,
|
|
628
|
-
{ provide: AuthProviderRepository, useExisting: InMemoryAuthProviderRepository },
|
|
629
|
-
InMemoryHttpMetricRepository,
|
|
630
|
-
{ provide: HttpMetricRepository, useExisting: InMemoryHttpMetricRepository },
|
|
631
|
-
InMemoryRouteSyncLogRepository,
|
|
632
|
-
{ provide: RouteSyncLogRepository, useExisting: InMemoryRouteSyncLogRepository },
|
|
633
|
-
]${options},
|
|
634
|
-
)`;
|
|
635
|
-
}
|
|
636
|
-
function buildModuleEntries(sel) {
|
|
637
|
-
const entries = [];
|
|
638
|
-
entries.push(`ConfigModule.forRoot({ isGlobal: true, load: [yamlConfig] })`);
|
|
639
|
-
entries.push(brokerForRootAsync());
|
|
640
|
-
if (sel.gatewayConfig) {
|
|
641
|
-
entries.push('HttpModule');
|
|
642
|
-
entries.push(proxyForRootAsync(sel));
|
|
643
|
-
}
|
|
644
|
-
if (sel.acl)
|
|
645
|
-
entries.push(aclForRoot());
|
|
646
|
-
if (sel.admin || sel.routeReception)
|
|
647
|
-
entries.push(gatewayAdminForRoot(sel));
|
|
648
|
-
return entries;
|
|
649
|
-
}
|
|
650
|
-
function main(options) {
|
|
651
|
-
const project = (options.project || 'my-service').toString();
|
|
652
|
-
options = transform(options);
|
|
653
|
-
return async (tree, context) => {
|
|
654
|
-
const sel = await resolveSelections({ ...options, project }, context);
|
|
655
|
-
const anyRepo = sel.acl || sel.admin || sel.routeReception;
|
|
656
|
-
return (0, schematics_1.branchAndMerge)((0, schematics_1.chain)([
|
|
657
|
-
(0, source_root_helpers_1.mergeSourceRoot)(options),
|
|
658
|
-
addModulesToAppModule(sel),
|
|
659
|
-
createConfigLoader(),
|
|
660
|
-
updateConfigYaml(sel),
|
|
661
|
-
sel.gatewayConfig ? configureMainForGateway() : (0, schematics_1.noop)(),
|
|
662
|
-
anyRepo ? copyAsset('db-core') : (0, schematics_1.noop)(),
|
|
663
|
-
sel.acl ? copyAsset('acl') : (0, schematics_1.noop)(),
|
|
664
|
-
(sel.admin || sel.routeReception) ? copyAsset('gateway-admin') : (0, schematics_1.noop)(),
|
|
665
|
-
sel.skills ? copySkills() : (0, schematics_1.noop)(),
|
|
666
|
-
updatePackageJson(sel),
|
|
667
|
-
]));
|
|
668
|
-
};
|
|
669
|
-
}
|
|
670
|
-
function transform(source) {
|
|
671
|
-
const target = Object.assign({}, source);
|
|
672
|
-
target.metadata = 'providers';
|
|
673
|
-
target.type = 'module';
|
|
674
|
-
target.language = 'ts';
|
|
675
|
-
const location = new name_parser_1.NameParser().parse({ ...target, name: 'init' });
|
|
676
|
-
target.name = '';
|
|
677
|
-
target.path = (0, formatting_1.normalizeToKebabOrSnakeCase)(location.path);
|
|
678
|
-
target.specFileSuffix = (0, formatting_1.normalizeToKebabOrSnakeCase)(source.specFileSuffix || 'spec');
|
|
679
|
-
return target;
|
|
680
|
-
}
|
|
681
|
-
function copyAsset(name) {
|
|
682
|
-
return (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)(`./files/${name}`), [(0, schematics_1.move)((0, path_1.normalize)('.'))]), schematics_1.MergeStrategy.Overwrite);
|
|
683
|
-
}
|
|
684
83
|
function copySkills() {
|
|
685
84
|
return (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)('./files/skills'), [(0, schematics_1.move)((0, path_1.normalize)('.claude/skills'))]), schematics_1.MergeStrategy.Overwrite);
|
|
686
85
|
}
|
|
687
|
-
function createConfigLoader() {
|
|
688
|
-
return (tree) => {
|
|
689
|
-
const path = 'src/config/config.loader.ts';
|
|
690
|
-
if (tree.exists(path))
|
|
691
|
-
return tree;
|
|
692
|
-
tree.create(path, `import { readFileSync } from 'fs';
|
|
693
|
-
import * as yaml from 'js-yaml';
|
|
694
|
-
import { join } from 'path';
|
|
695
|
-
|
|
696
|
-
const YAML_CONFIG_FILENAME = 'config/config.yaml';
|
|
697
|
-
|
|
698
|
-
export default () =>
|
|
699
|
-
yaml.load(readFileSync(join(process.cwd(), YAML_CONFIG_FILENAME), 'utf8')) as Record<string, any>;
|
|
700
|
-
`);
|
|
701
|
-
return tree;
|
|
702
|
-
};
|
|
703
|
-
}
|
|
704
|
-
function updateConfigYaml(sel) {
|
|
705
|
-
return (tree) => {
|
|
706
|
-
const CONFIG_PATH = 'config/config.yaml';
|
|
707
|
-
const block = buildConfigYaml(sel);
|
|
708
|
-
if (!tree.exists(CONFIG_PATH)) {
|
|
709
|
-
tree.create(CONFIG_PATH, block);
|
|
710
|
-
return tree;
|
|
711
|
-
}
|
|
712
|
-
const existing = tree.read(CONFIG_PATH).toString('utf-8');
|
|
713
|
-
const SECTION_KEYS = ['app:', 'auth-providers:', 'broker:', 'topics:', ...(sel.gatewayConfig ? ['gateway:'] : [])];
|
|
714
|
-
let toAppend = '';
|
|
715
|
-
for (const key of SECTION_KEYS) {
|
|
716
|
-
if (!existing.includes(key)) {
|
|
717
|
-
const section = extractYamlSection(block, key);
|
|
718
|
-
if (section)
|
|
719
|
-
toAppend += '\n' + section + '\n';
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
if (toAppend.length > 0) {
|
|
723
|
-
tree.overwrite(CONFIG_PATH, existing.trimEnd() + '\n' + toAppend);
|
|
724
|
-
}
|
|
725
|
-
return tree;
|
|
726
|
-
};
|
|
727
|
-
}
|
|
728
|
-
function extractYamlSection(yaml, sectionKey) {
|
|
729
|
-
const lines = yaml.split('\n');
|
|
730
|
-
const startIdx = lines.findIndex((l) => l.startsWith(sectionKey));
|
|
731
|
-
if (startIdx === -1)
|
|
732
|
-
return '';
|
|
733
|
-
const endIdx = lines.findIndex((l, i) => i > startIdx && l.length > 0 && !l.startsWith(' ') && !l.startsWith('#'));
|
|
734
|
-
const sectionLines = endIdx === -1 ? lines.slice(startIdx) : lines.slice(startIdx, endIdx);
|
|
735
|
-
return sectionLines.join('\n').trimEnd();
|
|
736
|
-
}
|
|
737
|
-
function addModulesToAppModule(sel) {
|
|
738
|
-
return (tree) => {
|
|
739
|
-
const candidatePaths = ['/src/app.module.ts', '/app/app.module.ts', 'src/app.module.ts', 'app/app.module.ts'];
|
|
740
|
-
let modulePath = candidatePaths.find((p) => tree.exists(p)) || findFileInTree(tree, 'app.module.ts');
|
|
741
|
-
if (!modulePath) {
|
|
742
|
-
console.warn('[nest-add] app.module.ts not found: AppModule wiring skipped.');
|
|
743
|
-
return tree;
|
|
744
|
-
}
|
|
745
|
-
const raw = tree.read(modulePath);
|
|
746
|
-
if (!raw)
|
|
747
|
-
return tree;
|
|
748
|
-
let content = raw.toString('utf-8');
|
|
749
|
-
if (content.includes('BrokerModule.forRootAsync'))
|
|
750
|
-
return tree;
|
|
751
|
-
if (!content.includes('@open-rlb/nestjs-amqp')) {
|
|
752
|
-
const pos = findLastImportEndIndex(content);
|
|
753
|
-
content = content.slice(0, pos) + '\n' + buildImportStatements(sel) + content.slice(pos);
|
|
754
|
-
}
|
|
755
|
-
const entries = buildModuleEntries(sel);
|
|
756
|
-
for (let i = entries.length - 1; i >= 0; i--) {
|
|
757
|
-
content = insertIntoImportsArray(content, entries[i]);
|
|
758
|
-
}
|
|
759
|
-
tree.overwrite(modulePath, content);
|
|
760
|
-
return tree;
|
|
761
|
-
};
|
|
762
|
-
}
|
|
763
|
-
function configureMainForGateway() {
|
|
764
|
-
return (tree) => {
|
|
765
|
-
const candidatePaths = ['/src/main.ts', '/app/main.ts', 'src/main.ts', 'app/main.ts'];
|
|
766
|
-
const mainPath = candidatePaths.find((p) => tree.exists(p)) || findFileInTree(tree, 'main.ts');
|
|
767
|
-
if (!mainPath) {
|
|
768
|
-
console.warn('[nest-add] main.ts not found: enable rawBody + WsAdapter manually.');
|
|
769
|
-
return tree;
|
|
770
|
-
}
|
|
771
|
-
let content = tree.read(mainPath).toString('utf-8');
|
|
772
|
-
if (content.includes('WsAdapter'))
|
|
773
|
-
return tree;
|
|
774
|
-
const pos = findLastImportEndIndex(content);
|
|
775
|
-
content = content.slice(0, pos) + "\nimport { WsAdapter } from '@nestjs/platform-ws';" + content.slice(pos);
|
|
776
|
-
const m = content.match(/const\s+(\w+)\s*=\s*await\s+NestFactory\.create\(\s*([A-Za-z0-9_]+)\s*(,\s*\{[^}]*\})?\s*\)\s*;?/);
|
|
777
|
-
if (m) {
|
|
778
|
-
const appVar = m[1];
|
|
779
|
-
const moduleArg = m[2];
|
|
780
|
-
const replacement = `const ${appVar} = await NestFactory.create(${moduleArg}, { rawBody: true });\n ${appVar}.useWebSocketAdapter(new WsAdapter(${appVar}));\n ${appVar}.enableShutdownHooks();`;
|
|
781
|
-
content = content.replace(m[0], replacement);
|
|
782
|
-
}
|
|
783
|
-
else {
|
|
784
|
-
console.warn('[nest-add] NestFactory.create() not found in main.ts: add { rawBody: true } + useWebSocketAdapter manually.');
|
|
785
|
-
}
|
|
786
|
-
tree.overwrite(mainPath, content);
|
|
787
|
-
return tree;
|
|
788
|
-
};
|
|
789
|
-
}
|
|
790
|
-
function insertIntoImportsArray(source, moduleEntry) {
|
|
791
|
-
const match = /imports\s*:\s*\[/.exec(source);
|
|
792
|
-
if (!match)
|
|
793
|
-
return source;
|
|
794
|
-
const openBracketPos = source.indexOf('[', match.index);
|
|
795
|
-
let depth = 0;
|
|
796
|
-
let closeBracketPos = -1;
|
|
797
|
-
for (let i = openBracketPos; i < source.length; i++) {
|
|
798
|
-
if (source[i] === '[')
|
|
799
|
-
depth++;
|
|
800
|
-
else if (source[i] === ']') {
|
|
801
|
-
depth--;
|
|
802
|
-
if (depth === 0) {
|
|
803
|
-
closeBracketPos = i;
|
|
804
|
-
break;
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
if (closeBracketPos === -1)
|
|
809
|
-
return source;
|
|
810
|
-
const arrayContent = source.slice(openBracketPos + 1, closeBracketPos).trim();
|
|
811
|
-
const newArrayContent = arrayContent.length === 0
|
|
812
|
-
? `\n ${moduleEntry},\n `
|
|
813
|
-
: `\n ${moduleEntry},\n ${arrayContent}\n `;
|
|
814
|
-
return source.slice(0, openBracketPos + 1) + newArrayContent + source.slice(closeBracketPos);
|
|
815
|
-
}
|
|
816
|
-
function findLastImportEndIndex(source) {
|
|
817
|
-
const importRegex = /^import\s+.+from\s+['"][^'"]+['"];?\s*$/gm;
|
|
818
|
-
let lastEnd = 0;
|
|
819
|
-
let match;
|
|
820
|
-
while ((match = importRegex.exec(source)) !== null) {
|
|
821
|
-
lastEnd = match.index + match[0].length;
|
|
822
|
-
}
|
|
823
|
-
return lastEnd;
|
|
824
|
-
}
|
|
825
|
-
function findFileInTree(tree, fileName) {
|
|
826
|
-
let found;
|
|
827
|
-
tree.visit((path) => {
|
|
828
|
-
if (!found && path.endsWith(`/${fileName}`))
|
|
829
|
-
found = path;
|
|
830
|
-
});
|
|
831
|
-
return found;
|
|
832
|
-
}
|
|
833
|
-
function updatePackageJson(sel) {
|
|
834
|
-
return (host) => {
|
|
835
|
-
if (!host.exists('package.json'))
|
|
836
|
-
return host;
|
|
837
|
-
return updateJsonFile(host, 'package.json', (packageJson) => {
|
|
838
|
-
packageJson.dependencies = packageJson.dependencies || {};
|
|
839
|
-
const add = (name, version) => {
|
|
840
|
-
if (!packageJson.dependencies[name])
|
|
841
|
-
packageJson.dependencies[name] = version;
|
|
842
|
-
};
|
|
843
|
-
add('@nestjs/config', '^4.0.4');
|
|
844
|
-
add('js-yaml', '^4.1.0');
|
|
845
|
-
if (sel.gatewayConfig) {
|
|
846
|
-
add('@nestjs/axios', '^4.0.1');
|
|
847
|
-
add('@nestjs/platform-ws', '^11.0.1');
|
|
848
|
-
add('@nestjs/websockets', '^11.0.1');
|
|
849
|
-
add('ws', '^8.21.0');
|
|
850
|
-
}
|
|
851
|
-
});
|
|
852
|
-
};
|
|
853
|
-
}
|
|
854
|
-
function updateJsonFile(host, path, callback) {
|
|
855
|
-
const source = host.read(path);
|
|
856
|
-
if (source) {
|
|
857
|
-
const json = (0, jsonc_parser_1.parse)(source.toString('utf-8'));
|
|
858
|
-
callback(json);
|
|
859
|
-
host.overwrite(path, JSON.stringify(json, null, 2));
|
|
860
|
-
}
|
|
861
|
-
return host;
|
|
862
|
-
}
|
|
863
86
|
//# sourceMappingURL=index.js.map
|