@webmate-studio/cli 0.3.45 → 0.3.46
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.js +0 -53
- package/package.json +1 -1
- package/src/commands/build.js +0 -113
- package/src/commands/info.js +0 -49
- package/src/commands/login.js +0 -233
- package/src/commands/logout.js +0 -20
- package/src/commands/switch-org.js +0 -177
- package/src/commands/switch.js +0 -143
package/bin/wm.js
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
|
-
import { buildCommand } from '../src/commands/build.js';
|
|
5
|
-
import { pushCommand } from '../src/commands/push.js';
|
|
6
4
|
import { devCommand } from '../src/commands/dev.js';
|
|
7
5
|
import { initCommand } from '../src/commands/init.js';
|
|
8
|
-
import { loginCommand } from '../src/commands/login.js';
|
|
9
6
|
import { generate } from '../src/commands/generate.js';
|
|
10
|
-
import { logout } from '../src/commands/logout.js';
|
|
11
|
-
import { info } from '../src/commands/info.js';
|
|
12
|
-
import { switchCommand } from '../src/commands/switch.js';
|
|
13
7
|
import { readFileSync } from 'fs';
|
|
14
8
|
import { fileURLToPath } from 'url';
|
|
15
9
|
import { dirname, join } from 'path';
|
|
@@ -24,13 +18,6 @@ program
|
|
|
24
18
|
.description('Webmate Component CLI - Build and manage HTML-first components')
|
|
25
19
|
.version(packageJson.version);
|
|
26
20
|
|
|
27
|
-
// wm login - Login to CMS
|
|
28
|
-
program
|
|
29
|
-
.command('login')
|
|
30
|
-
.description('Login to Webmate CMS')
|
|
31
|
-
.option('-u, --url <url>', 'CMS base URL')
|
|
32
|
-
.action(loginCommand);
|
|
33
|
-
|
|
34
21
|
// wm init - Create new component project
|
|
35
22
|
program
|
|
36
23
|
.command('init')
|
|
@@ -58,44 +45,4 @@ program
|
|
|
58
45
|
.option('-o, --open', 'Open browser automatically')
|
|
59
46
|
.action(devCommand);
|
|
60
47
|
|
|
61
|
-
// wm build - Build components
|
|
62
|
-
program
|
|
63
|
-
.command('build')
|
|
64
|
-
.description('Build components for production')
|
|
65
|
-
.option('-o, --output <dir>', 'Output directory', './dist')
|
|
66
|
-
.option('-m, --minify', 'Minify output')
|
|
67
|
-
.option('--watch', 'Watch for changes')
|
|
68
|
-
.action(buildCommand);
|
|
69
|
-
|
|
70
|
-
// wm push - Upload to CMS
|
|
71
|
-
program
|
|
72
|
-
.command('push')
|
|
73
|
-
.description('Upload components to CMS (auto-builds by default)')
|
|
74
|
-
.option('-t, --target <url>', 'CMS target URL')
|
|
75
|
-
.option('--token <token>', 'Authentication token')
|
|
76
|
-
.option('-f, --force', 'Overwrite existing version (development only)')
|
|
77
|
-
.option('--patch', 'Increment patch version (x.y.Z)')
|
|
78
|
-
.option('--minor', 'Increment minor version (x.Y.0)')
|
|
79
|
-
.option('--major', 'Increment major version (X.0.0)')
|
|
80
|
-
.option('--no-build', 'Skip auto-build, use existing dist/ (for CI/CD)')
|
|
81
|
-
.action(pushCommand);
|
|
82
|
-
|
|
83
|
-
// wm logout - Logout from CMS
|
|
84
|
-
program
|
|
85
|
-
.command('logout')
|
|
86
|
-
.description('Logout from Webmate CMS')
|
|
87
|
-
.action(logout);
|
|
88
|
-
|
|
89
|
-
// wm info - Show current project information
|
|
90
|
-
program
|
|
91
|
-
.command('info')
|
|
92
|
-
.description('Show current project information')
|
|
93
|
-
.action(info);
|
|
94
|
-
|
|
95
|
-
// wm switch - Switch to a different project
|
|
96
|
-
program
|
|
97
|
-
.command('switch')
|
|
98
|
-
.description('Switch to a different project')
|
|
99
|
-
.action(switchCommand);
|
|
100
|
-
|
|
101
48
|
program.parse();
|
package/package.json
CHANGED
package/src/commands/build.js
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { build as buildComponents } from '@webmate-studio/builder';
|
|
2
|
-
import { logger } from '@webmate-studio/core';
|
|
3
|
-
import { isLoggedIn, loadAuth, getTenantCmsUrl } from '../utils/auth.js';
|
|
4
|
-
import ora from 'ora';
|
|
5
|
-
import pc from 'picocolors';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Load design tokens from CMS
|
|
9
|
-
*/
|
|
10
|
-
async function loadDesignTokens() {
|
|
11
|
-
try {
|
|
12
|
-
const auth = loadAuth();
|
|
13
|
-
if (!auth || !auth.apiToken) {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const cmsUrl = getTenantCmsUrl();
|
|
18
|
-
if (!cmsUrl) {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Disable SSL verification for localhost
|
|
23
|
-
const isLocalhost = cmsUrl.includes('localhost') || cmsUrl.includes('127.0.0.1');
|
|
24
|
-
if (isLocalhost) {
|
|
25
|
-
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const response = await fetch(`${cmsUrl}/api/design-tokens`, {
|
|
29
|
-
headers: {
|
|
30
|
-
'x-api-token': auth.apiToken
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
if (!response.ok) {
|
|
35
|
-
logger.warn('Could not load design tokens from CMS');
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const data = await response.json();
|
|
40
|
-
|
|
41
|
-
// Restore SSL verification
|
|
42
|
-
if (isLocalhost) {
|
|
43
|
-
delete process.env.NODE_TLS_REJECT_UNAUTHORIZED;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return data.tokens || null;
|
|
47
|
-
} catch (error) {
|
|
48
|
-
logger.warn(`Error loading design tokens: ${error.message}`);
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Build command - compile components to distribution
|
|
55
|
-
* Requires login for project context
|
|
56
|
-
*/
|
|
57
|
-
export async function buildCommand(options) {
|
|
58
|
-
// Check if logged in
|
|
59
|
-
if (!isLoggedIn()) {
|
|
60
|
-
console.log('');
|
|
61
|
-
logger.error('Nicht eingeloggt.');
|
|
62
|
-
logger.info(`Bitte zuerst einloggen: ${pc.cyan('wm login')}`);
|
|
63
|
-
console.log('');
|
|
64
|
-
process.exit(1);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const auth = loadAuth();
|
|
68
|
-
logger.info(`Projekt: ${pc.cyan(auth.tenant.name)}`);
|
|
69
|
-
|
|
70
|
-
// Load design tokens from CMS
|
|
71
|
-
const designTokens = await loadDesignTokens();
|
|
72
|
-
if (designTokens) {
|
|
73
|
-
logger.success('Loaded design tokens from CMS');
|
|
74
|
-
} else {
|
|
75
|
-
logger.info('Using default design tokens');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const spinner = ora('Building components...').start();
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
const manifest = await buildComponents({
|
|
82
|
-
outputDir: options.output,
|
|
83
|
-
minify: options.minify || false,
|
|
84
|
-
designTokens: designTokens // Pass design tokens to builder
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
spinner.succeed('Build complete!');
|
|
88
|
-
|
|
89
|
-
logger.info(`\n📦 Built ${manifest.components.length} components`);
|
|
90
|
-
logger.info(`📁 Output: ${options.output}`);
|
|
91
|
-
|
|
92
|
-
if (manifest.styles.length > 0) {
|
|
93
|
-
logger.info(`🎨 Styles: ${manifest.styles.length} files`);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (manifest.islands) {
|
|
97
|
-
logger.info(`🏝️ Islands: ${manifest.islands.files.length} files`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Show component list
|
|
101
|
-
console.log('\n📋 Components:');
|
|
102
|
-
for (const comp of manifest.components) {
|
|
103
|
-
const propsCount = comp.props ? Object.keys(comp.props).length : 0;
|
|
104
|
-
logger.info(` • ${comp.name} (${propsCount} props)`);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return manifest;
|
|
108
|
-
} catch (error) {
|
|
109
|
-
spinner.fail('Build failed');
|
|
110
|
-
logger.error(error.message);
|
|
111
|
-
process.exit(1);
|
|
112
|
-
}
|
|
113
|
-
}
|
package/src/commands/info.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { loadAuth } from '../utils/auth.js';
|
|
2
|
-
import { logger } from '@webmate-studio/core';
|
|
3
|
-
import pc from 'picocolors';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Info command - Show current project information
|
|
7
|
-
*/
|
|
8
|
-
export async function info(options = {}) {
|
|
9
|
-
try {
|
|
10
|
-
const auth = loadAuth();
|
|
11
|
-
|
|
12
|
-
if (!auth) {
|
|
13
|
-
logger.error('Not logged in. Run ' + pc.cyan('wm login') + ' first');
|
|
14
|
-
process.exit(1);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
showProjectInfo(auth);
|
|
18
|
-
} catch (error) {
|
|
19
|
-
logger.error(`Info command failed: ${error.message}`);
|
|
20
|
-
process.exit(1);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Show current project information
|
|
26
|
-
*/
|
|
27
|
-
function showProjectInfo(auth) {
|
|
28
|
-
console.log();
|
|
29
|
-
logger.info(pc.bold('Current Project'));
|
|
30
|
-
console.log();
|
|
31
|
-
|
|
32
|
-
// Build CMS URL from base URL and tenant subdomain
|
|
33
|
-
let cmsUrl = 'Not set';
|
|
34
|
-
if (auth.baseUrl && auth.tenant?.subdomain) {
|
|
35
|
-
try {
|
|
36
|
-
const baseUrl = new URL(auth.baseUrl);
|
|
37
|
-
const subdomain = auth.tenant.subdomain;
|
|
38
|
-
cmsUrl = `${baseUrl.protocol}//${subdomain}.cms.${baseUrl.host}`;
|
|
39
|
-
} catch (error) {
|
|
40
|
-
cmsUrl = auth.baseUrl;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
console.log(pc.gray(' CMS URL: ') + pc.cyan(cmsUrl));
|
|
45
|
-
console.log(pc.gray(' User: ') + pc.cyan(auth.user?.email || 'Not set'));
|
|
46
|
-
console.log();
|
|
47
|
-
console.log(pc.gray(' To switch project, run: ') + pc.cyan('wm switch'));
|
|
48
|
-
console.log();
|
|
49
|
-
}
|
package/src/commands/login.js
DELETED
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
import { input, password, select } from '@inquirer/prompts';
|
|
2
|
-
import { logger } from '@webmate-studio/core';
|
|
3
|
-
import { saveAuth, loadAuth, getCmsBaseUrl } from '../utils/auth.js';
|
|
4
|
-
import pc from 'picocolors';
|
|
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
|
-
// Temporarily disable TLS verification for localhost
|
|
14
|
-
const originalValue = process.env.NODE_TLS_REJECT_UNAUTHORIZED;
|
|
15
|
-
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
return await fetch(url, options);
|
|
19
|
-
} finally {
|
|
20
|
-
// Restore original value
|
|
21
|
-
if (originalValue !== undefined) {
|
|
22
|
-
process.env.NODE_TLS_REJECT_UNAUTHORIZED = originalValue;
|
|
23
|
-
} else {
|
|
24
|
-
delete process.env.NODE_TLS_REJECT_UNAUTHORIZED;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// For non-localhost, use regular fetch
|
|
30
|
-
return fetch(url, options);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Login command - Interactive login flow
|
|
35
|
-
*/
|
|
36
|
-
export async function loginCommand(options = {}) {
|
|
37
|
-
console.log('');
|
|
38
|
-
logger.info(pc.bold('Webmate CLI Login'));
|
|
39
|
-
console.log('');
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
// Check if already logged in
|
|
43
|
-
const existingAuth = loadAuth();
|
|
44
|
-
if (existingAuth?.user) {
|
|
45
|
-
const shouldLogout = await select({
|
|
46
|
-
message: `Bereits eingeloggt als ${pc.cyan(existingAuth.user.email)}. Neu anmelden?`,
|
|
47
|
-
choices: [
|
|
48
|
-
{ name: 'Nein, weiter mit aktuellem Login', value: false },
|
|
49
|
-
{ name: 'Ja, neu anmelden', value: true }
|
|
50
|
-
]
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
if (!shouldLogout) {
|
|
54
|
-
logger.info('Login abgebrochen');
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Get base URL (optional, default to production)
|
|
60
|
-
// Note: CLI API routes are under app subdomain
|
|
61
|
-
const baseUrl = options.url || await input({
|
|
62
|
-
message: 'Webmate Base URL:',
|
|
63
|
-
default: 'https://app.webmate-studio.com',
|
|
64
|
-
validate: (value) => {
|
|
65
|
-
try {
|
|
66
|
-
new URL(value);
|
|
67
|
-
return true;
|
|
68
|
-
} catch {
|
|
69
|
-
return 'Bitte gültige URL eingeben';
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
// Get credentials
|
|
75
|
-
const email = await input({
|
|
76
|
-
message: 'E-Mail:',
|
|
77
|
-
validate: (value) => {
|
|
78
|
-
if (!value || !value.includes('@')) {
|
|
79
|
-
return 'Bitte gültige E-Mail-Adresse eingeben';
|
|
80
|
-
}
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
const pwd = await password({
|
|
86
|
-
message: 'Passwort:',
|
|
87
|
-
mask: '*'
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
console.log('');
|
|
91
|
-
logger.info('Authentifiziere...');
|
|
92
|
-
|
|
93
|
-
// Login API call
|
|
94
|
-
const loginUrl = `${baseUrl}/api/cli/auth/login`;
|
|
95
|
-
const loginResponse = await secureFetch(loginUrl, {
|
|
96
|
-
method: 'POST',
|
|
97
|
-
headers: { 'Content-Type': 'application/json' },
|
|
98
|
-
body: JSON.stringify({ email, password: pwd })
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
if (!loginResponse.ok) {
|
|
102
|
-
const error = await loginResponse.json();
|
|
103
|
-
throw new Error(error.error || 'Login fehlgeschlagen');
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const loginData = await loginResponse.json();
|
|
107
|
-
logger.success(`Eingeloggt als ${pc.cyan(loginData.user.email)}`);
|
|
108
|
-
|
|
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
|
|
148
|
-
console.log('');
|
|
149
|
-
logger.info('Lade Projekte...');
|
|
150
|
-
|
|
151
|
-
const tenantsUrl = `${baseUrl}/api/cli/organizations/${selectedOrganizationId}/tenants?userId=${loginData.user.id}`;
|
|
152
|
-
const tenantsResponse = await secureFetch(tenantsUrl);
|
|
153
|
-
|
|
154
|
-
if (!tenantsResponse.ok) {
|
|
155
|
-
throw new Error('Fehler beim Laden der Projekte');
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const tenantsData = await tenantsResponse.json();
|
|
159
|
-
|
|
160
|
-
if (!tenantsData.tenants || tenantsData.tenants.length === 0) {
|
|
161
|
-
logger.warning('Keine Projekte in dieser Organization gefunden. Bitte erstelle zuerst ein Projekt im CMS.');
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Let user select tenant
|
|
166
|
-
console.log('');
|
|
167
|
-
const selectedTenantId = await select({
|
|
168
|
-
message: 'Wähle ein Projekt:',
|
|
169
|
-
choices: tenantsData.tenants.map(t => ({
|
|
170
|
-
name: `${t.name} (${t.subdomain})`,
|
|
171
|
-
value: t.id,
|
|
172
|
-
description: `Erstellt am ${new Date(t.createdAt).toLocaleDateString('de-DE')}`
|
|
173
|
-
}))
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
const selectedTenant = tenantsData.tenants.find(t => t.id === selectedTenantId);
|
|
177
|
-
|
|
178
|
-
// Generate API token for selected tenant
|
|
179
|
-
console.log('');
|
|
180
|
-
logger.info('Generiere API-Token...');
|
|
181
|
-
|
|
182
|
-
const tokenUrl = `${baseUrl}/api/cli/tenants`;
|
|
183
|
-
const tokenResponse = await secureFetch(tokenUrl, {
|
|
184
|
-
method: 'POST',
|
|
185
|
-
headers: { 'Content-Type': 'application/json' },
|
|
186
|
-
body: JSON.stringify({
|
|
187
|
-
userId: loginData.user.id,
|
|
188
|
-
tenantId: selectedTenantId
|
|
189
|
-
})
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
if (!tokenResponse.ok) {
|
|
193
|
-
throw new Error('Fehler beim Generieren des API-Tokens');
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const tokenData = await tokenResponse.json();
|
|
197
|
-
|
|
198
|
-
// Save auth data
|
|
199
|
-
saveAuth({
|
|
200
|
-
user: loginData.user,
|
|
201
|
-
organization: selectedOrganization,
|
|
202
|
-
tenant: selectedTenant,
|
|
203
|
-
apiToken: tokenData.apiToken,
|
|
204
|
-
baseUrl,
|
|
205
|
-
loginAt: new Date().toISOString()
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
console.log('');
|
|
209
|
-
logger.success(pc.green('✨ Login erfolgreich!'));
|
|
210
|
-
console.log('');
|
|
211
|
-
console.log(` ${pc.bold('Organization:')} ${pc.cyan(selectedOrganization.name)}`);
|
|
212
|
-
console.log(` ${pc.bold('Projekt:')} ${pc.cyan(selectedTenant.name)}`);
|
|
213
|
-
console.log(` ${pc.bold('Subdomain:')} ${pc.cyan(selectedTenant.subdomain)}`);
|
|
214
|
-
console.log('');
|
|
215
|
-
console.log(`${pc.gray('Du kannst jetzt')} ${pc.cyan('wm dev')} ${pc.gray('und')} ${pc.cyan('wm push')} ${pc.gray('verwenden.')}`);
|
|
216
|
-
console.log('');
|
|
217
|
-
|
|
218
|
-
} catch (error) {
|
|
219
|
-
console.log('');
|
|
220
|
-
logger.error(`Login fehlgeschlagen: ${error.message}`);
|
|
221
|
-
|
|
222
|
-
// Detaillierte Fehlerausgabe für Debugging
|
|
223
|
-
if (error.cause) {
|
|
224
|
-
logger.error(`Ursache: ${error.cause.message || error.cause}`);
|
|
225
|
-
}
|
|
226
|
-
if (error.stack) {
|
|
227
|
-
console.error('\nStack trace:');
|
|
228
|
-
console.error(error.stack);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
process.exit(1);
|
|
232
|
-
}
|
|
233
|
-
}
|
package/src/commands/logout.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { clearAuth } from '../utils/auth.js';
|
|
2
|
-
import { logger } from '@webmate-studio/core';
|
|
3
|
-
import pc from 'picocolors';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Logout command - removes stored authentication
|
|
7
|
-
*/
|
|
8
|
-
export async function logout(options = {}) {
|
|
9
|
-
try {
|
|
10
|
-
// Clear authentication
|
|
11
|
-
clearAuth();
|
|
12
|
-
|
|
13
|
-
logger.success('Successfully logged out');
|
|
14
|
-
logger.info('Run ' + pc.cyan('wm login') + ' to log in again');
|
|
15
|
-
|
|
16
|
-
} catch (error) {
|
|
17
|
-
logger.error(`Logout failed: ${error.message}`);
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
@@ -1,177 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
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 command - Switch to a different tenant/project
|
|
32
|
-
*/
|
|
33
|
-
export async function switchCommand(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
|
-
if (!auth.organization) {
|
|
43
|
-
logger.error('No organization selected. Run ' + pc.cyan('wm switch-org') + ' first');
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
console.log();
|
|
48
|
-
logger.info(pc.bold('Switch Project'));
|
|
49
|
-
console.log();
|
|
50
|
-
|
|
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
|
|
56
|
-
logger.info('Loading available projects...');
|
|
57
|
-
|
|
58
|
-
const tenantsUrl = `${auth.baseUrl}/api/cli/organizations/${auth.organization.id}/tenants?userId=${auth.user.id}`;
|
|
59
|
-
const tenantsResponse = await secureFetch(tenantsUrl);
|
|
60
|
-
|
|
61
|
-
if (!tenantsResponse.ok) {
|
|
62
|
-
throw new Error('Failed to load projects from CMS');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const tenantsData = await tenantsResponse.json();
|
|
66
|
-
|
|
67
|
-
if (!tenantsData.tenants || tenantsData.tenants.length === 0) {
|
|
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();
|
|
72
|
-
process.exit(1);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Show current project
|
|
76
|
-
const currentTenant = auth.tenant;
|
|
77
|
-
console.log();
|
|
78
|
-
console.log(pc.gray(' Current: ') + pc.cyan(`${currentTenant.name} (${currentTenant.subdomain})`));
|
|
79
|
-
console.log();
|
|
80
|
-
|
|
81
|
-
// Let user select a different tenant
|
|
82
|
-
const selectedTenantId = await select({
|
|
83
|
-
message: 'Switch to:',
|
|
84
|
-
choices: tenantsData.tenants.map(t => ({
|
|
85
|
-
name: `${t.name} (${t.subdomain})`,
|
|
86
|
-
value: t.id,
|
|
87
|
-
description: t.id === currentTenant.id ? 'Current project' : `Created ${new Date(t.createdAt).toLocaleDateString('de-DE')}`
|
|
88
|
-
}))
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
const selectedTenant = tenantsData.tenants.find(t => t.id === selectedTenantId);
|
|
92
|
-
|
|
93
|
-
// If same tenant, no need to switch
|
|
94
|
-
if (selectedTenant.id === currentTenant.id) {
|
|
95
|
-
console.log();
|
|
96
|
-
logger.info('Already using this project');
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Generate new API token for selected tenant
|
|
101
|
-
console.log();
|
|
102
|
-
logger.info('Generating API token...');
|
|
103
|
-
|
|
104
|
-
const tokenUrl = `${auth.baseUrl}/api/cli/tenants`;
|
|
105
|
-
const tokenResponse = await secureFetch(tokenUrl, {
|
|
106
|
-
method: 'POST',
|
|
107
|
-
headers: { 'Content-Type': 'application/json' },
|
|
108
|
-
body: JSON.stringify({
|
|
109
|
-
userId: auth.user.id,
|
|
110
|
-
tenantId: selectedTenantId
|
|
111
|
-
})
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
if (!tokenResponse.ok) {
|
|
115
|
-
throw new Error('Failed to generate API token');
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const tokenData = await tokenResponse.json();
|
|
119
|
-
|
|
120
|
-
// Update auth with new tenant and token
|
|
121
|
-
const newAuth = {
|
|
122
|
-
...auth,
|
|
123
|
-
tenant: selectedTenant,
|
|
124
|
-
apiToken: tokenData.apiToken
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
saveAuth(newAuth);
|
|
128
|
-
|
|
129
|
-
console.log();
|
|
130
|
-
logger.success(pc.green('✨ Switched to project: ') + pc.cyan(selectedTenant.name));
|
|
131
|
-
console.log();
|
|
132
|
-
console.log(` ${pc.bold('Subdomain:')} ${pc.cyan(selectedTenant.subdomain)}`);
|
|
133
|
-
console.log(` ${pc.bold('CMS URL:')} ${pc.cyan(`${new URL(auth.baseUrl).protocol}//${selectedTenant.subdomain}.cms.${new URL(auth.baseUrl).host}`)}`);
|
|
134
|
-
console.log();
|
|
135
|
-
console.log(pc.yellow('⚠ ') + pc.gray('If you have ') + pc.cyan('wm dev') + pc.gray(' running, please restart it to load the new project.'));
|
|
136
|
-
console.log();
|
|
137
|
-
|
|
138
|
-
} catch (error) {
|
|
139
|
-
console.log();
|
|
140
|
-
logger.error(`Switch failed: ${error.message}`);
|
|
141
|
-
process.exit(1);
|
|
142
|
-
}
|
|
143
|
-
}
|