@axinom/mosaic-cli 0.62.0-rc.3 → 0.62.0-rc.4

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 (21) hide show
  1. package/dist/esm/commands/hosting/database/database-commands.d.ts +2 -0
  2. package/dist/esm/commands/hosting/database/database-commands.js +13 -0
  3. package/dist/esm/commands/hosting/database/database-commands.js.map +1 -0
  4. package/dist/esm/commands/hosting/database/proxy/database-proxy-command.d.ts +3 -0
  5. package/dist/esm/commands/hosting/database/proxy/database-proxy-command.js +60 -0
  6. package/dist/esm/commands/hosting/database/proxy/database-proxy-command.js.map +1 -0
  7. package/dist/esm/commands/hosting/database/proxy/database-proxy-options.d.ts +13 -0
  8. package/dist/esm/commands/hosting/database/proxy/database-proxy-options.js +2 -0
  9. package/dist/esm/commands/hosting/database/proxy/database-proxy-options.js.map +1 -0
  10. package/dist/esm/commands/hosting/database/proxy/database-proxy.d.ts +27 -0
  11. package/dist/esm/commands/hosting/database/proxy/database-proxy.js +338 -0
  12. package/dist/esm/commands/hosting/database/proxy/database-proxy.js.map +1 -0
  13. package/dist/esm/commands/hosting/index.js +2 -0
  14. package/dist/esm/commands/hosting/index.js.map +1 -1
  15. package/package.json +6 -4
  16. package/src/commands/hosting/database/database-commands.ts +21 -0
  17. package/src/commands/hosting/database/proxy/database-proxy-command.ts +78 -0
  18. package/src/commands/hosting/database/proxy/database-proxy-options.ts +13 -0
  19. package/src/commands/hosting/database/proxy/database-proxy.spec.ts +178 -0
  20. package/src/commands/hosting/database/proxy/database-proxy.ts +486 -0
  21. package/src/commands/hosting/index.ts +2 -0
