securenow 5.3.2 → 5.3.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.
- package/cli/apps.js +140 -13
- package/cli.js +2 -2
- package/package.json +1 -1
package/cli/apps.js
CHANGED
|
@@ -4,32 +4,74 @@ const { api, requireAuth } = require('./client');
|
|
|
4
4
|
const config = require('./config');
|
|
5
5
|
const ui = require('./ui');
|
|
6
6
|
|
|
7
|
+
const FREE_TRIAL_URL = 'https://freetrial.securenow.ai:4318';
|
|
8
|
+
|
|
9
|
+
function instanceUrl(inst) {
|
|
10
|
+
if (!inst) return FREE_TRIAL_URL;
|
|
11
|
+
return `${inst.protocol || 'https'}://${inst.host}:4318`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function fetchInstanceMap() {
|
|
15
|
+
try {
|
|
16
|
+
const data = await api.get('/instances');
|
|
17
|
+
const instances = data.instances || [];
|
|
18
|
+
const map = {};
|
|
19
|
+
for (const inst of instances) {
|
|
20
|
+
map[inst._id] = inst;
|
|
21
|
+
}
|
|
22
|
+
return map;
|
|
23
|
+
} catch {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
7
28
|
async function list(args, flags) {
|
|
8
29
|
requireAuth();
|
|
9
30
|
const s = ui.spinner('Fetching applications');
|
|
10
31
|
|
|
11
32
|
try {
|
|
12
|
-
const
|
|
13
|
-
|
|
33
|
+
const [appData, instMap] = await Promise.all([
|
|
34
|
+
api.get('/applications'),
|
|
35
|
+
fetchInstanceMap(),
|
|
36
|
+
]);
|
|
37
|
+
const apps = appData.applications || [];
|
|
14
38
|
s.stop(`Found ${apps.length} application${apps.length !== 1 ? 's' : ''}`);
|
|
15
39
|
console.log('');
|
|
16
40
|
|
|
17
41
|
if (flags.json) {
|
|
18
|
-
|
|
42
|
+
const enriched = apps.map(app => ({
|
|
43
|
+
...app,
|
|
44
|
+
instanceUrl: instanceUrl(app.instanceId ? instMap[app.instanceId] : null),
|
|
45
|
+
}));
|
|
46
|
+
ui.json(enriched);
|
|
19
47
|
return;
|
|
20
48
|
}
|
|
21
49
|
|
|
22
50
|
const defaultApp = config.getDefaultApp();
|
|
23
|
-
const rows = apps.map(app =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
51
|
+
const rows = apps.map(app => {
|
|
52
|
+
const inst = app.instanceId ? instMap[app.instanceId] : null;
|
|
53
|
+
const url = instanceUrl(inst);
|
|
54
|
+
const instLabel = inst ? `${inst.name} ${ui.c.dim(url)}` : `${ui.c.green('Free Trial')} ${ui.c.dim(url)}`;
|
|
55
|
+
return [
|
|
56
|
+
app.name + (app.key === defaultApp ? ui.c.cyan(' (default)') : ''),
|
|
57
|
+
ui.c.dim(app.key),
|
|
58
|
+
instLabel,
|
|
59
|
+
ui.timeAgo(app.createdAt),
|
|
60
|
+
];
|
|
61
|
+
});
|
|
29
62
|
|
|
30
|
-
ui.table(['Name', 'Key', '
|
|
63
|
+
ui.table(['Name', 'Key', 'Instance', 'Created'], rows);
|
|
31
64
|
console.log('');
|
|
32
65
|
|
|
66
|
+
if (apps.length > 0) {
|
|
67
|
+
console.log(` ${ui.c.bold('Add to your .env:')}`);
|
|
68
|
+
const first = apps.find(a => a.key === defaultApp) || apps[0];
|
|
69
|
+
const firstInst = first.instanceId ? instMap[first.instanceId] : null;
|
|
70
|
+
console.log(` SECURENOW_APPID=${first.key}`);
|
|
71
|
+
console.log(` SECURENOW_INSTANCE=${instanceUrl(firstInst)}`);
|
|
72
|
+
console.log('');
|
|
73
|
+
}
|
|
74
|
+
|
|
33
75
|
if (!defaultApp && apps.length > 0) {
|
|
34
76
|
ui.info(`Tip: Set a default app with ${ui.c.bold('securenow config set defaultApp <key>')}`);
|
|
35
77
|
console.log('');
|
|
@@ -56,21 +98,36 @@ async function create(args, flags) {
|
|
|
56
98
|
if (flags.hosts) {
|
|
57
99
|
body.hosts = flags.hosts.split(',').map(h => h.trim());
|
|
58
100
|
}
|
|
101
|
+
|
|
59
102
|
if (flags.instance) {
|
|
60
103
|
body.instanceId = flags.instance;
|
|
104
|
+
} else if (process.stdin.isTTY) {
|
|
105
|
+
body.instanceId = await pickInstance();
|
|
61
106
|
}
|
|
62
107
|
|
|
108
|
+
const selectedInstanceId = body.instanceId;
|
|
109
|
+
|
|
63
110
|
const s = ui.spinner(`Creating application "${name}"`);
|
|
64
111
|
try {
|
|
65
112
|
const result = await api.post('/applications', body);
|
|
66
113
|
const app = result.application || result;
|
|
67
114
|
s.stop(`Application created`);
|
|
68
115
|
|
|
116
|
+
let inst = null;
|
|
117
|
+
if (selectedInstanceId) {
|
|
118
|
+
try {
|
|
119
|
+
const instData = await api.get(`/instances/${selectedInstanceId}`);
|
|
120
|
+
inst = instData.instance || null;
|
|
121
|
+
} catch {}
|
|
122
|
+
}
|
|
123
|
+
const envUrl = instanceUrl(inst);
|
|
124
|
+
|
|
69
125
|
console.log('');
|
|
70
126
|
ui.keyValue([
|
|
71
127
|
['Name', app.name],
|
|
72
128
|
['Key', ui.c.green(ui.c.bold(app.key))],
|
|
73
129
|
['ID', ui.c.dim(app._id)],
|
|
130
|
+
['Instance', inst ? `${inst.name} ${ui.c.dim(`(${envUrl})`)}` : `${ui.c.green('Free Trial')} ${ui.c.dim(`(${envUrl})`)}`],
|
|
74
131
|
['Hosts', app.hosts?.length ? app.hosts.join(', ') : ui.c.dim('none')],
|
|
75
132
|
]);
|
|
76
133
|
|
|
@@ -78,12 +135,13 @@ async function create(args, flags) {
|
|
|
78
135
|
console.log(` ${ui.c.bold('Add to your .env.local:')}`);
|
|
79
136
|
console.log('');
|
|
80
137
|
console.log(` SECURENOW_APPID=${app.key}`);
|
|
138
|
+
console.log(` SECURENOW_INSTANCE=${envUrl}`);
|
|
81
139
|
console.log('');
|
|
82
140
|
ui.info(`Set as default: ${ui.c.bold(`securenow config set defaultApp ${app.key}`)}`);
|
|
83
141
|
console.log('');
|
|
84
142
|
|
|
85
143
|
if (flags.json) {
|
|
86
|
-
ui.json(app);
|
|
144
|
+
ui.json({ ...app, instanceUrl: envUrl });
|
|
87
145
|
}
|
|
88
146
|
} catch (err) {
|
|
89
147
|
s.fail('Failed to create application');
|
|
@@ -91,6 +149,61 @@ async function create(args, flags) {
|
|
|
91
149
|
}
|
|
92
150
|
}
|
|
93
151
|
|
|
152
|
+
async function pickInstance() {
|
|
153
|
+
const s = ui.spinner('Loading instances');
|
|
154
|
+
let instances = [];
|
|
155
|
+
try {
|
|
156
|
+
const data = await api.get('/instances');
|
|
157
|
+
instances = data.instances || [];
|
|
158
|
+
s.stop('Instances loaded');
|
|
159
|
+
} catch {
|
|
160
|
+
s.stop('Could not load instances');
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const FREE_TRIAL_LABEL = `${ui.c.green('Free Trial')} ${ui.c.dim('— SecureNow managed instance (no setup needed)')}`;
|
|
165
|
+
|
|
166
|
+
const choices = [{ label: FREE_TRIAL_LABEL, value: null }];
|
|
167
|
+
|
|
168
|
+
for (const inst of instances) {
|
|
169
|
+
const status = inst.status === 'active' ? ui.c.green('●') : ui.c.red('●');
|
|
170
|
+
const apps = inst.linkedApps ? ui.c.dim(` (${inst.linkedApps} app${inst.linkedApps !== 1 ? 's' : ''})`) : '';
|
|
171
|
+
choices.push({
|
|
172
|
+
label: `${status} ${inst.name}${apps} ${ui.c.dim(`[${inst._id}]`)}`,
|
|
173
|
+
value: inst._id,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const appUrl = config.getAppUrl();
|
|
178
|
+
choices.push({
|
|
179
|
+
label: `${ui.c.cyan('+')} Create a new instance ${ui.c.dim('(opens browser)')}`,
|
|
180
|
+
value: '__new__',
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const selected = await ui.select('Which instance should this app use?', choices);
|
|
184
|
+
|
|
185
|
+
if (selected === '__new__') {
|
|
186
|
+
const url = `${appUrl}/dashboard/settings/instances`;
|
|
187
|
+
ui.info(`Opening ${ui.c.underline(url)}`);
|
|
188
|
+
openBrowser(url);
|
|
189
|
+
ui.info('Create your instance in the browser, then run this command again.');
|
|
190
|
+
process.exit(0);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return selected;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function openBrowser(url) {
|
|
197
|
+
const { execSync } = require('child_process');
|
|
198
|
+
try {
|
|
199
|
+
if (process.platform === 'darwin') execSync(`open "${url}"`);
|
|
200
|
+
else if (process.platform === 'win32') execSync(`start "" "${url}"`);
|
|
201
|
+
else execSync(`xdg-open "${url}"`);
|
|
202
|
+
} catch {
|
|
203
|
+
ui.warn(`Could not open browser. Visit: ${url}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
94
207
|
async function info(args, flags) {
|
|
95
208
|
requireAuth();
|
|
96
209
|
|
|
@@ -104,10 +217,19 @@ async function info(args, flags) {
|
|
|
104
217
|
try {
|
|
105
218
|
const data = await api.get(`/applications/${id}`);
|
|
106
219
|
const app = data.application || data;
|
|
220
|
+
|
|
221
|
+
let inst = null;
|
|
222
|
+
if (app.instanceId) {
|
|
223
|
+
try {
|
|
224
|
+
const instData = await api.get(`/instances/${app.instanceId}`);
|
|
225
|
+
inst = instData.instance || null;
|
|
226
|
+
} catch {}
|
|
227
|
+
}
|
|
228
|
+
const envUrl = instanceUrl(inst);
|
|
107
229
|
s.stop('Application details loaded');
|
|
108
230
|
|
|
109
231
|
if (flags.json) {
|
|
110
|
-
ui.json(app);
|
|
232
|
+
ui.json({ ...app, instanceUrl: envUrl });
|
|
111
233
|
return;
|
|
112
234
|
}
|
|
113
235
|
|
|
@@ -117,11 +239,16 @@ async function info(args, flags) {
|
|
|
117
239
|
ui.keyValue([
|
|
118
240
|
['Key', ui.c.bold(app.key)],
|
|
119
241
|
['ID', ui.c.dim(app._id)],
|
|
242
|
+
['Instance', inst ? `${inst.name} ${ui.c.dim(`(${envUrl})`)}` : `${ui.c.green('Free Trial')} ${ui.c.dim(`(${envUrl})`)}`],
|
|
120
243
|
['Hosts', app.hosts?.length ? app.hosts.join(', ') : ui.c.dim('none')],
|
|
121
|
-
['Instance', app.instanceId || ui.c.dim('default')],
|
|
122
244
|
['Created', app.createdAt ? new Date(app.createdAt).toLocaleString() : '—'],
|
|
123
245
|
['Updated', app.updatedAt ? new Date(app.updatedAt).toLocaleString() : '—'],
|
|
124
246
|
]);
|
|
247
|
+
|
|
248
|
+
console.log('');
|
|
249
|
+
console.log(` ${ui.c.bold('Environment variables:')}`);
|
|
250
|
+
console.log(` SECURENOW_APPID=${app.key}`);
|
|
251
|
+
console.log(` SECURENOW_INSTANCE=${envUrl}`);
|
|
125
252
|
console.log('');
|
|
126
253
|
} catch (err) {
|
|
127
254
|
s.fail('Failed to fetch application');
|
package/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const ui = require('./cli/ui');
|
|
@@ -60,7 +60,7 @@ const COMMANDS = {
|
|
|
60
60
|
usage: 'securenow apps <subcommand> [options]',
|
|
61
61
|
sub: {
|
|
62
62
|
list: { desc: 'List all applications', run: (a, f) => require('./cli/apps').list(a, f) },
|
|
63
|
-
create: { desc: 'Create a new application', usage: 'securenow apps create <name> [--hosts host1,host2] [--instance <id>]', run: (a, f) => require('./cli/apps').create(a, f) },
|
|
63
|
+
create: { desc: 'Create a new application (interactive instance picker)', usage: 'securenow apps create <name> [--hosts host1,host2] [--instance <id>]', run: (a, f) => require('./cli/apps').create(a, f) },
|
|
64
64
|
info: { desc: 'Show application details', usage: 'securenow apps info <id>', run: (a, f) => require('./cli/apps').info(a, f) },
|
|
65
65
|
delete: { desc: 'Delete an application', usage: 'securenow apps delete <id> [--force]', run: (a, f) => require('./cli/apps').remove(a, f) },
|
|
66
66
|
default: { desc: 'Set default application', usage: 'securenow apps default <key>', run: (a, f) => require('./cli/apps').setDefault(a, f) },
|
package/package.json
CHANGED