@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 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.10",
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.8",
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",
@@ -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 tenants
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
+ }
@@ -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
- // Load available tenants for this user
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