@@ -0,0 +1,2 @@
1
+ import type { CommandModule } from 'yargs';
2
+ export declare const databaseCommands: CommandModule<unknown, unknown>;
@@ -0,0 +1,13 @@
1
+ import { databaseProxy } from './proxy/database-proxy-command.js';
2
+ export const databaseCommands = {
3
+ command: 'database',
4
+ aliases: ['db'],
5
+ describe: 'Database access related actions',
6
+ builder: (yargs) => yargs
7
+ .command('proxy', 'Open a database access session and start a local database proxy.', databaseProxy)
8
+ .demandCommand(),
9
+ handler: () => {
10
+ console.log('Please pick an action related to the hosting database command.');
11
+ },
12
+ };
13
+ //# sourceMappingURL=database-commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-commands.js","sourceRoot":"","sources":["../../../../../src/commands/hosting/database/database-commands.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAElE,MAAM,CAAC,MAAM,gBAAgB,GAAoC;IAC/D,OAAO,EAAE,UAAU;IACnB,OAAO,EAAE,CAAC,IAAI,CAAC;IACf,QAAQ,EAAE,iCAAiC;IAC3C,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CACjB,KAAK;SACF,OAAO,CACN,OAAO,EACP,kEAAkE,EAClE,aAAa,CACd;SACA,aAAa,EAAE;IACpB,OAAO,EAAE,GAAG,EAAE;QACZ,OAAO,CAAC,GAAG,CACT,gEAAgE,CACjE,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CommandModule } from 'yargs';
2
+ import type { DatabaseProxyOptions } from './database-proxy-options.js';
3
+ export declare const databaseProxy: CommandModule<unknown, DatabaseProxyOptions>;
@@ -0,0 +1,60 @@
1
+ import chalk from 'chalk';
2
+ import { DEFAULT_LOCAL_PROXY_PORT, proxyDatabase, validateDatabaseProxyArgs, } from './database-proxy.js';
3
+ const { red, yellow } = chalk;
4
+ export const databaseProxy = {
5
+ builder: (yargs) => {
6
+ return yargs
7
+ .option('serviceId', {
8
+ describe: 'Service ID to open a database access session for',
9
+ alias: 'i',
10
+ string: true,
11
+ })
12
+ .option('ttlMinutes', {
13
+ describe: 'How long the session should last. A request, not a guarantee: a shorter lifetime is granted when the service allows less. Defaults to whatever the service allows.',
14
+ number: true,
15
+ })
16
+ .option('localProxyPort', {
17
+ describe: 'Local loopback port the database client connects to. Not 5432, which a local PostgreSQL usually holds. Deliberately not named `port`: a solution sets PORT for its own service, and that would override this.',
18
+ number: true,
19
+ default: DEFAULT_LOCAL_PROXY_PORT,
20
+ })
21
+ .option('json', {
22
+ describe: 'Emit the connection details as one JSON object on stdout, status messages on stderr. Contains the password, so it is as sensitive as the normal output.',
23
+ boolean: true,
24
+ })
25
+ .option('mosaicHostingClientId', {
26
+ describe: "The service account client id.\nRequired permissions:\nHOSTING SERVICE -> 'Database Access: Write', 'Database Access Sessions: View', 'Service Definitions: View'",
27
+ alias: 'c',
28
+ string: true,
29
+ })
30
+ .option('mosaicHostingClientSecret', {
31
+ describe: 'Service Account Client Secret to authenticate',
32
+ alias: 's',
33
+ string: true,
34
+ })
35
+ .option('idServiceAuthBaseURL', {
36
+ describe: 'ID Service Authentication Endpoint Base URL',
37
+ alias: 'a',
38
+ string: true,
39
+ })
40
+ .option('hostingServiceBaseURL', {
41
+ describe: 'Hosting Service Base URL',
42
+ alias: 'h',
43
+ string: true,
44
+ })
45
+ .example('$0 hosting database proxy -i channel-service', 'Open a session and listen on 127.0.0.1:5433')
46
+ .example('$0 hosting database proxy -i channel-service --ttl-minutes 30', 'Open a session for half an hour');
47
+ },
48
+ handler: async (args) => {
49
+ const [validatedArgs, errorMessages] = validateDatabaseProxyArgs(args);
50
+ if (errorMessages.length > 0) {
51
+ process.stderr.write(red('Some required arguments are missing.\n'));
52
+ process.stderr.write(yellow(errorMessages.join('\n') + '\n'));
53
+ process.exit(1);
54
+ }
55
+ else {
56
+ await proxyDatabase(validatedArgs);
57
+ }
58
+ },
59
+ };
60
+ //# sourceMappingURL=database-proxy-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-proxy-command.js","sourceRoot":"","sources":["../../../../../../src/commands/hosting/database/proxy/database-proxy-command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EACL,wBAAwB,EACxB,aAAa,EACb,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;AAE9B,MAAM,CAAC,MAAM,aAAa,GAAiD;IACzE,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QACjB,OAAO,KAAK;aACT,MAAM,CAAC,WAAW,EAAE;YACnB,QAAQ,EAAE,kDAAkD;YAC5D,KAAK,EAAE,GAAG;YACV,MAAM,EAAE,IAAI;SACb,CAAC;aACD,MAAM,CAAC,YAAY,EAAE;YACpB,QAAQ,EACN,oKAAoK;YACtK,MAAM,EAAE,IAAI;SACb,CAAC;aACD,MAAM,CAAC,gBAAgB,EAAE;YACxB,QAAQ,EACN,+MAA+M;YACjN,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,wBAAwB;SAClC,CAAC;aACD,MAAM,CAAC,MAAM,EAAE;YACd,QAAQ,EACN,yJAAyJ;YAC3J,OAAO,EAAE,IAAI;SACd,CAAC;aACD,MAAM,CAAC,uBAAuB,EAAE;YAC/B,QAAQ,EACN,mKAAmK;YACrK,KAAK,EAAE,GAAG;YACV,MAAM,EAAE,IAAI;SACb,CAAC;aACD,MAAM,CAAC,2BAA2B,EAAE;YACnC,QAAQ,EAAE,+CAA+C;YACzD,KAAK,EAAE,GAAG;YACV,MAAM,EAAE,IAAI;SACb,CAAC;aACD,MAAM,CAAC,sBAAsB,EAAE;YAC9B,QAAQ,EAAE,6CAA6C;YACvD,KAAK,EAAE,GAAG;YACV,MAAM,EAAE,IAAI;SACb,CAAC;aACD,MAAM,CAAC,uBAAuB,EAAE;YAC/B,QAAQ,EAAE,0BAA0B;YACpC,KAAK,EAAE,GAAG;YACV,MAAM,EAAE,IAAI;SACb,CAAC;aACD,OAAO,CACN,8CAA8C,EAC9C,6CAA6C,CAC9C;aACA,OAAO,CACN,+DAA+D,EAC/D,iCAAiC,CAClC,CAAC;IACN,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACtB,MAAM,CAAC,aAAa,EAAE,aAAa,CAAC,GAAG,yBAAyB,CAAC,IAAI,CAAC,CAAC;QACvE,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC,CAAC;YACpE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;YAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;aAAM,CAAC;YACN,MAAM,aAAa,CACjB,aAA6D,CAC9D,CAAC;QACJ,CAAC;IACH,CAAC;CACF,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Options for proxying a direct database connection to a customizable service.
3
+ */
4
+ export interface DatabaseProxyOptions {
5
+ serviceId?: string;
6
+ ttlMinutes?: number;
7
+ localProxyPort?: number;
8
+ json?: boolean;
9
+ mosaicHostingClientId?: string;
10
+ mosaicHostingClientSecret?: string;
11
+ idServiceAuthBaseURL?: string;
12
+ hostingServiceBaseURL?: string;
13
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=database-proxy-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-proxy-options.js","sourceRoot":"","sources":["../../../../../../src/commands/hosting/database/proxy/database-proxy-options.ts"],"names":[],"mappings":""}
@@ -0,0 +1,27 @@
1
+ import type { DatabaseProxyOptions } from './database-proxy-options.js';
2
+ export declare const DEFAULT_LOCAL_PROXY_PORT = 5433;
3
+ /** Declared here rather than imported: the Hosting schema types are private. */
4
+ export interface DatabaseAccessSession {
5
+ databaseAccessSessionId: string;
6
+ username: string;
7
+ password: string;
8
+ databaseName: string;
9
+ expiresAt: string;
10
+ databaseProxyUrl: string;
11
+ }
12
+ export declare const minutesUntil: (expiresAt: string, now?: number) => number;
13
+ /**
14
+ * Hosting may grant less than was asked for. The minute of tolerance absorbs
15
+ * the round trip.
16
+ */
17
+ export declare const formatClampedTtlWarning: (requestedMinutes: number | undefined, expiresAt: string, now?: number) => string | undefined;
18
+ export declare const WARN_BEFORE_EXPIRY_MS: number;
19
+ /** Undefined when the session is too short for the warning to precede it. */
20
+ export declare const toExpiryWarningDelay: (expiresAt: string, now?: number) => number | undefined;
21
+ export declare const toConnectionJson: (session: DatabaseAccessSession, port: number) => Record<string, string | number>;
22
+ export declare const formatConnectionDetails: (session: DatabaseAccessSession, port: number, now?: number) => string;
23
+ export declare const proxyDatabase: (args: Required<Pick<DatabaseProxyOptions, "serviceId">> & DatabaseProxyOptions) => Promise<void>;
24
+ /**
25
+ * Validate arguments for proxying a direct database connection.
26
+ */
27
+ export declare const validateDatabaseProxyArgs: (args: DatabaseProxyOptions) => [DatabaseProxyOptions, string[]];
@@ -0,0 +1,338 @@
1
+ import { getServiceAccountToken } from '@axinom/mosaic-id-link-be';
2
+ import { isNullOrWhitespace } from '@axinom/mosaic-service-common';
3
+ import axios, { AxiosError } from 'axios';
4
+ import chalk from 'chalk';
5
+ import net from 'net';
6
+ import WebSocket, { createWebSocketStream } from 'ws';
7
+ import { exitCode } from '../../../../common/index.js';
8
+ const { red, yellow, green, dim, bold } = chalk;
9
+ export const DEFAULT_LOCAL_PROXY_PORT = 5433;
10
+ /** Only loopback is ever bound: the credential is not meant to leave the machine. */
11
+ const LOOPBACK = '127.0.0.1';
12
+ const OPEN_SESSION = `mutation OpenDatabaseAccessSession($input: OpenDatabaseAccessSessionInput!) {
13
+ openDatabaseAccessSession(input: $input) {
14
+ databaseAccessSessionId
15
+ username
16
+ password
17
+ databaseName
18
+ expiresAt
19
+ databaseProxyUrl
20
+ }
21
+ }`;
22
+ const CLOSE_SESSION = `mutation CloseDatabaseAccessSession($input: CloseDatabaseAccessSessionInput!) {
23
+ closeDatabaseAccessSession(input: $input) {
24
+ status
25
+ terminatedBackends
26
+ }
27
+ }`;
28
+ /** Connection details go to stdout and nothing else does, so `--json` pipes. */
29
+ const writeOut = (text) => {
30
+ process.stdout.write(text + '\n');
31
+ };
32
+ const writeStatus = (text) => {
33
+ process.stderr.write(text + '\n');
34
+ };
35
+ export const minutesUntil = (expiresAt, now = Date.now()) => Math.max(0, Math.floor((new Date(expiresAt).getTime() - now) / 60_000));
36
+ /**
37
+ * Hosting may grant less than was asked for. The minute of tolerance absorbs
38
+ * the round trip.
39
+ */
40
+ export const formatClampedTtlWarning = (requestedMinutes, expiresAt, now = Date.now()) => {
41
+ if (requestedMinutes === undefined) {
42
+ return undefined;
43
+ }
44
+ const granted = minutesUntil(expiresAt, now);
45
+ if (granted >= requestedMinutes - 1) {
46
+ return undefined;
47
+ }
48
+ return `Requested ${requestedMinutes} minutes, granted ${granted} - database access for this service expires then.`;
49
+ };
50
+ export const WARN_BEFORE_EXPIRY_MS = 5 * 60_000;
51
+ /** Undefined when the session is too short for the warning to precede it. */
52
+ export const toExpiryWarningDelay = (expiresAt, now = Date.now()) => {
53
+ const remaining = new Date(expiresAt).getTime() - now;
54
+ return remaining > WARN_BEFORE_EXPIRY_MS
55
+ ? remaining - WARN_BEFORE_EXPIRY_MS
56
+ : undefined;
57
+ };
58
+ export const toConnectionJson = (session, port) => ({
59
+ databaseAccessSessionId: session.databaseAccessSessionId,
60
+ host: LOOPBACK,
61
+ port,
62
+ database: session.databaseName,
63
+ user: session.username,
64
+ password: session.password,
65
+ expiresAt: session.expiresAt,
66
+ });
67
+ export const formatConnectionDetails = (session, port, now = Date.now()) => {
68
+ const rows = [
69
+ ['Host', LOOPBACK],
70
+ ['Port', String(port)],
71
+ ['Database', session.databaseName],
72
+ ['User', session.username],
73
+ ['Password', session.password],
74
+ [
75
+ 'Expires',
76
+ `${session.expiresAt} (${minutesUntil(session.expiresAt, now)} minutes)`,
77
+ ],
78
+ ];
79
+ return rows
80
+ .map(([label, value]) => ` ${label.padEnd(9)} ${value}`)
81
+ .join('\n');
82
+ };
83
+ const getAxiosInstance = (hostingServiceBaseUrl, serviceAccountToken) => {
84
+ return axios.create({
85
+ baseURL: new URL(hostingServiceBaseUrl).toString(),
86
+ headers: {
87
+ Authorization: `Bearer ${serviceAccountToken}`,
88
+ 'content-type': 'application/json',
89
+ },
90
+ });
91
+ };
92
+ /** Built per call rather than once: a long session outlives its startup token. */
93
+ const callHosting = async (hostingServiceBaseUrl, serviceAccountToken, query, variables) => {
94
+ const axiosInstance = getAxiosInstance(hostingServiceBaseUrl, serviceAccountToken);
95
+ const payload = JSON.stringify({ query, variables });
96
+ const response = await axiosInstance.post('graphql', payload);
97
+ const errors = response.data.errors ?? [];
98
+ if (errors.length > 0) {
99
+ const { message, code } = errors[0];
100
+ throw new Error(code ? `${code}: ${message}` : message);
101
+ }
102
+ return response.data.data;
103
+ };
104
+ /** Caches the token and renews it a minute before it lapses. */
105
+ const createTokenProvider = (authEndpoint, clientId, clientSecret) => {
106
+ let token = '';
107
+ let renewAt = 0;
108
+ return async () => {
109
+ if (token === '' || Date.now() >= renewAt) {
110
+ const result = await getServiceAccountToken(authEndpoint, clientId, clientSecret);
111
+ token = result.accessToken;
112
+ renewAt = Date.now() + (result.expiresInSeconds - 60) * 1_000;
113
+ }
114
+ return token;
115
+ };
116
+ };
117
+ const describeError = (error) => {
118
+ const axiosError = error;
119
+ if (axiosError.response) {
120
+ return `${axiosError.message} (HTTP ${axiosError.response.status}: ${JSON.stringify(axiosError.response.data)})`;
121
+ }
122
+ return error instanceof Error ? error.message : String(error);
123
+ };
124
+ /**
125
+ * Pipes one accepted TCP connection through one WebSocket, never multiplexed.
126
+ * The bytes are the TLS session the client negotiated with Postgres directly.
127
+ */
128
+ const databaseProxyConnection = (client, session, getToken) => {
129
+ // Paused until the carrier is open, so nothing the client sends is dropped.
130
+ client.pause();
131
+ void (async () => {
132
+ // Everything up to the carrier existing is guarded: an escape here is an
133
+ // unhandled rejection, which kills the process before the session is
134
+ // closed and strands the database role until it expires.
135
+ let ws;
136
+ try {
137
+ const token = await getToken();
138
+ ws = new WebSocket(session.databaseProxyUrl, {
139
+ headers: {
140
+ authorization: `Bearer ${token}`,
141
+ 'x-mosaic-database-access-session': session.databaseAccessSessionId,
142
+ },
143
+ });
144
+ }
145
+ catch (error) {
146
+ writeStatus(red(`Could not reach the database proxy: ${describeError(error)}`));
147
+ client.destroy();
148
+ return;
149
+ }
150
+ // end(), not destroy(): destroy drops bytes still buffered for the client.
151
+ const stop = () => {
152
+ client.end();
153
+ if (ws.readyState === WebSocket.CONNECTING ||
154
+ ws.readyState === WebSocket.OPEN) {
155
+ ws.close();
156
+ }
157
+ };
158
+ // Piped rather than forwarded by hand: a slow reader on either end pauses
159
+ // the other instead of queueing without limit.
160
+ ws.on('open', () => {
161
+ const carrier = createWebSocketStream(ws);
162
+ carrier.on('error', stop);
163
+ client.pipe(carrier);
164
+ carrier.pipe(client);
165
+ client.resume();
166
+ });
167
+ ws.on('unexpected-response', (_request, response) => {
168
+ writeStatus(red(`The database proxy refused the connection (HTTP ${response.statusCode}).`));
169
+ client.destroy();
170
+ });
171
+ ws.on('error', (error) => {
172
+ writeStatus(red(`Database proxy error: ${error.message}`));
173
+ stop();
174
+ });
175
+ ws.on('close', stop);
176
+ client.on('error', stop);
177
+ client.on('close', () => {
178
+ if (ws.readyState === WebSocket.CONNECTING ||
179
+ ws.readyState === WebSocket.OPEN) {
180
+ ws.close();
181
+ }
182
+ });
183
+ })();
184
+ };
185
+ const listen = (server, port) => new Promise((resolve, reject) => {
186
+ server.once('error', reject);
187
+ server.listen(port, LOOPBACK, () => {
188
+ server.removeListener('error', reject);
189
+ resolve();
190
+ });
191
+ });
192
+ export const proxyDatabase = async (args) => {
193
+ const port = args.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
194
+ const hostingServiceBaseUrl = args.hostingServiceBaseURL;
195
+ const getToken = createTokenProvider(args.idServiceAuthBaseURL, args.mosaicHostingClientId, args.mosaicHostingClientSecret);
196
+ let session;
197
+ try {
198
+ const data = await callHosting(hostingServiceBaseUrl, await getToken(), OPEN_SESSION, {
199
+ input: {
200
+ serviceId: args.serviceId,
201
+ // Left undefined when not asked for, so the service applies its own
202
+ // default rather than this one.
203
+ ttlMinutes: args.ttlMinutes,
204
+ },
205
+ });
206
+ session = data.openDatabaseAccessSession;
207
+ }
208
+ catch (error) {
209
+ writeStatus(red(`Could not open a session for service [${args.serviceId}].`));
210
+ writeStatus(red(describeError(error)));
211
+ process.exit(exitCode);
212
+ }
213
+ const closeSession = async () => {
214
+ // Fresh: the startup token may have lapsed, and a 401 strands the role.
215
+ const token = await getToken();
216
+ const data = await callHosting(hostingServiceBaseUrl, token, CLOSE_SESSION, {
217
+ input: { databaseAccessSessionId: session.databaseAccessSessionId },
218
+ });
219
+ return data.closeDatabaseAccessSession.status;
220
+ };
221
+ const server = net.createServer((client) => databaseProxyConnection(client, session, getToken));
222
+ // Bound before printing: a bind error gives the session back.
223
+ try {
224
+ await listen(server, port);
225
+ }
226
+ catch (error) {
227
+ writeStatus(red(`Could not listen on ${LOOPBACK}:${port}.`));
228
+ writeStatus(red(describeError(error)));
229
+ try {
230
+ await closeSession();
231
+ writeStatus(dim('The session was closed again.'));
232
+ }
233
+ catch (closeError) {
234
+ writeStatus(yellow(`The session could not be closed and will expire at ${session.expiresAt}: ${describeError(closeError)}`));
235
+ }
236
+ process.exit(exitCode);
237
+ }
238
+ let closing = false;
239
+ const shutdown = async (signal) => {
240
+ if (closing) {
241
+ return;
242
+ }
243
+ closing = true;
244
+ writeStatus(dim(`\n${signal} - closing the session...`));
245
+ server.close();
246
+ try {
247
+ const status = await closeSession();
248
+ if (status === 'CLOSED') {
249
+ writeStatus(green('Session closed and the database role dropped.'));
250
+ }
251
+ else {
252
+ // CLOSING: the role outlives the command by at most one reconciliation
253
+ // pass, so there is nothing useful to wait here for.
254
+ writeStatus(yellow('Local proxy closed. Dropping the database role is still pending and will be completed by the service.'));
255
+ }
256
+ process.exit(0);
257
+ }
258
+ catch (error) {
259
+ writeStatus(yellow(`The session could not be closed and will expire at ${session.expiresAt}: ${describeError(error)}`));
260
+ process.exit(exitCode);
261
+ }
262
+ };
263
+ // Node exits on all three by default, skipping the close. SIGKILL cannot be
264
+ // caught; that falls to the session's expiry.
265
+ for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
266
+ process.on(signal, () => void shutdown(signal));
267
+ }
268
+ const clampWarning = formatClampedTtlWarning(args.ttlMinutes, session.expiresAt);
269
+ if (clampWarning) {
270
+ writeStatus(yellow(clampWarning));
271
+ }
272
+ if (args.json === true) {
273
+ writeOut(JSON.stringify(toConnectionJson(session, port)));
274
+ }
275
+ else {
276
+ writeStatus(bold(`\nDatabase access to [${args.serviceId}] is open.\n`));
277
+ writeOut(formatConnectionDetails(session, port));
278
+ writeStatus(dim('\nThe password is shown once and cannot be retrieved again.'));
279
+ writeStatus(dim('Press Ctrl+C to close the session.'));
280
+ }
281
+ const warningDelay = toExpiryWarningDelay(session.expiresAt);
282
+ if (warningDelay !== undefined) {
283
+ setTimeout(() => {
284
+ writeStatus(yellow(`\nThis session ends at ${session.expiresAt}. Open connections will be dropped.`));
285
+ }, warningDelay).unref();
286
+ }
287
+ };
288
+ /**
289
+ * Validate arguments for proxying a direct database connection.
290
+ */
291
+ export const validateDatabaseProxyArgs = (args) => {
292
+ const errorMessages = [];
293
+ const idServiceAuthBaseURL = args.idServiceAuthBaseURL ?? process.env.ID_SERVICE_AUTH_BASE_URL ?? '';
294
+ const mosaicHostingClientId = args.mosaicHostingClientId ?? process.env.MOSAIC_HOSTING_CLIENT_ID ?? '';
295
+ const mosaicHostingClientSecret = args.mosaicHostingClientSecret ??
296
+ process.env.MOSAIC_HOSTING_CLIENT_SECRET ??
297
+ '';
298
+ const hostingServiceBaseURL = args.hostingServiceBaseURL ?? process.env.HOSTING_SERVICE_BASE_URL ?? '';
299
+ // Run from a solution, the service's own .env names it.
300
+ const serviceId = args.serviceId ?? process.env.SERVICE_ID ?? '';
301
+ if (isNullOrWhitespace(idServiceAuthBaseURL)) {
302
+ errorMessages.push('[idServiceAuthBaseURL] is required.');
303
+ }
304
+ if (isNullOrWhitespace(mosaicHostingClientId)) {
305
+ errorMessages.push('[mosaicHostingClientId] is required.');
306
+ }
307
+ if (isNullOrWhitespace(mosaicHostingClientSecret)) {
308
+ errorMessages.push('[mosaicHostingClientSecret] is required.');
309
+ }
310
+ if (isNullOrWhitespace(hostingServiceBaseURL)) {
311
+ errorMessages.push('[hostingServiceBaseURL] is required.');
312
+ }
313
+ if (isNullOrWhitespace(serviceId)) {
314
+ errorMessages.push('[serviceId] is required.');
315
+ }
316
+ if (args.ttlMinutes !== undefined &&
317
+ (!Number.isInteger(args.ttlMinutes) || args.ttlMinutes < 1)) {
318
+ errorMessages.push('[ttlMinutes] must be a whole number of 1 or more.');
319
+ }
320
+ if (args.localProxyPort !== undefined &&
321
+ (!Number.isInteger(args.localProxyPort) ||
322
+ args.localProxyPort < 1 ||
323
+ args.localProxyPort > 65535)) {
324
+ errorMessages.push('[localProxyPort] must be a whole number between 1 and 65535.');
325
+ }
326
+ return [
327
+ {
328
+ ...args,
329
+ serviceId,
330
+ idServiceAuthBaseURL,
331
+ mosaicHostingClientId,
332
+ mosaicHostingClientSecret,
333
+ hostingServiceBaseURL,
334
+ },
335
+ errorMessages,
336
+ ];
337
+ };
338
+ //# sourceMappingURL=database-proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-proxy.js","sourceRoot":"","sources":["../../../../../../src/commands/hosting/database/proxy/database-proxy.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { databaseCommands } from './database/database-commands.js';
1
2
  import { manifestCommands } from './manifest/manifest-commands.js';
