@govuk-pay/cli 0.0.30 → 0.0.31

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.
Files changed (47) hide show
  1. package/package.json +5 -2
  2. package/resources/pay-local/config/localstack/init-aws.sh +70 -0
  3. package/resources/pay-local/config/postgres/docker-entrypoint-initdb.d/make_payments_databases.sql +26 -0
  4. package/resources/pay-local/config/service_config.yaml +193 -0
  5. package/resources/pay-local/config/services/adminusers.env +49 -0
  6. package/resources/pay-local/config/services/cardid.env +2 -0
  7. package/resources/pay-local/config/services/connector.env +70 -0
  8. package/resources/pay-local/config/services/demo-service.env +10 -0
  9. package/resources/pay-local/config/services/egress/squid.conf +47 -0
  10. package/resources/pay-local/config/services/frontend.env +12 -0
  11. package/resources/pay-local/config/services/java_app.env +1 -0
  12. package/resources/pay-local/config/services/ledger.env +10 -0
  13. package/resources/pay-local/config/services/products-ui.env +14 -0
  14. package/resources/pay-local/config/services/products.env +25 -0
  15. package/resources/pay-local/config/services/publicapi.env +13 -0
  16. package/resources/pay-local/config/services/publicauth.env +13 -0
  17. package/resources/pay-local/config/services/selfservice.env +21 -0
  18. package/resources/pay-local/config/services/ssl/certs/frontend-proxy.crt +18 -0
  19. package/resources/pay-local/config/services/ssl/certs/products-ui-proxy.crt +20 -0
  20. package/resources/pay-local/config/services/ssl/certs/publicapi-proxy.crt +18 -0
  21. package/resources/pay-local/config/services/ssl/certs/selfservice-proxy.crt +20 -0
  22. package/resources/pay-local/config/services/ssl/certs/stubs-proxy.crt +18 -0
  23. package/resources/pay-local/config/services/ssl/keys/frontend-proxy.key +28 -0
  24. package/resources/pay-local/config/services/ssl/keys/products-ui-proxy.key +28 -0
  25. package/resources/pay-local/config/services/ssl/keys/publicapi-proxy.key +28 -0
  26. package/resources/pay-local/config/services/ssl/keys/selfservice-proxy.key +28 -0
  27. package/resources/pay-local/config/services/ssl/keys/stubs-proxy.key +28 -0
  28. package/resources/pay-local/config/services/ssl/make-selfsigned.sh +2 -0
  29. package/resources/pay-local/config/services/stubs.env +12 -0
  30. package/resources/pay-local/config/services/toolbox.env +6 -0
  31. package/resources/pay-local/config/services/webhooks.env +9 -0
  32. package/resources/pay-local/templates/docker-compose.hbs +276 -0
  33. package/resources/usageDetails.txt +1 -0
  34. package/src/commands/local/config/default_config_setup.js +13 -0
  35. package/src/commands/local/config/last_up_record.js +50 -0
  36. package/src/commands/local/config/pay_local_cluster.js +221 -0
  37. package/src/commands/local/config/renderer.js +46 -0
  38. package/src/commands/local/docker_compose_controller.js +24 -0
  39. package/src/commands/local/subcommands/down.js +56 -0
  40. package/src/commands/local/subcommands/nuke.js +28 -0
  41. package/src/commands/local/subcommands/otp.js +27 -0
  42. package/src/commands/local/subcommands/restart.js +35 -0
  43. package/src/commands/local/subcommands/up.js +55 -0
  44. package/src/commands/local.js +98 -0
  45. package/src/core/commandRouter.js +4 -0
  46. package/src/util/configs.js +47 -0
  47. package/src/util/md5.js +16 -0
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadClusterConfig = exports.PayLocalCluster = void 0;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const yaml_1 = __importDefault(require("yaml"));
10
+ const md5_js_1 = require("../../../util/md5.js");
11
+ // This is the complete cluster configuration that will be used as the context for template rendering
12
+ class PayLocalCluster {
13
+ name;
14
+ payServices;
15
+ dbServices;
16
+ reverseProxyServices;
17
+ egressProxy;
18
+ javaApps;
19
+ nodeApps;
20
+ localJavaApps;
21
+ localNodeApps;
22
+ remoteJavaApps;
23
+ remoteNodeApps;
24
+ anyJavaApps;
25
+ anyNodeApps;
26
+ workspace;
27
+ requiresLocalStack;
28
+ requiresRedis;
29
+ requiresEgressProxy;
30
+ serviceURLEnvVars;
31
+ mountLocalNodeApps;
32
+ noProxyEnvVar;
33
+ defaultServiceConfigsPath;
34
+ environmentOverridesPath;
35
+ constructor(clusterConfig) {
36
+ this.name = clusterConfig.name;
37
+ this.workspace = clusterConfig.workspace;
38
+ this.payServices = clusterConfig.payServices;
39
+ this.dbServices = clusterConfig.dbServices;
40
+ this.reverseProxyServices = clusterConfig.reverseProxyServices;
41
+ this.egressProxy = clusterConfig.egressProxy;
42
+ this.javaApps = this.payServices.filter((service) => service.serviceType === PayServiceType.Java);
43
+ this.nodeApps = this.payServices.filter((service) => service.serviceType === PayServiceType.Node);
44
+ this.remoteJavaApps = this.javaApps.filter((service) => service.localBuild);
45
+ this.remoteNodeApps = this.nodeApps.filter((service) => service.localBuild);
46
+ this.localJavaApps = this.javaApps.filter((service) => service.localBuild);
47
+ this.localNodeApps = this.nodeApps.filter((service) => service.localBuild);
48
+ this.anyJavaApps = this.javaApps.length >= 0;
49
+ this.anyNodeApps = this.nodeApps.length >= 0;
50
+ this.requiresLocalStack = this.payServices.some((service) => service.requiresLocalStack);
51
+ this.requiresRedis = this.payServices.some((service) => service.usesRedis);
52
+ this.requiresEgressProxy = this.egressProxy !== undefined;
53
+ this.mountLocalNodeApps = clusterConfig.mountLocalNodeApps;
54
+ this.serviceURLEnvVars = [
55
+ this.javaApps.map((javaService) => envVarForJavaService(javaService)),
56
+ this.nodeApps.map((nodeService) => envVarForNodeService(nodeService))
57
+ ].flat();
58
+ this.noProxyEnvVar = [
59
+ 'localhost',
60
+ '127.0.0.1',
61
+ '172.18.0.253',
62
+ 'localstack',
63
+ 'redis',
64
+ this.payServices.map((service) => service.name),
65
+ this.dbServices.map((service) => service.name),
66
+ this.reverseProxyServices.map((service) => service.name),
67
+ this.egressProxy === undefined ? [] : this.egressProxy.name
68
+ ].flat().join(',');
69
+ this.environmentOverridesPath = clusterConfig.environmentOverridesPath;
70
+ this.defaultServiceConfigsPath = clusterConfig.defaultServiceConfigsPath;
71
+ }
72
+ }
73
+ exports.PayLocalCluster = PayLocalCluster;
74
+ function envVarForJavaService(service) {
75
+ return {
76
+ name: `${service.name.toUpperCase()}_URL`,
77
+ value: `http://${service.name}:${service.port}`
78
+ };
79
+ }
80
+ function envVarForNodeService(service) {
81
+ return {
82
+ name: `${service.name.toUpperCase()}_URL`,
83
+ value: `http://localhost:${service.port}`
84
+ };
85
+ }
86
+ var PayServiceType;
87
+ (function (PayServiceType) {
88
+ PayServiceType["Java"] = "java";
89
+ PayServiceType["Node"] = "node";
90
+ })(PayServiceType || (PayServiceType = {}));
91
+ function loadClusterConfig(options, workspace, defaultServiceConfigsPath, environmentOverridesPath) {
92
+ const servicesConfigPath = node_path_1.default.join(__dirname, '..', '..', '..', '..', 'resources', 'pay-local', 'config', 'service_config.yaml');
93
+ const serviceConfigFileContents = node_fs_1.default.readFileSync(servicesConfigPath, 'utf8');
94
+ const servicesConfig = yaml_1.default.parse(serviceConfigFileContents);
95
+ const payServices = [];
96
+ for (const [, serviceConfig] of Object.entries(servicesConfig)) {
97
+ if (includeAppInCluster(options.cluster, options, serviceConfig)) {
98
+ payServices.push(payServiceFromPayServiceConfig(serviceConfig, options, environmentOverridesPath));
99
+ }
100
+ }
101
+ const dbServices = [];
102
+ for (const payService of payServices) {
103
+ if (payService.payServiceConfig.db) {
104
+ dbServices.push(dbServiceFromPayServiceConfig(payService.payServiceConfig));
105
+ }
106
+ }
107
+ const reverseProxyServices = [];
108
+ if (options.proxy) {
109
+ for (const payService of payServices) {
110
+ if (payService.hasProxy) {
111
+ reverseProxyServices.push(proxyServiceFromPayServiceConfig(payService.payServiceConfig, workspace));
112
+ }
113
+ }
114
+ }
115
+ const clusterConfig = {
116
+ name: options.cluster,
117
+ payServices,
118
+ dbServices,
119
+ reverseProxyServices,
120
+ workspace,
121
+ mountLocalNodeApps: options.mount_local_node_apps,
122
+ defaultServiceConfigsPath,
123
+ environmentOverridesPath
124
+ };
125
+ return new PayLocalCluster(clusterConfig);
126
+ }
127
+ exports.loadClusterConfig = loadClusterConfig;
128
+ function includeAppInCluster(cluster, options, serviceConfig) {
129
+ if (cluster === 'all' || cluster === 'nuke' || serviceConfig.clusters.includes(cluster)) {
130
+ if (options.apps.length === 0 || options.apps.includes(serviceConfig.name)) {
131
+ return true;
132
+ }
133
+ }
134
+ return false;
135
+ }
136
+ function payServiceFromPayServiceConfig(config, upOptions, environmentOverridesPath) {
137
+ const ports = [config.port];
138
+ if (config.admin_port !== undefined)
139
+ ports.push(config.admin_port);
140
+ const localBuild = upOptions.local.includes(config.name);
141
+ const sqsQueues = [];
142
+ if (config.queues !== undefined) {
143
+ for (const [envVarName, queueName] of Object.entries(config.queues)) {
144
+ sqsQueues.push({ envVarName, queueName });
145
+ }
146
+ }
147
+ const snsTopics = [];
148
+ if (config.sns_topics !== undefined) {
149
+ for (const [envVarName, topicName] of Object.entries(config.sns_topics)) {
150
+ snsTopics.push({ envVarName, topicName });
151
+ }
152
+ }
153
+ return {
154
+ name: config.name,
155
+ containerName: config.name,
156
+ port: config.port,
157
+ adminPort: config.admin_port,
158
+ debugPort: config.debug_port,
159
+ volumes: new Map(),
160
+ serviceType: config.type,
161
+ localBuild,
162
+ usesRedis: config.uses_redis === undefined ? false : config.uses_redis,
163
+ sqsQueues,
164
+ hasSQSQueues: sqsQueues.length > 0,
165
+ snsTopics,
166
+ hasSNSTopics: snsTopics.length > 0,
167
+ hasProxy: config.proxy === undefined ? false : config.proxy,
168
+ hasDB: config.db,
169
+ useEgressProxy: config.can_use_egress_proxy === undefined ? false : config.can_use_egress_proxy && upOptions.with_egress_proxy,
170
+ dependsOn: config.db ? [`${config.name}_db`] : [],
171
+ expose: [],
172
+ payServiceConfig: config,
173
+ imageTag: localBuild ? 'local' : 'latest-master',
174
+ requiresLocalStack: sqsQueues.length > 0 || snsTopics.length > 0,
175
+ entrypointOverrideLocal: config.entrypoint_override_local,
176
+ environmentOverrideFilePath: node_path_1.default.join(environmentOverridesPath, `${config.name}.env`)
177
+ };
178
+ }
179
+ function dbServiceFromPayServiceConfig(config) {
180
+ if (config.db_port === undefined) {
181
+ throw new Error(`The db specification for ${config.name} (which sets db: true) is missing the db_port specification`);
182
+ }
183
+ return {
184
+ name: `${config.name}_db`,
185
+ containerName: `${config.name}_db`,
186
+ dependsOn: [],
187
+ port: config.db_port,
188
+ expose: [],
189
+ volumes: new Map()
190
+ };
191
+ }
192
+ function proxyServiceFromPayServiceConfig(config, workspace) {
193
+ if (config.proxy_port === undefined) {
194
+ throw new Error(`The proxy specification for ${config.name} (which sets proxy: true) is missing the proxy_port specification`);
195
+ }
196
+ // TODO: Deal with naxsi rules file and md5s
197
+ const naxsiSourcePath = naxsiConfigPathForPayServiceConfig(config, workspace);
198
+ return {
199
+ name: `${config.name}-proxy`,
200
+ containerName: `${config.name}-proxy`,
201
+ appName: config.name,
202
+ appPort: config.port,
203
+ port: config.proxy_port,
204
+ dependsOn: [],
205
+ expose: [],
206
+ volumes: new Map(),
207
+ naxsiRulesSourceFile: naxsiSourcePath,
208
+ naxsiRulesSourceFileMD5: (0, md5_js_1.md5File)(naxsiSourcePath)
209
+ };
210
+ }
211
+ function naxsiConfigPathForPayServiceConfig(service, workspace) {
212
+ const naxsiSourcePath = node_path_1.default.join(workspace, 'pay-infra', 'provisioning', 'terraform', 'modules', 'pay_microservices_v2', service.name, 'files', `${service.name}.naxsi`);
213
+ const nasxiSourcePathStat = node_fs_1.default.statSync(naxsiSourcePath, { throwIfNoEntry: false });
214
+ if (nasxiSourcePathStat === undefined) {
215
+ throw new Error(`Reverse proxies were requested, but the naxsi config file for '${service.name}' (${naxsiSourcePath}') was not found. Perhaps you need to checkout pay-infra into your workspace directory '${workspace}'?`);
216
+ }
217
+ if (!nasxiSourcePathStat.isFile()) {
218
+ throw new Error(`Reverse proxies were requested, but the naxsi config file for '${service.name}' (${naxsiSourcePath}') is not a regular file. Perhaps you need to checkout pay-infra into your workspace directory '${workspace}'?`);
219
+ }
220
+ return naxsiSourcePath;
221
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.renderDockerCompose = void 0;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const handlebars_1 = __importDefault(require("handlebars"));
10
+ /**
11
+ * Renders the 'if' path if both a and b are true, otherwise renders the 'else' path
12
+ */
13
+ handlebars_1.default.registerHelper('ifBoth', (a, b, options) => {
14
+ if (a && b) {
15
+ return options.fn(this);
16
+ }
17
+ else {
18
+ return options.inverse(this);
19
+ }
20
+ });
21
+ /**
22
+ * Renders the 'if' path if the file specified exists, otherwise renders the 'else' path
23
+ */
24
+ handlebars_1.default.registerHelper('ifFileExists', (filePath, options) => {
25
+ if (node_fs_1.default.existsSync(filePath)) {
26
+ return options.fn(this);
27
+ }
28
+ else {
29
+ return options.inverse(this);
30
+ }
31
+ });
32
+ /**
33
+ * Renders the handlebars template, writes it into the destination directory and returns the path
34
+ * to the file
35
+ */
36
+ function renderDockerCompose(cluster, destinationDirectory) {
37
+ const tempalteSource = loadTemplate();
38
+ const template = handlebars_1.default.compile(tempalteSource);
39
+ const destinationFilePath = `${node_fs_1.default.realpathSync(destinationDirectory)}/${cluster.name}.yaml`;
40
+ node_fs_1.default.writeFileSync(destinationFilePath, template(cluster), { mode: 0o660 });
41
+ return destinationFilePath;
42
+ }
43
+ exports.renderDockerCompose = renderDockerCompose;
44
+ function loadTemplate() {
45
+ return node_fs_1.default.readFileSync(node_path_1.default.join(__dirname, '..', '..', '..', '..', 'resources', 'pay-local', 'templates', 'docker-compose.hbs'), 'utf8');
46
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_child_process_1 = require("node:child_process");
4
+ const dockerComposeController = {
5
+ up(composeFilePath) {
6
+ (0, node_child_process_1.spawnSync)('docker', ['compose', '--file', composeFilePath, 'up', '--detach', '--wait'], { shell: true, stdio: 'inherit' });
7
+ },
8
+ down(composeFilePath) {
9
+ (0, node_child_process_1.spawnSync)('docker', ['compose', '--file', composeFilePath, 'down'], { shell: true, stdio: 'inherit' });
10
+ },
11
+ nuke(composeFilePath) {
12
+ (0, node_child_process_1.spawnSync)('docker', ['compose', '--file', composeFilePath, 'down', '--volumes'], { shell: true, stdio: 'inherit' });
13
+ },
14
+ pull(composeFilePath) {
15
+ (0, node_child_process_1.spawnSync)('docker', ['compose', '--file', composeFilePath, 'pull'], { shell: true, stdio: 'inherit' });
16
+ },
17
+ restart(composeFilePath, service) {
18
+ (0, node_child_process_1.spawnSync)('docker', ['compose', '--file', composeFilePath, 'restart', service], { shell: true, stdio: 'inherit' });
19
+ },
20
+ build(composeFilePath) {
21
+ (0, node_child_process_1.spawnSync)('docker', ['compose', '--file', composeFilePath, 'build'], { shell: true, stdio: 'inherit' });
22
+ }
23
+ };
24
+ exports.default = dockerComposeController;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.composeFileToDown = exports.ENVIRONMENT_OVERRIDES_PATH = exports.DOCKER_COMPOSE_SERVICES_CONFIG_PATH = exports.DOCKER_COMPOSE_RENDERED_TEMPALTES_PATH = void 0;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const configs_js_1 = require("../../../util/configs.js");
10
+ const last_up_record_js_1 = require("../config/last_up_record.js");
11
+ const docker_compose_controller_js_1 = __importDefault(require("../docker_compose_controller.js"));
12
+ exports.DOCKER_COMPOSE_RENDERED_TEMPALTES_PATH = node_path_1.default.join('local', 'docker-compose', 'rendered-templates');
13
+ exports.DOCKER_COMPOSE_SERVICES_CONFIG_PATH = node_path_1.default.join('local', 'docker-compose', 'default-configs-DO-NOT-EDIT');
14
+ exports.ENVIRONMENT_OVERRIDES_PATH = node_path_1.default.join('local', 'environment-overrides');
15
+ async function DownHandler(options) {
16
+ const renderedTempaltesPath = (0, configs_js_1.ensureConfigDirectory)(exports.DOCKER_COMPOSE_RENDERED_TEMPALTES_PATH);
17
+ const args = parseArguments(options);
18
+ const composeFilePath = composeFileToDown(args, renderedTempaltesPath);
19
+ const composeFileName = node_path_1.default.basename(composeFilePath);
20
+ const clusterToDown = composeFileName.substring(0, composeFileName.lastIndexOf('.yaml'));
21
+ console.log(`Bringing down cluster ${clusterToDown}`);
22
+ docker_compose_controller_js_1.default.down(composeFilePath);
23
+ }
24
+ exports.default = DownHandler;
25
+ function composeFileToDown(downOptions, renderedTemplatesPath) {
26
+ if (downOptions.cluster !== undefined) {
27
+ const clusterFilePath = node_path_1.default.resolve(renderedTemplatesPath, `${downOptions.cluster}.yaml`);
28
+ if (node_fs_1.default.existsSync(clusterFilePath)) {
29
+ return clusterFilePath;
30
+ }
31
+ throw new Error(`Cannot bring down cluster '${downOptions.cluster}' since it does not have a rendered docker-compose template in ${renderedTemplatesPath}.\n` +
32
+ '\n' +
33
+ `Perhaps you don't have a cluster running, or the cluster that is running isn't ${downOptions.cluster}\n`);
34
+ }
35
+ const mostRecentUpFile = (0, last_up_record_js_1.getLastUp)(renderedTemplatesPath);
36
+ if (mostRecentUpFile !== undefined) {
37
+ return mostRecentUpFile;
38
+ }
39
+ const defaultDownFile = node_path_1.default.join(renderedTemplatesPath, 'all.yaml');
40
+ if (node_fs_1.default.existsSync(defaultDownFile)) {
41
+ return defaultDownFile;
42
+ }
43
+ throw new Error('Could not determine which cluster to bring down.\n' +
44
+ '\n' +
45
+ ' * You did not specify a cluster with --cluster.\n' +
46
+ " * There is no valid record of a most recent 'up'\n" +
47
+ " * The 'all' cluster rendered docker-compose.yaml file does not exist.\n" +
48
+ '\n' +
49
+ 'Perhaps you do not have a running cluster, or you need to `pay local down --cluster <cluster>` specifying the last cluster you launched\n');
50
+ }
51
+ exports.composeFileToDown = composeFileToDown;
52
+ function parseArguments(_options) {
53
+ return {
54
+ cluster: 'card'
55
+ };
56
+ }
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ENVIRONMENT_OVERRIDES_PATH = exports.DOCKER_COMPOSE_SERVICES_CONFIG_PATH = exports.DOCKER_COMPOSE_RENDERED_TEMPALTES_PATH = void 0;
7
+ const up_js_1 = __importDefault(require("./up.js"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const docker_compose_controller_js_1 = __importDefault(require("../docker_compose_controller.js"));
10
+ exports.DOCKER_COMPOSE_RENDERED_TEMPALTES_PATH = node_path_1.default.join('local', 'docker-compose', 'rendered-templates');
11
+ exports.DOCKER_COMPOSE_SERVICES_CONFIG_PATH = node_path_1.default.join('local', 'docker-compose', 'default-configs-DO-NOT-EDIT');
12
+ exports.ENVIRONMENT_OVERRIDES_PATH = node_path_1.default.join('local', 'environment-overrides');
13
+ async function NukeHandler() {
14
+ const upOptions = {
15
+ cluster: 'nuke',
16
+ proxy: true,
17
+ with_egress_proxy: true,
18
+ apps: [],
19
+ local: [],
20
+ rebuild: false,
21
+ pull: false,
22
+ mount_local_node_apps: false
23
+ };
24
+ const renderedTemplatePath = await (0, up_js_1.default)(upOptions, true);
25
+ console.log('Nuking cluster');
26
+ docker_compose_controller_js_1.default.nuke(renderedTemplatePath);
27
+ }
28
+ exports.default = NukeHandler;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_child_process_1 = require("node:child_process");
4
+ const totp_generator_1 = require("totp-generator");
5
+ async function OTPHandler(options) {
6
+ const args = parseArguments(options);
7
+ const { otp } = totp_generator_1.TOTP.generate(args.secret);
8
+ console.log(otp);
9
+ copyToClipboard(otp);
10
+ console.log(`📋 “${otp}” copied to clipboard`);
11
+ }
12
+ exports.default = OTPHandler;
13
+ function parseArguments(options) {
14
+ if (options.length !== 1) {
15
+ help();
16
+ throw new Error('No key specified');
17
+ }
18
+ return { secret: options[0].trimEnd() };
19
+ }
20
+ function copyToClipboard(otp) {
21
+ const proc = (0, node_child_process_1.spawn)('pbcopy');
22
+ proc.stdin.write(otp);
23
+ proc.stdin.end();
24
+ }
25
+ function help() {
26
+ console.log('Usage: pay local otp <otp_secret_key>');
27
+ }
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DOCKER_COMPOSE_RENDERED_TEMPALTES_PATH = void 0;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const configs_js_1 = require("../../../util/configs.js");
9
+ const last_up_record_js_1 = require("../config/last_up_record.js");
10
+ const docker_compose_controller_js_1 = __importDefault(require("../docker_compose_controller.js"));
11
+ exports.DOCKER_COMPOSE_RENDERED_TEMPALTES_PATH = node_path_1.default.join('local', 'docker-compose', 'rendered-templates');
12
+ async function DownHandler(options) {
13
+ const renderedTempaltesPath = (0, configs_js_1.ensureConfigDirectory)(exports.DOCKER_COMPOSE_RENDERED_TEMPALTES_PATH);
14
+ const args = parseArguments(options);
15
+ const composeFilePath = (0, last_up_record_js_1.getLastUp)(renderedTempaltesPath);
16
+ if (composeFilePath === undefined) {
17
+ throw new Error('Cannot find the docker-compose file for the running cluster');
18
+ }
19
+ const composeFileName = node_path_1.default.basename(composeFilePath);
20
+ const clusterName = composeFileName.substring(0, composeFileName.lastIndexOf('.yaml'));
21
+ console.log(`\nRestarting '${args.service} in cluster ${clusterName}`);
22
+ docker_compose_controller_js_1.default.restart(composeFilePath, args.service);
23
+ }
24
+ exports.default = DownHandler;
25
+ function parseArguments(options) {
26
+ if (options.length !== 1) {
27
+ throw new Error('Incorrect number of arguments, expected 1 to restart command\n' +
28
+ '\n' +
29
+ ' Usage: pay local restart <service>\n' +
30
+ '\n');
31
+ }
32
+ return {
33
+ service: options[0]
34
+ };
35
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ENVIRONMENT_OVERRIDES_PATH = exports.DOCKER_COMPOSE_SERVICES_CONFIG_PATH = exports.DOCKER_COMPOSE_RENDERED_TEMPALTES_PATH = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const docker_compose_controller_js_1 = __importDefault(require("../docker_compose_controller.js"));
9
+ const configs_js_1 = require("../../../util/configs.js");
10
+ const pay_local_cluster_js_1 = require("../config/pay_local_cluster.js");
11
+ const renderer_js_1 = require("../config/renderer.js");
12
+ const last_up_record_js_1 = require("../config/last_up_record.js");
13
+ const default_config_setup_js_1 = require("../config/default_config_setup.js");
14
+ exports.DOCKER_COMPOSE_RENDERED_TEMPALTES_PATH = path_1.default.join('local', 'docker-compose', 'rendered-templates');
15
+ exports.DOCKER_COMPOSE_SERVICES_CONFIG_PATH = path_1.default.join('local', 'docker-compose', 'default-configs-DO-NOT-EDIT');
16
+ exports.ENVIRONMENT_OVERRIDES_PATH = path_1.default.join('local', 'environment-overrides');
17
+ async function UpHandler(options, onlyWriteConfig = false) {
18
+ const workspace = (0, configs_js_1.workspaceEnvVar)();
19
+ const renderedTemplatesPath = (0, configs_js_1.ensureConfigDirectory)(exports.DOCKER_COMPOSE_RENDERED_TEMPALTES_PATH);
20
+ const composeServicesConfigPath = (0, configs_js_1.ensureConfigDirectory)(exports.DOCKER_COMPOSE_SERVICES_CONFIG_PATH);
21
+ const environmentOverridesPath = (0, configs_js_1.ensureConfigDirectory)(exports.ENVIRONMENT_OVERRIDES_PATH);
22
+ (0, default_config_setup_js_1.copyDefaultConfigs)(composeServicesConfigPath);
23
+ const args = options instanceof Array ? parseArguments(options) : options;
24
+ const clusterConfig = (0, pay_local_cluster_js_1.loadClusterConfig)(args, workspace, composeServicesConfigPath, environmentOverridesPath);
25
+ const renderedTemplatePath = (0, renderer_js_1.renderDockerCompose)(clusterConfig, renderedTemplatesPath);
26
+ console.log(`docker-compose yaml written to ${renderedTemplatePath}`);
27
+ (0, last_up_record_js_1.recordUp)(renderedTemplatesPath, clusterConfig);
28
+ if (onlyWriteConfig) {
29
+ return renderedTemplatePath;
30
+ }
31
+ if (args.pull) {
32
+ console.log(`\nPulling containers for cluster ${clusterConfig.name}`);
33
+ docker_compose_controller_js_1.default.pull(renderedTemplatePath);
34
+ }
35
+ if (args.local.length > 0 && args.rebuild) {
36
+ console.log(`\nBuilding all local apps for cluster ${clusterConfig.name}`);
37
+ docker_compose_controller_js_1.default.build(renderedTemplatePath);
38
+ }
39
+ console.log(`\nGoing up with cluster ${clusterConfig.name}`);
40
+ docker_compose_controller_js_1.default.up(renderedTemplatePath);
41
+ return renderedTemplatePath;
42
+ }
43
+ exports.default = UpHandler;
44
+ function parseArguments(_options) {
45
+ return {
46
+ cluster: 'card',
47
+ apps: [],
48
+ local: ['cardid', 'frontend'],
49
+ proxy: true,
50
+ with_egress_proxy: false,
51
+ rebuild: true,
52
+ pull: true,
53
+ mount_local_node_apps: false
54
+ };
55
+ }
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const standardContent_js_1 = require("../core/standardContent.js");
7
+ const legacy_1 = __importDefault(require("../commands/legacy"));
8
+ const otp_js_1 = __importDefault(require("./local/subcommands/otp.js"));
9
+ const up_js_1 = __importDefault(require("./local/subcommands/up.js"));
10
+ const down_js_1 = __importDefault(require("./local/subcommands/down.js"));
11
+ const nuke_js_1 = __importDefault(require("./local/subcommands/nuke.js"));
12
+ const restart_js_1 = __importDefault(require("./local/subcommands/restart.js"));
13
+ const SUBCOMMANDS_NOT_YET_IMPLEMENTED = [
14
+ 'account',
15
+ 'browse',
16
+ 'db',
17
+ 'payment',
18
+ 'paymentlink',
19
+ 'token',
20
+ 'url',
21
+ 'user'
22
+ ];
23
+ var LocalCommand;
24
+ (function (LocalCommand) {
25
+ LocalCommand["OTP"] = "otp";
26
+ LocalCommand["Up"] = "up";
27
+ LocalCommand["Launch"] = "launch";
28
+ LocalCommand["Down"] = "down";
29
+ LocalCommand["Restart"] = "restart";
30
+ LocalCommand["Nuke"] = "nuke";
31
+ LocalCommand["Help"] = "help";
32
+ LocalCommand["Unknown"] = "Unknown";
33
+ })(LocalCommand || (LocalCommand = {}));
34
+ async function localHandler(options) {
35
+ // Not everything is implemented yet. Lets hand off to the legacy handler for unimplemented
36
+ // subcommands
37
+ if (options.arguments.length > 0 && SUBCOMMANDS_NOT_YET_IMPLEMENTED.includes(options.arguments[0])) {
38
+ options.commandName = 'local';
39
+ await (0, legacy_1.default)(options);
40
+ return;
41
+ }
42
+ await (0, standardContent_js_1.showHeader)();
43
+ const { subcommand, subcommandOptions } = parseArguments(options);
44
+ switch (subcommand) {
45
+ case LocalCommand.OTP: {
46
+ await (0, otp_js_1.default)(subcommandOptions);
47
+ break;
48
+ }
49
+ case LocalCommand.Up: {
50
+ await (0, up_js_1.default)(subcommandOptions);
51
+ break;
52
+ }
53
+ case LocalCommand.Launch: {
54
+ await (0, up_js_1.default)(subcommandOptions);
55
+ break;
56
+ }
57
+ case LocalCommand.Down: {
58
+ await (0, down_js_1.default)(subcommandOptions);
59
+ break;
60
+ }
61
+ case LocalCommand.Restart: {
62
+ await (0, restart_js_1.default)(subcommandOptions);
63
+ break;
64
+ }
65
+ case LocalCommand.Nuke: {
66
+ await (0, nuke_js_1.default)();
67
+ break;
68
+ }
69
+ case LocalCommand.Help:
70
+ case LocalCommand.Unknown: {
71
+ help();
72
+ break;
73
+ }
74
+ default: {
75
+ help();
76
+ break;
77
+ }
78
+ }
79
+ }
80
+ exports.default = localHandler;
81
+ // TODO: Replace this with yargs
82
+ function parseArguments(options) {
83
+ const subcommandOptions = options.arguments;
84
+ const subcommand = subcommandOptions.shift();
85
+ switch (subcommand) {
86
+ case LocalCommand.OTP: return { subcommand, subcommandOptions };
87
+ case LocalCommand.Up: return { subcommand, subcommandOptions };
88
+ case LocalCommand.Launch: return { subcommand, subcommandOptions };
89
+ case LocalCommand.Down: return { subcommand, subcommandOptions };
90
+ case LocalCommand.Restart: return { subcommand, subcommandOptions };
91
+ case LocalCommand.Nuke: return { subcommand, subcommandOptions };
92
+ case LocalCommand.Help: return { subcommand, subcommandOptions };
93
+ default: return { subcommand: LocalCommand.Unknown, subcommandOptions };
94
+ }
95
+ }
96
+ function help() {
97
+ console.error('USAGE FOLLOWS');
98
+ }
@@ -7,6 +7,7 @@ const standardContent_js_1 = require("./standardContent.js");
7
7
  const browse_js_1 = __importDefault(require("../commands/browse.js"));
8
8
  const demo_js_1 = __importDefault(require("../commands/demo.js"));
9
9
  const legacy_1 = __importDefault(require("../commands/legacy"));
10
+ const local_js_1 = __importDefault(require("../commands/local.js"));
10
11
  const help_1 = __importDefault(require("../commands/help"));
11
12
  const tunnel_js_1 = __importDefault(require("../commands/tunnel.js"));
12
13
  const package_json_1 = __importDefault(require("../../package.json"));
@@ -30,6 +31,9 @@ handlers.set('help', {
30
31
  handlers.set('local', {
31
32
  handler: legacy_1.default
32
33
  });
34
+ handlers.set('localx', {
35
+ handler: local_js_1.default
36
+ });
33
37
  handlers.set('schema', {
34
38
  handler: legacy_1.default
35
39
  });
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.workspaceEnvVar = exports.ensureConfigDirectory = exports.PAY_CLI_CONFIG_PATH = void 0;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_os_1 = require("node:os");
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ exports.PAY_CLI_CONFIG_PATH = node_path_1.default.join((0, node_os_1.homedir)(), '.pay-cli');
11
+ /**
12
+ * This function validates a config path within the pay cli config dir exists, if it does then it creates it.
13
+ * It does this by joining all the subPath param elements onto the end of the default config path
14
+ * using path.join.
15
+ *
16
+ * Returns the absolute file path
17
+ */
18
+ function ensureConfigDirectory(...subPath) {
19
+ const fullConfigDirPath = node_path_1.default.resolve(node_path_1.default.join(exports.PAY_CLI_CONFIG_PATH, ...subPath));
20
+ const configPathStat = node_fs_1.default.statSync(fullConfigDirPath, { throwIfNoEntry: false });
21
+ if (configPathStat === undefined) {
22
+ node_fs_1.default.mkdirSync(fullConfigDirPath, { mode: 0o770, recursive: true });
23
+ }
24
+ else if (!configPathStat.isDirectory()) {
25
+ throw new Error(`pay local config path ${fullConfigDirPath} already exists and is not a directory, cannot continue`);
26
+ }
27
+ return fullConfigDirPath;
28
+ }
29
+ exports.ensureConfigDirectory = ensureConfigDirectory;
30
+ /**
31
+ * This function validates the the WORKSPACE env var is set, and that it is directory that exists.
32
+ */
33
+ function workspaceEnvVar() {
34
+ const workspacePath = process.env.WORKSPACE;
35
+ if (workspacePath === undefined) {
36
+ throw new Error('Error, the WORKSPACE env var must be set to the parent directory of where you check out your pay repositories');
37
+ }
38
+ const workspacePathStat = node_fs_1.default.statSync(workspacePath, { throwIfNoEntry: false });
39
+ if (workspacePathStat === undefined) {
40
+ throw new Error(`Error, the WORKSPACE env var is set to '${workspacePath}' which doesn't exist. This must be set to the parent directory of where you check out your pay repositories`);
41
+ }
42
+ if (!workspacePathStat.isDirectory()) {
43
+ throw new Error(`Error, the WORKSPACE env var is set to '${workspacePath}' which exists, but is not a directory. This must be set to the parent directory of where you check out your pay repositories`);
44
+ }
45
+ return workspacePath;
46
+ }
47
+ exports.workspaceEnvVar = workspaceEnvVar;