@webmate-studio/cli 0.3.10 → 0.3.12
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/bin/wm.mjs +8 -1
- package/package.json +4 -3
- package/src/commands/login.js +43 -3
- package/src/commands/switch-org.js +177 -0
- package/src/commands/switch.js +15 -3
package/bin/wm.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { generate } from '../src/commands/generate.js';
|
|
|
9
9
|
import { logout } from '../src/commands/logout.js';
|
|
10
10
|
import { info } from '../src/commands/info.js';
|
|
11
11
|
import { switchCommand } from '../src/commands/switch.js';
|
|
12
|
+
import { switchOrgCommand } from '../src/commands/switch-org.js';
|
|
12
13
|
import { readFileSync } from 'fs';
|
|
13
14
|
import { fileURLToPath } from 'url';
|
|
14
15
|
import { dirname, join } from 'path';
|
|
@@ -81,7 +82,13 @@ program
|
|
|
81
82
|
// wm switch - Switch to a different project
|
|
82
83
|
program
|
|
83
84
|
.command('switch')
|
|
84
|
-
.description('Switch to a different project')
|
|
85
|
+
.description('Switch to a different project within current organization')
|
|
85
86
|
.action(switchCommand);
|
|
86
87
|
|
|
88
|
+
// wm switch-org - Switch to a different organization
|
|
89
|
+
program
|
|
90
|
+
.command('switch-org')
|
|
91
|
+
.description('Switch to a different organization')
|
|
92
|
+
.action(switchOrgCommand);
|
|
93
|
+
|
|
87
94
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webmate-studio/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Webmate Studio CLI - Build and manage your Webmate components",
|
|
6
6
|
"keywords": [
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"src"
|
|
25
25
|
],
|
|
26
26
|
"exports": {
|
|
27
|
-
".": "./src/index.js"
|
|
27
|
+
".": "./src/index.js",
|
|
28
|
+
"./package.json": "./package.json"
|
|
28
29
|
},
|
|
29
30
|
"publishConfig": {
|
|
30
31
|
"access": "public"
|
|
@@ -34,7 +35,7 @@
|
|
|
34
35
|
"@webmate-studio/builder": "^0.2.1",
|
|
35
36
|
"@webmate-studio/core": "^0.2.0",
|
|
36
37
|
"@webmate-studio/parser": "^0.2.1",
|
|
37
|
-
"@webmate-studio/preview": "^0.2.
|
|
38
|
+
"@webmate-studio/preview": "^0.2.10",
|
|
38
39
|
"alpinejs": "^3.15.0",
|
|
39
40
|
"commander": "^11.0.0",
|
|
40
41
|
"esbuild": "^0.19.0",
|
package/src/commands/login.js
CHANGED
|
@@ -106,11 +106,49 @@ export async function loginCommand(options = {}) {
|
|
|
106
106
|
const loginData = await loginResponse.json();
|
|
107
107
|
logger.success(`Eingeloggt als ${pc.cyan(loginData.user.email)}`);
|
|
108
108
|
|
|
109
|
-
// Get list of
|
|
109
|
+
// Get list of organizations
|
|
110
|
+
console.log('');
|
|
111
|
+
logger.info('Lade Organizations...');
|
|
112
|
+
|
|
113
|
+
const organizationsUrl = `${baseUrl}/api/cli/organizations?userId=${loginData.user.id}`;
|
|
114
|
+
const organizationsResponse = await secureFetch(organizationsUrl);
|
|
115
|
+
|
|
116
|
+
if (!organizationsResponse.ok) {
|
|
117
|
+
throw new Error('Fehler beim Laden der Organizations');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const organizationsData = await organizationsResponse.json();
|
|
121
|
+
|
|
122
|
+
if (!organizationsData.organizations || organizationsData.organizations.length === 0) {
|
|
123
|
+
logger.warning('Keine Organizations gefunden. Bitte erstelle zuerst eine Organization im CMS.');
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Let user select organization
|
|
128
|
+
let selectedOrganizationId;
|
|
129
|
+
if (organizationsData.organizations.length === 1) {
|
|
130
|
+
// Auto-select if only one organization
|
|
131
|
+
selectedOrganizationId = organizationsData.organizations[0].id;
|
|
132
|
+
logger.info(`Organization: ${pc.cyan(organizationsData.organizations[0].name)}`);
|
|
133
|
+
} else {
|
|
134
|
+
console.log('');
|
|
135
|
+
selectedOrganizationId = await select({
|
|
136
|
+
message: 'Wähle eine Organization:',
|
|
137
|
+
choices: organizationsData.organizations.map(org => ({
|
|
138
|
+
name: `${org.name} (${org.role})`,
|
|
139
|
+
value: org.id,
|
|
140
|
+
description: `Erstellt am ${new Date(org.createdAt).toLocaleDateString('de-DE')}`
|
|
141
|
+
}))
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const selectedOrganization = organizationsData.organizations.find(o => o.id === selectedOrganizationId);
|
|
146
|
+
|
|
147
|
+
// Get list of tenants for selected organization
|
|
110
148
|
console.log('');
|
|
111
149
|
logger.info('Lade Projekte...');
|
|
112
150
|
|
|
113
|
-
const tenantsUrl = `${baseUrl}/api/cli/tenants?userId=${loginData.user.id}`;
|
|
151
|
+
const tenantsUrl = `${baseUrl}/api/cli/organizations/${selectedOrganizationId}/tenants?userId=${loginData.user.id}`;
|
|
114
152
|
const tenantsResponse = await secureFetch(tenantsUrl);
|
|
115
153
|
|
|
116
154
|
if (!tenantsResponse.ok) {
|
|
@@ -120,7 +158,7 @@ export async function loginCommand(options = {}) {
|
|
|
120
158
|
const tenantsData = await tenantsResponse.json();
|
|
121
159
|
|
|
122
160
|
if (!tenantsData.tenants || tenantsData.tenants.length === 0) {
|
|
123
|
-
logger.warning('Keine Projekte gefunden. Bitte erstelle zuerst ein Projekt im CMS.');
|
|
161
|
+
logger.warning('Keine Projekte in dieser Organization gefunden. Bitte erstelle zuerst ein Projekt im CMS.');
|
|
124
162
|
return;
|
|
125
163
|
}
|
|
126
164
|
|
|
@@ -160,6 +198,7 @@ export async function loginCommand(options = {}) {
|
|
|
160
198
|
// Save auth data
|
|
161
199
|
saveAuth({
|
|
162
200
|
user: loginData.user,
|
|
201
|
+
organization: selectedOrganization,
|
|
163
202
|
tenant: selectedTenant,
|
|
164
203
|
apiToken: tokenData.apiToken,
|
|
165
204
|
baseUrl,
|
|
@@ -169,6 +208,7 @@ export async function loginCommand(options = {}) {
|
|
|
169
208
|
console.log('');
|
|
170
209
|
logger.success(pc.green('✨ Login erfolgreich!'));
|
|
171
210
|
console.log('');
|
|
211
|
+
console.log(` ${pc.bold('Organization:')} ${pc.cyan(selectedOrganization.name)}`);
|
|
172
212
|
console.log(` ${pc.bold('Projekt:')} ${pc.cyan(selectedTenant.name)}`);
|
|
173
213
|
console.log(` ${pc.bold('Subdomain:')} ${pc.cyan(selectedTenant.subdomain)}`);
|
|
174
214
|
console.log('');
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { loadAuth, saveAuth } from '../utils/auth.js';
|
|
2
|
+
import { logger } from '@webmate-studio/core';
|
|
3
|
+
import pc from 'picocolors';
|
|
4
|
+
import { select } from '@inquirer/prompts';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Custom fetch wrapper that accepts self-signed certificates for localhost
|
|
8
|
+
*/
|
|
9
|
+
async function secureFetch(url, options = {}) {
|
|
10
|
+
const isLocalhost = url.includes('localhost') || url.includes('127.0.0.1');
|
|
11
|
+
|
|
12
|
+
if (isLocalhost) {
|
|
13
|
+
const originalValue = process.env.NODE_TLS_REJECT_UNAUTHORIZED;
|
|
14
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
return await fetch(url, options);
|
|
18
|
+
} finally {
|
|
19
|
+
if (originalValue !== undefined) {
|
|
20
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = originalValue;
|
|
21
|
+
} else {
|
|
22
|
+
delete process.env.NODE_TLS_REJECT_UNAUTHORIZED;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return fetch(url, options);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Switch Organization command - Switch to a different organization
|
|
32
|
+
*/
|
|
33
|
+
export async function switchOrgCommand(options = {}) {
|
|
34
|
+
try {
|
|
35
|
+
const auth = loadAuth();
|
|
36
|
+
|
|
37
|
+
if (!auth) {
|
|
38
|
+
logger.error('Not logged in. Run ' + pc.cyan('wm login') + ' first');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.log();
|
|
43
|
+
logger.info(pc.bold('Switch Organization'));
|
|
44
|
+
console.log();
|
|
45
|
+
|
|
46
|
+
// Load available organizations for this user
|
|
47
|
+
logger.info('Loading available organizations...');
|
|
48
|
+
|
|
49
|
+
const organizationsUrl = `${auth.baseUrl}/api/cli/organizations?userId=${auth.user.id}`;
|
|
50
|
+
const organizationsResponse = await secureFetch(organizationsUrl);
|
|
51
|
+
|
|
52
|
+
if (!organizationsResponse.ok) {
|
|
53
|
+
throw new Error('Failed to load organizations from CMS');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const organizationsData = await organizationsResponse.json();
|
|
57
|
+
|
|
58
|
+
if (!organizationsData.organizations || organizationsData.organizations.length === 0) {
|
|
59
|
+
logger.error('No organizations found');
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Show current organization
|
|
64
|
+
const currentOrganization = auth.organization;
|
|
65
|
+
console.log();
|
|
66
|
+
if (currentOrganization) {
|
|
67
|
+
console.log(pc.gray(' Current: ') + pc.cyan(currentOrganization.name));
|
|
68
|
+
}
|
|
69
|
+
console.log();
|
|
70
|
+
|
|
71
|
+
// Let user select a different organization
|
|
72
|
+
const selectedOrganizationId = await select({
|
|
73
|
+
message: 'Switch to:',
|
|
74
|
+
choices: organizationsData.organizations.map(org => ({
|
|
75
|
+
name: `${org.name} (${org.role})`,
|
|
76
|
+
value: org.id,
|
|
77
|
+
description: org.id === currentOrganization?.id ? 'Current organization' : `Created ${new Date(org.createdAt).toLocaleDateString('de-DE')}`
|
|
78
|
+
}))
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const selectedOrganization = organizationsData.organizations.find(o => o.id === selectedOrganizationId);
|
|
82
|
+
|
|
83
|
+
// If same organization, no need to switch
|
|
84
|
+
if (currentOrganization && selectedOrganization.id === currentOrganization.id) {
|
|
85
|
+
console.log();
|
|
86
|
+
logger.info('Already using this organization');
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Load tenants for new organization
|
|
91
|
+
console.log();
|
|
92
|
+
logger.info('Loading projects...');
|
|
93
|
+
|
|
94
|
+
const tenantsUrl = `${auth.baseUrl}/api/cli/organizations/${selectedOrganizationId}/tenants?userId=${auth.user.id}`;
|
|
95
|
+
const tenantsResponse = await secureFetch(tenantsUrl);
|
|
96
|
+
|
|
97
|
+
if (!tenantsResponse.ok) {
|
|
98
|
+
throw new Error('Failed to load projects');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const tenantsData = await tenantsResponse.json();
|
|
102
|
+
|
|
103
|
+
if (!tenantsData.tenants || tenantsData.tenants.length === 0) {
|
|
104
|
+
logger.warning('No projects found in this organization. Please create a project first.');
|
|
105
|
+
|
|
106
|
+
// Update auth with new organization but no tenant
|
|
107
|
+
const newAuth = {
|
|
108
|
+
...auth,
|
|
109
|
+
organization: selectedOrganization,
|
|
110
|
+
tenant: null,
|
|
111
|
+
apiToken: null
|
|
112
|
+
};
|
|
113
|
+
saveAuth(newAuth);
|
|
114
|
+
|
|
115
|
+
console.log();
|
|
116
|
+
logger.success(pc.green('✨ Switched to organization: ') + pc.cyan(selectedOrganization.name));
|
|
117
|
+
console.log();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Let user select a tenant
|
|
122
|
+
const selectedTenantId = await select({
|
|
123
|
+
message: 'Select a project:',
|
|
124
|
+
choices: tenantsData.tenants.map(t => ({
|
|
125
|
+
name: `${t.name} (${t.subdomain})`,
|
|
126
|
+
value: t.id,
|
|
127
|
+
description: `Created ${new Date(t.createdAt).toLocaleDateString('de-DE')}`
|
|
128
|
+
}))
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const selectedTenant = tenantsData.tenants.find(t => t.id === selectedTenantId);
|
|
132
|
+
|
|
133
|
+
// Generate API token for selected tenant
|
|
134
|
+
console.log();
|
|
135
|
+
logger.info('Generating API token...');
|
|
136
|
+
|
|
137
|
+
const tokenUrl = `${auth.baseUrl}/api/cli/tenants`;
|
|
138
|
+
const tokenResponse = await secureFetch(tokenUrl, {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: { 'Content-Type': 'application/json' },
|
|
141
|
+
body: JSON.stringify({
|
|
142
|
+
userId: auth.user.id,
|
|
143
|
+
tenantId: selectedTenantId
|
|
144
|
+
})
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (!tokenResponse.ok) {
|
|
148
|
+
throw new Error('Failed to generate API token');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const tokenData = await tokenResponse.json();
|
|
152
|
+
|
|
153
|
+
// Update auth with new organization, tenant and token
|
|
154
|
+
const newAuth = {
|
|
155
|
+
...auth,
|
|
156
|
+
organization: selectedOrganization,
|
|
157
|
+
tenant: selectedTenant,
|
|
158
|
+
apiToken: tokenData.apiToken
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
saveAuth(newAuth);
|
|
162
|
+
|
|
163
|
+
console.log();
|
|
164
|
+
logger.success(pc.green('✨ Switched to organization: ') + pc.cyan(selectedOrganization.name));
|
|
165
|
+
console.log();
|
|
166
|
+
console.log(` ${pc.bold('Project:')} ${pc.cyan(selectedTenant.name)}`);
|
|
167
|
+
console.log(` ${pc.bold('Subdomain:')} ${pc.cyan(selectedTenant.subdomain)}`);
|
|
168
|
+
console.log();
|
|
169
|
+
console.log(pc.yellow('⚠ ') + pc.gray('If you have ') + pc.cyan('wm dev') + pc.gray(' running, please restart it to load the new project.'));
|
|
170
|
+
console.log();
|
|
171
|
+
|
|
172
|
+
} catch (error) {
|
|
173
|
+
console.log();
|
|
174
|
+
logger.error(`Switch organization failed: ${error.message}`);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
}
|
package/src/commands/switch.js
CHANGED
|
@@ -39,14 +39,23 @@ export async function switchCommand(options = {}) {
|
|
|
39
39
|
process.exit(1);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
if (!auth.organization) {
|
|
43
|
+
logger.error('No organization selected. Run ' + pc.cyan('wm switch-org') + ' first');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
42
47
|
console.log();
|
|
43
48
|
logger.info(pc.bold('Switch Project'));
|
|
44
49
|
console.log();
|
|
45
50
|
|
|
46
|
-
//
|
|
51
|
+
// Show current organization
|
|
52
|
+
console.log(pc.gray(' Organization: ') + pc.cyan(auth.organization.name));
|
|
53
|
+
console.log();
|
|
54
|
+
|
|
55
|
+
// Load available tenants for current organization
|
|
47
56
|
logger.info('Loading available projects...');
|
|
48
57
|
|
|
49
|
-
const tenantsUrl = `${auth.baseUrl}/api/cli/tenants?userId=${auth.user.id}`;
|
|
58
|
+
const tenantsUrl = `${auth.baseUrl}/api/cli/organizations/${auth.organization.id}/tenants?userId=${auth.user.id}`;
|
|
50
59
|
const tenantsResponse = await secureFetch(tenantsUrl);
|
|
51
60
|
|
|
52
61
|
if (!tenantsResponse.ok) {
|
|
@@ -56,7 +65,10 @@ export async function switchCommand(options = {}) {
|
|
|
56
65
|
const tenantsData = await tenantsResponse.json();
|
|
57
66
|
|
|
58
67
|
if (!tenantsData.tenants || tenantsData.tenants.length === 0) {
|
|
59
|
-
logger.error('No projects found');
|
|
68
|
+
logger.error('No projects found in this organization');
|
|
69
|
+
console.log();
|
|
70
|
+
console.log(pc.gray(' Tip: Use ') + pc.cyan('wm switch-org') + pc.gray(' to switch to a different organization'));
|
|
71
|
+
console.log();
|
|
60
72
|
process.exit(1);
|
|
61
73
|
}
|
|
62
74
|
|