2
3
  import { piletCommands } from './pilet/pilet-commands.js';
3
4
  import { serviceCommands } from './service/service-commands.js';
@@ -8,6 +9,7 @@ export const hosting = {
8
9
  .command('pilet', 'Pilet related actions', piletCommands)
9
10
  .command('manifest', 'Deployment Manifest related actions', manifestCommands)
10
11
  .command('service', 'Service Deployment related actions', serviceCommands)
12
+ .command(databaseCommands)
11
13
  .demandCommand(),
12
14
  handler: (_args) => {
13
15
  console.log('Please pick a sub command related to hosting.');
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/commands/hosting/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEhE,MAAM,CAAC,MAAM,OAAO,GAAoC;IACtD,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,qCAAqC;IAC/C,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CACjB,KAAK;SACF,OAAO,CAAC,OAAO,EAAE,uBAAuB,EAAE,aAAa,CAAC;SACxD,OAAO,CACN,UAAU,EACV,qCAAqC,EACrC,gBAAgB,CACjB;SACA,OAAO,CAAC,SAAS,EAAE,oCAAoC,EAAE,eAAe,CAAC;SACzE,aAAa,EAAE;IACpB,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QACjB,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;IAC/D,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/commands/hosting/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEhE,MAAM,CAAC,MAAM,OAAO,GAAoC;IACtD,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,qCAAqC;IAC/C,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CACjB,KAAK;SACF,OAAO,CAAC,OAAO,EAAE,uBAAuB,EAAE,aAAa,CAAC;SACxD,OAAO,CACN,UAAU,EACV,qCAAqC,EACrC,gBAAgB,CACjB;SACA,OAAO,CAAC,SAAS,EAAE,oCAAoC,EAAE,eAAe,CAAC;SACzE,OAAO,CAAC,gBAAgB,CAAC;SACzB,aAAa,EAAE;IACpB,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QACjB,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;IAC/D,CAAC;CACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axinom/mosaic-cli",
3
- "version": "0.62.0-rc.3",
3
+ "version": "0.62.0-rc.4",
4
4
  "engines": {
5
5
  "node": ">=20.19.0"
6
6
  },
@@ -55,8 +55,8 @@
55
55
  "@asyncapi/diff": "^0.5.0",
56
56
  "@asyncapi/modelina": "^5.10.1",
57
57
  "@asyncapi/parser": "^3.6.0",
58
- "@axinom/mosaic-id-link-be": "^0.45.0-rc.3",
59
- "@axinom/mosaic-service-common": "^0.75.0-rc.3",
58
+ "@axinom/mosaic-id-link-be": "^0.45.0-rc.4",
59
+ "@axinom/mosaic-service-common": "^0.75.0-rc.4",
60
60
  "@graphql-inspector/core": "3.1.2",
61
61
  "@inquirer/confirm": "^6.1.1",
62
62
  "@inquirer/core": "^11.2.1",
@@ -85,11 +85,13 @@
85
85
  "simple-git": "^4.0.1",
86
86
  "sql-formatter": "^4.0.2",
87
87
  "tar": "^7.5.20",
88
+ "ws": "^8.22.0",
88
89
  "yargs": "^18.1.0"
89
90
  },
90
91
  "devDependencies": {
91
92
  "@types/diff": "^5.0.0",
92
93
  "@types/node": "^22.0.0",
94
+ "@types/ws": "^8.18.2",
93
95
  "@types/yargs": "^17.0.0",
94
96
  "@vitest/ui": "^4.1.10",
95
97
  "jest-extended": "^7.0.0",
@@ -100,5 +102,5 @@
100
102
  "publishConfig": {
101
103
  "access": "public"
102
104
  },
103
- "gitHead": "2565297e65a9c860a6493af23982c43d40fbef91"
105
+ "gitHead": "c2cd7a254faf3436a01e61dedf194a0c74de9a00"
104
106
  }
@@ -0,0 +1,21 @@
1
+ import type { CommandModule } from 'yargs';
2
+ import { databaseProxy } from './proxy/database-proxy-command.js';
3
+
4
+ export const databaseCommands: CommandModule<unknown, unknown> = {
5
+ command: 'database',
6
+ aliases: ['db'],
7
+ describe: 'Database access related actions',
8
+ builder: (yargs) =>
9
+ yargs
10
+ .command(
11
+ 'proxy',
12
+ 'Open a database access session and start a local database proxy.',
13
+ databaseProxy,
14
+ )
15
+ .demandCommand(),
16
+ handler: () => {
17
+ console.log(
18
+ 'Please pick an action related to the hosting database command.',
19
+ );
20
+ },
21
+ };