@oussema_mili/test-pkg-123 1.1.22
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.
Potentially problematic release.
This version of @oussema_mili/test-pkg-123 might be problematic. Click here for more details.
- package/LICENSE +29 -0
- package/README.md +220 -0
- package/auth-callback.html +97 -0
- package/auth.js +276 -0
- package/cli-commands.js +1923 -0
- package/containerManager.js +304 -0
- package/daemon/agentRunner.js +429 -0
- package/daemon/daemonEntry.js +64 -0
- package/daemon/daemonManager.js +271 -0
- package/daemon/logManager.js +227 -0
- package/dist/styles.css +504 -0
- package/docker-actions/apps.js +3938 -0
- package/docker-actions/config-transformer.js +380 -0
- package/docker-actions/containers.js +355 -0
- package/docker-actions/general.js +171 -0
- package/docker-actions/images.js +1128 -0
- package/docker-actions/logs.js +224 -0
- package/docker-actions/metrics.js +270 -0
- package/docker-actions/registry.js +1100 -0
- package/docker-actions/setup-tasks.js +859 -0
- package/docker-actions/terminal.js +247 -0
- package/docker-actions/volumes.js +696 -0
- package/helper-functions.js +193 -0
- package/index.html +83 -0
- package/index.js +341 -0
- package/package.json +82 -0
- package/postcss.config.mjs +5 -0
- package/scripts/release.sh +212 -0
- package/setup/setupWizard.js +403 -0
- package/store/agentSessionStore.js +51 -0
- package/store/agentStore.js +113 -0
- package/store/configStore.js +171 -0
- package/store/daemonStore.js +217 -0
- package/store/deviceCredentialStore.js +107 -0
- package/store/npmTokenStore.js +65 -0
- package/store/registryStore.js +329 -0
- package/store/setupState.js +147 -0
- package/styles.css +1 -0
- package/utils/appLogger.js +223 -0
- package/utils/deviceInfo.js +98 -0
- package/utils/ecrAuth.js +225 -0
- package/utils/encryption.js +112 -0
- package/utils/envSetup.js +44 -0
- package/utils/errorHandler.js +327 -0
- package/utils/portUtils.js +59 -0
- package/utils/prerequisites.js +323 -0
- package/utils/prompts.js +318 -0
- package/utils/ssl-certificates.js +256 -0
- package/websocket-server.js +415 -0
package/utils/prompts.js
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Prompt for registration token
|
|
7
|
+
*
|
|
8
|
+
* @param {string} fenwaveUrl - Fenwave URL to display
|
|
9
|
+
* @returns {Promise<string>} Registration token
|
|
10
|
+
*/
|
|
11
|
+
export async function promptRegistrationToken(fenwaveUrl) {
|
|
12
|
+
console.log(chalk.blue('\n📝 Registration Token Required\n'));
|
|
13
|
+
console.log(chalk.gray(`Get your token from: ${fenwaveUrl}/agent-installer\n`));
|
|
14
|
+
|
|
15
|
+
const answers = await inquirer.prompt([
|
|
16
|
+
{
|
|
17
|
+
type: 'password',
|
|
18
|
+
name: 'token',
|
|
19
|
+
message: 'Enter your registration token:',
|
|
20
|
+
mask: '*',
|
|
21
|
+
validate: (input) => {
|
|
22
|
+
if (!input || input.trim().length === 0) {
|
|
23
|
+
return 'Registration token is required';
|
|
24
|
+
}
|
|
25
|
+
if (input.length < 32) {
|
|
26
|
+
return 'Invalid token format';
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
return answers.token.trim();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Prompt for confirmation
|
|
38
|
+
*
|
|
39
|
+
* @param {string} message - Confirmation message
|
|
40
|
+
* @param {boolean} defaultValue - Default value (true/false)
|
|
41
|
+
* @returns {Promise<boolean>} User's answer
|
|
42
|
+
*/
|
|
43
|
+
export async function promptConfirmation(message, defaultValue = true) {
|
|
44
|
+
const answers = await inquirer.prompt([
|
|
45
|
+
{
|
|
46
|
+
type: 'confirm',
|
|
47
|
+
name: 'confirmed',
|
|
48
|
+
message,
|
|
49
|
+
default: defaultValue,
|
|
50
|
+
},
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
return answers.confirmed;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Prompt for Docker registry configuration
|
|
58
|
+
*
|
|
59
|
+
* @returns {Promise<Object>} Registry configuration
|
|
60
|
+
*/
|
|
61
|
+
export async function promptDockerRegistries() {
|
|
62
|
+
console.log(chalk.blue('\n🐳 Docker Registry Configuration\n'));
|
|
63
|
+
|
|
64
|
+
const answers = await inquirer.prompt([
|
|
65
|
+
{
|
|
66
|
+
type: 'confirm',
|
|
67
|
+
name: 'configureEcr',
|
|
68
|
+
message: 'Configure AWS ECR for Fenwave images?',
|
|
69
|
+
default: true,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
type: 'input',
|
|
73
|
+
name: 'awsRegion',
|
|
74
|
+
message: 'AWS Region:',
|
|
75
|
+
default: 'eu-west-1',
|
|
76
|
+
when: (answers) => answers.configureEcr,
|
|
77
|
+
validate: (input) => {
|
|
78
|
+
if (!input || input.trim().length === 0) {
|
|
79
|
+
return 'AWS Region is required';
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
type: 'confirm',
|
|
86
|
+
name: 'addCustomRegistry',
|
|
87
|
+
message: 'Add custom Docker registry?',
|
|
88
|
+
default: false,
|
|
89
|
+
},
|
|
90
|
+
]);
|
|
91
|
+
|
|
92
|
+
const registries = [];
|
|
93
|
+
|
|
94
|
+
if (answers.configureEcr) {
|
|
95
|
+
registries.push({
|
|
96
|
+
type: 'ecr',
|
|
97
|
+
region: answers.awsRegion,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (answers.addCustomRegistry) {
|
|
102
|
+
const customRegistry = await inquirer.prompt([
|
|
103
|
+
{
|
|
104
|
+
type: 'input',
|
|
105
|
+
name: 'url',
|
|
106
|
+
message: 'Registry URL:',
|
|
107
|
+
validate: (input) => {
|
|
108
|
+
if (!input || input.trim().length === 0) {
|
|
109
|
+
return 'Registry URL is required';
|
|
110
|
+
}
|
|
111
|
+
return true;
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: 'input',
|
|
116
|
+
name: 'username',
|
|
117
|
+
message: 'Username:',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
type: 'password',
|
|
121
|
+
name: 'password',
|
|
122
|
+
message: 'Password:',
|
|
123
|
+
mask: '*',
|
|
124
|
+
},
|
|
125
|
+
]);
|
|
126
|
+
|
|
127
|
+
registries.push({
|
|
128
|
+
type: 'custom',
|
|
129
|
+
...customRegistry,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return registries;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Prompt for choice from a list
|
|
138
|
+
*
|
|
139
|
+
* @param {string} message - Prompt message
|
|
140
|
+
* @param {Array} choices - Array of choices
|
|
141
|
+
* @param {string} defaultChoice - Default choice
|
|
142
|
+
* @returns {Promise<string>} Selected choice
|
|
143
|
+
*/
|
|
144
|
+
export async function promptChoice(message, choices, defaultChoice) {
|
|
145
|
+
const answers = await inquirer.prompt([
|
|
146
|
+
{
|
|
147
|
+
type: 'list',
|
|
148
|
+
name: 'choice',
|
|
149
|
+
message,
|
|
150
|
+
choices,
|
|
151
|
+
default: defaultChoice,
|
|
152
|
+
},
|
|
153
|
+
]);
|
|
154
|
+
|
|
155
|
+
return answers.choice;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Prompt for text input
|
|
160
|
+
*
|
|
161
|
+
* @param {string} message - Prompt message
|
|
162
|
+
* @param {string} defaultValue - Default value
|
|
163
|
+
* @param {Function} validate - Validation function
|
|
164
|
+
* @returns {Promise<string>} User input
|
|
165
|
+
*/
|
|
166
|
+
export async function promptInput(message, defaultValue, validate) {
|
|
167
|
+
const answers = await inquirer.prompt([
|
|
168
|
+
{
|
|
169
|
+
type: 'input',
|
|
170
|
+
name: 'input',
|
|
171
|
+
message,
|
|
172
|
+
default: defaultValue,
|
|
173
|
+
validate,
|
|
174
|
+
},
|
|
175
|
+
]);
|
|
176
|
+
|
|
177
|
+
return answers.input;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Display progress with spinner
|
|
182
|
+
*
|
|
183
|
+
* @param {string} message - Progress message
|
|
184
|
+
* @param {Promise} promise - Promise to await
|
|
185
|
+
* @returns {Promise} Result of the promise
|
|
186
|
+
*/
|
|
187
|
+
export async function displayProgress(message, promise) {
|
|
188
|
+
const spinner = ora(message).start();
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const result = await promise;
|
|
192
|
+
spinner.succeed(chalk.green(message));
|
|
193
|
+
return result;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
spinner.fail(chalk.red(`${message} - ${error.message}`));
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Display success message
|
|
202
|
+
*
|
|
203
|
+
* @param {string} message - Success message
|
|
204
|
+
*/
|
|
205
|
+
export function displaySuccess(message) {
|
|
206
|
+
console.log(chalk.green('✅ ' + message));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Display error message
|
|
211
|
+
*
|
|
212
|
+
* @param {string} message - Error message
|
|
213
|
+
*/
|
|
214
|
+
export function displayError(message) {
|
|
215
|
+
console.log(chalk.red('❌ ' + message));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Display warning message
|
|
220
|
+
*
|
|
221
|
+
* @param {string} message - Warning message
|
|
222
|
+
*/
|
|
223
|
+
export function displayWarning(message) {
|
|
224
|
+
console.log(chalk.yellow('⚠️ ' + message));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Display info message
|
|
229
|
+
*
|
|
230
|
+
* @param {string} message - Info message
|
|
231
|
+
*/
|
|
232
|
+
export function displayInfo(message) {
|
|
233
|
+
console.log(chalk.blue('ℹ️ ' + message));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Display header with decorative border
|
|
238
|
+
*
|
|
239
|
+
* @param {string} title - Header title
|
|
240
|
+
*/
|
|
241
|
+
export function displayHeader(title) {
|
|
242
|
+
const border = '━'.repeat(70);
|
|
243
|
+
console.log('\n' + chalk.cyan(border));
|
|
244
|
+
console.log(chalk.cyan.bold(` ${title}`));
|
|
245
|
+
console.log(chalk.cyan(border) + '\n');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Display step indicator
|
|
250
|
+
*
|
|
251
|
+
* @param {number} current - Current step
|
|
252
|
+
* @param {number} total - Total steps
|
|
253
|
+
* @param {string} description - Step description
|
|
254
|
+
*/
|
|
255
|
+
export function displayStep(current, total, description) {
|
|
256
|
+
console.log(chalk.bold(`\nStep ${current}/${total}: ${description}`));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Display a list of items
|
|
261
|
+
*
|
|
262
|
+
* @param {Array} items - Items to display
|
|
263
|
+
* @param {string} prefix - Prefix for each item (default: ' • ')
|
|
264
|
+
*/
|
|
265
|
+
export function displayList(items, prefix = ' • ') {
|
|
266
|
+
items.forEach((item) => {
|
|
267
|
+
console.log(chalk.gray(prefix + item));
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Display key-value pairs
|
|
273
|
+
*
|
|
274
|
+
* @param {Object} data - Data to display
|
|
275
|
+
* @param {string} indent - Indentation (default: ' ')
|
|
276
|
+
*/
|
|
277
|
+
export function displayKeyValue(data, indent = ' ') {
|
|
278
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
279
|
+
console.log(chalk.gray(`${indent}${key}: `) + chalk.white(value));
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Clear console
|
|
285
|
+
*/
|
|
286
|
+
export function clearConsole() {
|
|
287
|
+
console.clear();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Prompt for backend URL
|
|
292
|
+
*
|
|
293
|
+
* @param {string} defaultUrl - Default backend URL
|
|
294
|
+
* @returns {Promise<string>} Backend URL
|
|
295
|
+
*/
|
|
296
|
+
export async function promptBackendUrl(defaultUrl = 'http://localhost:7007') {
|
|
297
|
+
const answers = await inquirer.prompt([
|
|
298
|
+
{
|
|
299
|
+
type: 'input',
|
|
300
|
+
name: 'url',
|
|
301
|
+
message: 'Fenwave Backend URL:',
|
|
302
|
+
default: defaultUrl,
|
|
303
|
+
validate: (input) => {
|
|
304
|
+
if (!input || input.trim().length === 0) {
|
|
305
|
+
return 'Backend URL is required';
|
|
306
|
+
}
|
|
307
|
+
try {
|
|
308
|
+
new URL(input);
|
|
309
|
+
return true;
|
|
310
|
+
} catch (error) {
|
|
311
|
+
return 'Invalid URL format';
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
]);
|
|
316
|
+
|
|
317
|
+
return answers.url.trim();
|
|
318
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import { exec } from "child_process";
|
|
5
|
+
import { promisify } from "util";
|
|
6
|
+
|
|
7
|
+
const execAsync = promisify(exec);
|
|
8
|
+
|
|
9
|
+
// Directory for storing generated certificates
|
|
10
|
+
const CERTS_DIR = path.join(os.homedir(), ".fenwave", "certs");
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Ensure the certificates directory exists
|
|
14
|
+
*/
|
|
15
|
+
function ensureCertsDir() {
|
|
16
|
+
if (!fs.existsSync(CERTS_DIR)) {
|
|
17
|
+
fs.mkdirSync(CERTS_DIR, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Check if mkcert is installed and available
|
|
23
|
+
* @returns {Promise<{installed: boolean, version?: string, error?: string}>}
|
|
24
|
+
*/
|
|
25
|
+
export async function checkMkcertInstalled() {
|
|
26
|
+
try {
|
|
27
|
+
const { stdout } = await execAsync("mkcert -version");
|
|
28
|
+
return {
|
|
29
|
+
installed: true,
|
|
30
|
+
version: stdout.trim(),
|
|
31
|
+
};
|
|
32
|
+
} catch (error) {
|
|
33
|
+
return {
|
|
34
|
+
installed: false,
|
|
35
|
+
error: "mkcert is not installed. Install it with: brew install mkcert (macOS) or see https://github.com/FiloSottile/mkcert",
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Install the mkcert CA to the system trust store
|
|
42
|
+
* This allows browsers to trust locally-generated certificates
|
|
43
|
+
* @returns {Promise<{success: boolean, message: string, caPath?: string}>}
|
|
44
|
+
*/
|
|
45
|
+
export async function installMkcertCA() {
|
|
46
|
+
try {
|
|
47
|
+
// Run mkcert -install to add CA to system trust store
|
|
48
|
+
await execAsync("mkcert -install");
|
|
49
|
+
|
|
50
|
+
// Get the CA root directory
|
|
51
|
+
const { stdout: caRootDir } = await execAsync("mkcert -CAROOT");
|
|
52
|
+
const caDir = caRootDir.trim();
|
|
53
|
+
const caPath = path.join(caDir, "rootCA.pem");
|
|
54
|
+
|
|
55
|
+
// Copy CA to our certs directory for easy access
|
|
56
|
+
ensureCertsDir();
|
|
57
|
+
const localCAPath = path.join(CERTS_DIR, "rootCA.pem");
|
|
58
|
+
|
|
59
|
+
if (fs.existsSync(caPath)) {
|
|
60
|
+
fs.copyFileSync(caPath, localCAPath);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
success: true,
|
|
65
|
+
message: "mkcert CA installed successfully",
|
|
66
|
+
caPath: localCAPath,
|
|
67
|
+
};
|
|
68
|
+
} catch (error) {
|
|
69
|
+
return {
|
|
70
|
+
success: false,
|
|
71
|
+
message: `Failed to install mkcert CA: ${error.message}`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get the path to the mkcert CA certificate
|
|
78
|
+
* @returns {Promise<{success: boolean, caPath?: string, error?: string}>}
|
|
79
|
+
*/
|
|
80
|
+
export async function getMkcertCAPath() {
|
|
81
|
+
try {
|
|
82
|
+
const { stdout: caRootDir } = await execAsync("mkcert -CAROOT");
|
|
83
|
+
const caDir = caRootDir.trim();
|
|
84
|
+
const caPath = path.join(caDir, "rootCA.pem");
|
|
85
|
+
|
|
86
|
+
// Also check our local copy
|
|
87
|
+
const localCAPath = path.join(CERTS_DIR, "rootCA.pem");
|
|
88
|
+
|
|
89
|
+
if (fs.existsSync(localCAPath)) {
|
|
90
|
+
return { success: true, caPath: localCAPath };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (fs.existsSync(caPath)) {
|
|
94
|
+
// Copy to local for consistency
|
|
95
|
+
ensureCertsDir();
|
|
96
|
+
fs.copyFileSync(caPath, localCAPath);
|
|
97
|
+
return { success: true, caPath: localCAPath };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
success: false,
|
|
102
|
+
error: "mkcert CA not found. Run 'mkcert -install' first.",
|
|
103
|
+
};
|
|
104
|
+
} catch (error) {
|
|
105
|
+
return {
|
|
106
|
+
success: false,
|
|
107
|
+
error: `Failed to get mkcert CA path: ${error.message}`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Derive a certificate name from domain(s)
|
|
114
|
+
* @param {string[]} domains - Array of domain names
|
|
115
|
+
* @returns {string} Sanitized certificate name
|
|
116
|
+
*/
|
|
117
|
+
function deriveCertName(domains) {
|
|
118
|
+
if (!domains || domains.length === 0) {
|
|
119
|
+
return "localhost";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Use first domain, remove wildcards and sanitize
|
|
123
|
+
const firstDomain = domains[0]
|
|
124
|
+
.replace(/^\*\./, "") // Remove leading wildcard
|
|
125
|
+
.replace(/[^a-zA-Z0-9.-]/g, "-") // Replace invalid chars
|
|
126
|
+
.toLowerCase();
|
|
127
|
+
|
|
128
|
+
return firstDomain || "localhost";
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Generate SSL certificates for the specified domains
|
|
133
|
+
* @param {string[]} domains - Array of domain names (e.g., ["example.local", "*.example.local"])
|
|
134
|
+
* @param {string} [certName] - Optional certificate file prefix
|
|
135
|
+
* @returns {Promise<{success: boolean, certPath?: string, keyPath?: string, message: string}>}
|
|
136
|
+
*/
|
|
137
|
+
export async function generateCertificates(domains, certName) {
|
|
138
|
+
if (!domains || domains.length === 0) {
|
|
139
|
+
return {
|
|
140
|
+
success: false,
|
|
141
|
+
message: "No domains specified for certificate generation",
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Check mkcert is installed
|
|
146
|
+
const mkcertCheck = await checkMkcertInstalled();
|
|
147
|
+
if (!mkcertCheck.installed) {
|
|
148
|
+
return {
|
|
149
|
+
success: false,
|
|
150
|
+
message: mkcertCheck.error,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
ensureCertsDir();
|
|
155
|
+
|
|
156
|
+
// Derive or use provided cert name
|
|
157
|
+
const name = certName || deriveCertName(domains);
|
|
158
|
+
const certPath = path.join(CERTS_DIR, `${name}.pem`);
|
|
159
|
+
const keyPath = path.join(CERTS_DIR, `${name}-key.pem`);
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
// Build mkcert command
|
|
163
|
+
// mkcert -cert-file <cert> -key-file <key> domain1 domain2 ...
|
|
164
|
+
const domainsArg = domains.map(d => `"${d}"`).join(" ");
|
|
165
|
+
const command = `mkcert -cert-file "${certPath}" -key-file "${keyPath}" ${domainsArg}`;
|
|
166
|
+
|
|
167
|
+
const { stdout, stderr } = await execAsync(command);
|
|
168
|
+
|
|
169
|
+
// Verify files were created
|
|
170
|
+
if (!fs.existsSync(certPath) || !fs.existsSync(keyPath)) {
|
|
171
|
+
return {
|
|
172
|
+
success: false,
|
|
173
|
+
message: "Certificate files were not created",
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
success: true,
|
|
179
|
+
certPath,
|
|
180
|
+
keyPath,
|
|
181
|
+
message: `Generated certificates for: ${domains.join(", ")}`,
|
|
182
|
+
output: stdout || stderr,
|
|
183
|
+
};
|
|
184
|
+
} catch (error) {
|
|
185
|
+
return {
|
|
186
|
+
success: false,
|
|
187
|
+
message: `Failed to generate certificates: ${error.message}`,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Get paths to existing certificates
|
|
194
|
+
* @param {string} certName - Certificate name/prefix
|
|
195
|
+
* @returns {{certPath: string, keyPath: string, caPath: string, exists: boolean}}
|
|
196
|
+
*/
|
|
197
|
+
export function getCertificatePaths(certName) {
|
|
198
|
+
const certPath = path.join(CERTS_DIR, `${certName}.pem`);
|
|
199
|
+
const keyPath = path.join(CERTS_DIR, `${certName}-key.pem`);
|
|
200
|
+
const caPath = path.join(CERTS_DIR, "rootCA.pem");
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
certPath,
|
|
204
|
+
keyPath,
|
|
205
|
+
caPath,
|
|
206
|
+
exists: fs.existsSync(certPath) && fs.existsSync(keyPath),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Check if certificates exist for a given name
|
|
212
|
+
* @param {string} certName - Certificate name/prefix
|
|
213
|
+
* @returns {boolean}
|
|
214
|
+
*/
|
|
215
|
+
export function certificatesExist(certName) {
|
|
216
|
+
const { exists } = getCertificatePaths(certName);
|
|
217
|
+
return exists;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Delete certificates for a given name
|
|
222
|
+
* @param {string} certName - Certificate name/prefix
|
|
223
|
+
* @returns {{success: boolean, message: string}}
|
|
224
|
+
*/
|
|
225
|
+
export function deleteCertificates(certName) {
|
|
226
|
+
const { certPath, keyPath } = getCertificatePaths(certName);
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
if (fs.existsSync(certPath)) {
|
|
230
|
+
fs.unlinkSync(certPath);
|
|
231
|
+
}
|
|
232
|
+
if (fs.existsSync(keyPath)) {
|
|
233
|
+
fs.unlinkSync(keyPath);
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
success: true,
|
|
237
|
+
message: `Deleted certificates for: ${certName}`,
|
|
238
|
+
};
|
|
239
|
+
} catch (error) {
|
|
240
|
+
return {
|
|
241
|
+
success: false,
|
|
242
|
+
message: `Failed to delete certificates: ${error.message}`,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export default {
|
|
248
|
+
checkMkcertInstalled,
|
|
249
|
+
installMkcertCA,
|
|
250
|
+
getMkcertCAPath,
|
|
251
|
+
generateCertificates,
|
|
252
|
+
getCertificatePaths,
|
|
253
|
+
certificatesExist,
|
|
254
|
+
deleteCertificates,
|
|
255
|
+
CERTS_DIR,
|
|
256
|
+
};
|