paymongo-cli 1.4.2 → 1.4.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/.github/copilot-instructions.md +1 -1
- package/AGENTS.md +0 -1
- package/CHANGELOG.md +72 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/commands/dev/logs.js +46 -0
- package/dist/commands/dev/status.js +41 -0
- package/dist/commands/dev/stop.js +41 -0
- package/dist/commands/dev.js +7 -282
- package/dist/commands/env.js +27 -3
- package/dist/commands/generate/templates/checkout-page/index.js +539 -0
- package/dist/commands/generate/templates/index.js +5 -0
- package/dist/commands/generate/templates/payment-intent/javascript.js +71 -0
- package/dist/commands/generate/templates/payment-intent/typescript.js +95 -0
- package/dist/commands/generate/templates/webhook-handler/javascript.js +174 -0
- package/dist/commands/generate/templates/webhook-handler/typescript.js +138 -0
- package/dist/commands/generate.js +7 -999
- package/dist/commands/init.js +27 -5
- package/dist/commands/login.js +27 -5
- package/dist/services/api/client.js +265 -15
- package/dist/services/api/undici-client.js +15 -21
- package/dist/services/dev/server.js +167 -0
- package/package.json +4 -4
- package/dist/commands/gui.d.ts +0 -4
- package/dist/commands/gui.d.ts.map +0 -1
- package/dist/commands/gui.js +0 -69
- package/dist/commands/gui.js.map +0 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { DevProcessManager } from '../../services/dev/process-manager.js';
|
|
5
|
+
const logsCommand = new Command('logs')
|
|
6
|
+
.description('View dev server logs')
|
|
7
|
+
.option('-n, --lines <number>', 'Number of lines to show', '50')
|
|
8
|
+
.option('-f, --follow', 'Follow log output (like tail -f)')
|
|
9
|
+
.option('--clear', 'Clear the log file')
|
|
10
|
+
.action(async (options) => {
|
|
11
|
+
if (options.clear) {
|
|
12
|
+
DevProcessManager.clearLogs();
|
|
13
|
+
console.log(chalk.green('✓ Logs cleared'));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const logFile = DevProcessManager.getLogFile();
|
|
17
|
+
const lines = DevProcessManager.readLogs(parseInt(options.lines));
|
|
18
|
+
if (lines.length === 0) {
|
|
19
|
+
console.log(chalk.yellow('No logs available.'));
|
|
20
|
+
console.log(chalk.gray('Log file:'), logFile);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
console.log(chalk.bold('Dev Server Logs'));
|
|
24
|
+
console.log(chalk.gray(`(Last ${lines.length} lines from ${logFile})`));
|
|
25
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
26
|
+
console.log('');
|
|
27
|
+
lines.forEach((line) => console.log(line));
|
|
28
|
+
if (options.follow) {
|
|
29
|
+
console.log('');
|
|
30
|
+
console.log(chalk.gray('Following logs... Press Ctrl+C to stop'));
|
|
31
|
+
let lastSize = fs.statSync(logFile).size;
|
|
32
|
+
fs.watchFile(logFile, { interval: 500 }, () => {
|
|
33
|
+
const newSize = fs.statSync(logFile).size;
|
|
34
|
+
if (newSize > lastSize) {
|
|
35
|
+
const fd = fs.openSync(logFile, 'r');
|
|
36
|
+
const buffer = Buffer.alloc(newSize - lastSize);
|
|
37
|
+
fs.readSync(fd, buffer, 0, buffer.length, lastSize);
|
|
38
|
+
fs.closeSync(fd);
|
|
39
|
+
process.stdout.write(buffer.toString());
|
|
40
|
+
lastSize = newSize;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
await new Promise(() => { });
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
export default logsCommand;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { DevProcessManager } from '../../services/dev/process-manager.js';
|
|
4
|
+
const statusCommand = new Command('status')
|
|
5
|
+
.description('Check if dev server is running in background')
|
|
6
|
+
.action(async () => {
|
|
7
|
+
const state = DevProcessManager.loadState();
|
|
8
|
+
if (!state) {
|
|
9
|
+
console.log(chalk.yellow('No dev server is running in background.'));
|
|
10
|
+
console.log(chalk.gray('Start one with: paymongo dev --detach'));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const isRunning = DevProcessManager.isProcessRunning(state.pid);
|
|
14
|
+
if (!isRunning) {
|
|
15
|
+
console.log(chalk.yellow('Dev server process is not running (stale state).'));
|
|
16
|
+
DevProcessManager.clearState();
|
|
17
|
+
console.log(chalk.gray('Start a new one with: paymongo dev --detach'));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
console.log(chalk.green('✓ Dev server is running'));
|
|
21
|
+
console.log('');
|
|
22
|
+
console.log(chalk.bold('Process:'));
|
|
23
|
+
console.log(chalk.gray(' PID:'), state.pid);
|
|
24
|
+
console.log(chalk.gray(' Uptime:'), DevProcessManager.formatUptime(state.startedAt));
|
|
25
|
+
console.log(chalk.gray(' Project:'), state.projectName);
|
|
26
|
+
console.log('');
|
|
27
|
+
console.log(chalk.bold('URLs:'));
|
|
28
|
+
console.log(chalk.gray(' External:'), chalk.yellow(state.webhookUrl));
|
|
29
|
+
console.log(chalk.gray(' Local:'), chalk.green(state.localUrl));
|
|
30
|
+
console.log('');
|
|
31
|
+
console.log(chalk.bold('Configuration:'));
|
|
32
|
+
console.log(chalk.gray(' Port:'), state.port);
|
|
33
|
+
console.log(chalk.gray(' Events:'), state.events.join(', '));
|
|
34
|
+
if (state.webhookId) {
|
|
35
|
+
console.log(chalk.gray(' Webhook ID:'), state.webhookId);
|
|
36
|
+
}
|
|
37
|
+
console.log('');
|
|
38
|
+
console.log(chalk.gray('Use "paymongo dev stop" to stop the server'));
|
|
39
|
+
console.log(chalk.gray('Use "paymongo dev logs" to view server logs'));
|
|
40
|
+
});
|
|
41
|
+
export default statusCommand;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import Spinner from '../../utils/spinner.js';
|
|
4
|
+
import { DevProcessManager } from '../../services/dev/process-manager.js';
|
|
5
|
+
const stopCommand = new Command('stop')
|
|
6
|
+
.description('Stop the background dev server')
|
|
7
|
+
.action(async () => {
|
|
8
|
+
const spinner = new Spinner();
|
|
9
|
+
const state = DevProcessManager.loadState();
|
|
10
|
+
if (!state) {
|
|
11
|
+
console.log(chalk.yellow('No dev server is running in background.'));
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const isRunning = DevProcessManager.isProcessRunning(state.pid);
|
|
15
|
+
if (!isRunning) {
|
|
16
|
+
console.log(chalk.yellow('Dev server process is not running (cleaning up stale state).'));
|
|
17
|
+
DevProcessManager.clearState();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
spinner.start('Stopping dev server...');
|
|
21
|
+
const killed = DevProcessManager.killProcess(state.pid);
|
|
22
|
+
if (killed) {
|
|
23
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
24
|
+
DevProcessManager.clearState();
|
|
25
|
+
spinner.succeed('Dev server stopped');
|
|
26
|
+
console.log('');
|
|
27
|
+
console.log(chalk.gray('Note: If the webhook was not cleaned up, it will be removed on next "paymongo dev" start.'));
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
spinner.fail('Failed to stop dev server');
|
|
31
|
+
console.log(chalk.yellow('Try manually killing the process:'));
|
|
32
|
+
console.log(chalk.gray(` PID: ${state.pid}`));
|
|
33
|
+
if (process.platform === 'win32') {
|
|
34
|
+
console.log(chalk.gray(` Run: taskkill /pid ${state.pid} /f`));
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
console.log(chalk.gray(` Run: kill -9 ${state.pid}`));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
export default stopCommand;
|
package/dist/commands/dev.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import * as http from 'http';
|
|
3
|
-
import * as crypto from 'crypto';
|
|
4
2
|
import * as fs from 'fs';
|
|
5
3
|
import { spawn } from 'child_process';
|
|
6
4
|
import chalk from 'chalk';
|
|
@@ -9,169 +7,10 @@ import ApiClient from '../services/api/client.js';
|
|
|
9
7
|
import Spinner from '../utils/spinner.js';
|
|
10
8
|
import { withRetry } from '../utils/errors.js';
|
|
11
9
|
import { DevProcessManager } from '../services/dev/process-manager.js';
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
config;
|
|
17
|
-
analytics;
|
|
18
|
-
constructor(port, config) {
|
|
19
|
-
this.port = port;
|
|
20
|
-
this.config = config;
|
|
21
|
-
this.analytics = new AnalyticsService(config);
|
|
22
|
-
this.server = http.createServer((req, res) => {
|
|
23
|
-
this.handleWebhookRequest(req, res);
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
async start() {
|
|
27
|
-
return new Promise((resolve, reject) => {
|
|
28
|
-
this.server.listen(this.port, () => {
|
|
29
|
-
console.log(chalk.green('✓'), `Webhook server listening on http://localhost:${this.port}`);
|
|
30
|
-
resolve();
|
|
31
|
-
});
|
|
32
|
-
this.server.on('error', (error) => {
|
|
33
|
-
reject(new Error(`Failed to start server on port ${this.port}: ${error.message}`));
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
async stop() {
|
|
38
|
-
return new Promise((resolve) => {
|
|
39
|
-
this.server.close(() => {
|
|
40
|
-
console.log(chalk.yellow('✓'), 'Webhook server stopped');
|
|
41
|
-
resolve();
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
handleWebhookRequest(req, res) {
|
|
46
|
-
const isWebhookPath = req.url?.startsWith('/webhook');
|
|
47
|
-
if (req.method !== 'POST' || !isWebhookPath) {
|
|
48
|
-
res.writeHead(404);
|
|
49
|
-
res.end('Not Found');
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
let body = '';
|
|
53
|
-
req.on('data', (chunk) => {
|
|
54
|
-
body += chunk.toString();
|
|
55
|
-
});
|
|
56
|
-
req.on('end', () => {
|
|
57
|
-
try {
|
|
58
|
-
const event = JSON.parse(body);
|
|
59
|
-
const signatureValid = this.verifyWebhookSignature(req, body);
|
|
60
|
-
if (!signatureValid) {
|
|
61
|
-
console.log(chalk.red('⚠️'), 'Webhook signature verification failed');
|
|
62
|
-
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
63
|
-
res.end(JSON.stringify({ error: 'Invalid signature' }));
|
|
64
|
-
this.analytics.recordEvent({
|
|
65
|
-
type: event.data?.type || 'unknown',
|
|
66
|
-
success: false,
|
|
67
|
-
error: 'Invalid signature',
|
|
68
|
-
data: event.data?.attributes,
|
|
69
|
-
});
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
this.logWebhookEvent(event);
|
|
73
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
74
|
-
res.end(JSON.stringify({ success: true }));
|
|
75
|
-
}
|
|
76
|
-
catch (error) {
|
|
77
|
-
console.error(chalk.red('✗'), 'Failed to process webhook:', error.message);
|
|
78
|
-
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
79
|
-
res.end(JSON.stringify({ error: 'Invalid JSON' }));
|
|
80
|
-
this.analytics.recordEvent({
|
|
81
|
-
type: 'unknown',
|
|
82
|
-
success: false,
|
|
83
|
-
error: 'Invalid JSON',
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
logWebhookEvent(event) {
|
|
89
|
-
const timestamp = new Date().toLocaleTimeString();
|
|
90
|
-
const eventType = event.data?.type || 'unknown';
|
|
91
|
-
const eventId = event.data?.id || 'unknown';
|
|
92
|
-
this.analytics.recordEvent({
|
|
93
|
-
type: eventType,
|
|
94
|
-
success: true,
|
|
95
|
-
data: event.data?.attributes,
|
|
96
|
-
});
|
|
97
|
-
console.log('');
|
|
98
|
-
console.log(chalk.gray('────────────────────────────────────────────────────────────'));
|
|
99
|
-
console.log(chalk.blue(`[${timestamp}]`), chalk.bold(eventType.toUpperCase()));
|
|
100
|
-
if (eventType === 'payment') {
|
|
101
|
-
const attributes = event.data.attributes;
|
|
102
|
-
const amount = attributes.amount ?? 0;
|
|
103
|
-
const status = attributes.status ?? 'unknown';
|
|
104
|
-
console.log(chalk.gray('└─'), `Amount: ₱${(amount / 100).toFixed(2)}`);
|
|
105
|
-
console.log(chalk.gray('└─'), `Status: ${status}`);
|
|
106
|
-
console.log(chalk.gray('└─'), `Payment ID: ${eventId}`);
|
|
107
|
-
}
|
|
108
|
-
console.log(chalk.gray('└─'), `View: https://dashboard.paymongo.com/${eventType === 'payment' ? 'payments' : 'webhooks'}/${eventId}`);
|
|
109
|
-
}
|
|
110
|
-
verifyWebhookSignature(req, body) {
|
|
111
|
-
if (!this.config.dev.verifyWebhookSignatures) {
|
|
112
|
-
console.log(chalk.yellow('ℹ️'), 'Webhook signature verification disabled in config');
|
|
113
|
-
return true;
|
|
114
|
-
}
|
|
115
|
-
const signatureHeader = req.headers['paymongo-signature'];
|
|
116
|
-
if (!signatureHeader) {
|
|
117
|
-
console.log(chalk.red('⚠️'), 'Signature verification required but no signature header found');
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
const signatureParts = signatureHeader.split(',');
|
|
121
|
-
if (signatureParts.length < 2) {
|
|
122
|
-
console.log(chalk.red('⚠️'), 'Invalid signature format');
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
const timestamp = signatureParts.find((part) => part.startsWith('t='))?.split('=')[1];
|
|
126
|
-
const signature = signatureParts.find((part) => part.startsWith('te='))?.split('=')[1];
|
|
127
|
-
if (!timestamp || !signature) {
|
|
128
|
-
console.log(chalk.red('⚠️'), 'Missing timestamp or signature in header');
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
const webhookId = signatureParts.find((part) => part.startsWith('li='))?.split('=')[1];
|
|
132
|
-
const webhookSecrets = this.config.webhookSecrets || {};
|
|
133
|
-
const configuredSecret = webhookId ? webhookSecrets[webhookId] : undefined;
|
|
134
|
-
let secretKeys = [];
|
|
135
|
-
if (configuredSecret) {
|
|
136
|
-
secretKeys = [configuredSecret];
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
secretKeys = Object.values(webhookSecrets).filter((secret) => typeof secret === 'string' && secret.length > 0);
|
|
140
|
-
if (webhookId && secretKeys.length > 0) {
|
|
141
|
-
console.log(chalk.yellow('⚠️'), `No webhook secret found for id ${webhookId}. Update your configuration.`);
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
if (secretKeys.length === 0) {
|
|
146
|
-
console.log(chalk.yellow('⚠️'), 'Signature verification enabled but no webhook secrets configured');
|
|
147
|
-
return true;
|
|
148
|
-
}
|
|
149
|
-
let isValid = false;
|
|
150
|
-
for (const secret of secretKeys) {
|
|
151
|
-
try {
|
|
152
|
-
const expectedSignature = crypto
|
|
153
|
-
.createHmac('sha256', secret)
|
|
154
|
-
.update(`${timestamp}.${body}`)
|
|
155
|
-
.digest('hex');
|
|
156
|
-
if (crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expectedSignature, 'hex'))) {
|
|
157
|
-
isValid = true;
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
catch (_error) {
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
if (isValid) {
|
|
166
|
-
console.log(chalk.green('✓'), 'Signature verified successfully');
|
|
167
|
-
return true;
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
console.log(chalk.red('✗'), 'Signature verification failed');
|
|
171
|
-
return false;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
10
|
+
import { DevServer } from '../services/dev/server.js';
|
|
11
|
+
import statusCommand from './dev/status.js';
|
|
12
|
+
import stopCommand from './dev/stop.js';
|
|
13
|
+
import logsCommand from './dev/logs.js';
|
|
175
14
|
const command = new Command('dev');
|
|
176
15
|
command
|
|
177
16
|
.description('Start local development server')
|
|
@@ -426,121 +265,7 @@ command
|
|
|
426
265
|
process.exit(1);
|
|
427
266
|
}
|
|
428
267
|
});
|
|
429
|
-
command
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
.action(async () => {
|
|
433
|
-
const state = DevProcessManager.loadState();
|
|
434
|
-
if (!state) {
|
|
435
|
-
console.log(chalk.yellow('No dev server is running in background.'));
|
|
436
|
-
console.log(chalk.gray('Start one with: paymongo dev --detach'));
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
const isRunning = DevProcessManager.isProcessRunning(state.pid);
|
|
440
|
-
if (!isRunning) {
|
|
441
|
-
console.log(chalk.yellow('Dev server process is not running (stale state).'));
|
|
442
|
-
DevProcessManager.clearState();
|
|
443
|
-
console.log(chalk.gray('Start a new one with: paymongo dev --detach'));
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
console.log(chalk.green('✓ Dev server is running'));
|
|
447
|
-
console.log('');
|
|
448
|
-
console.log(chalk.bold('Process:'));
|
|
449
|
-
console.log(chalk.gray(' PID:'), state.pid);
|
|
450
|
-
console.log(chalk.gray(' Uptime:'), DevProcessManager.formatUptime(state.startedAt));
|
|
451
|
-
console.log(chalk.gray(' Project:'), state.projectName);
|
|
452
|
-
console.log('');
|
|
453
|
-
console.log(chalk.bold('URLs:'));
|
|
454
|
-
console.log(chalk.gray(' External:'), chalk.yellow(state.webhookUrl));
|
|
455
|
-
console.log(chalk.gray(' Local:'), chalk.green(state.localUrl));
|
|
456
|
-
console.log('');
|
|
457
|
-
console.log(chalk.bold('Configuration:'));
|
|
458
|
-
console.log(chalk.gray(' Port:'), state.port);
|
|
459
|
-
console.log(chalk.gray(' Events:'), state.events.join(', '));
|
|
460
|
-
if (state.webhookId) {
|
|
461
|
-
console.log(chalk.gray(' Webhook ID:'), state.webhookId);
|
|
462
|
-
}
|
|
463
|
-
console.log('');
|
|
464
|
-
console.log(chalk.gray('Use "paymongo dev stop" to stop the server'));
|
|
465
|
-
console.log(chalk.gray('Use "paymongo dev logs" to view server logs'));
|
|
466
|
-
});
|
|
467
|
-
command
|
|
468
|
-
.command('stop')
|
|
469
|
-
.description('Stop the background dev server')
|
|
470
|
-
.action(async () => {
|
|
471
|
-
const spinner = new Spinner();
|
|
472
|
-
const state = DevProcessManager.loadState();
|
|
473
|
-
if (!state) {
|
|
474
|
-
console.log(chalk.yellow('No dev server is running in background.'));
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
|
-
const isRunning = DevProcessManager.isProcessRunning(state.pid);
|
|
478
|
-
if (!isRunning) {
|
|
479
|
-
console.log(chalk.yellow('Dev server process is not running (cleaning up stale state).'));
|
|
480
|
-
DevProcessManager.clearState();
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
spinner.start('Stopping dev server...');
|
|
484
|
-
const killed = DevProcessManager.killProcess(state.pid);
|
|
485
|
-
if (killed) {
|
|
486
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
487
|
-
DevProcessManager.clearState();
|
|
488
|
-
spinner.succeed('Dev server stopped');
|
|
489
|
-
console.log('');
|
|
490
|
-
console.log(chalk.gray('Note: If the webhook was not cleaned up, it will be removed on next "paymongo dev" start.'));
|
|
491
|
-
}
|
|
492
|
-
else {
|
|
493
|
-
spinner.fail('Failed to stop dev server');
|
|
494
|
-
console.log(chalk.yellow('Try manually killing the process:'));
|
|
495
|
-
console.log(chalk.gray(` PID: ${state.pid}`));
|
|
496
|
-
if (process.platform === 'win32') {
|
|
497
|
-
console.log(chalk.gray(` Run: taskkill /pid ${state.pid} /f`));
|
|
498
|
-
}
|
|
499
|
-
else {
|
|
500
|
-
console.log(chalk.gray(` Run: kill -9 ${state.pid}`));
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
});
|
|
504
|
-
command
|
|
505
|
-
.command('logs')
|
|
506
|
-
.description('View dev server logs')
|
|
507
|
-
.option('-n, --lines <number>', 'Number of lines to show', '50')
|
|
508
|
-
.option('-f, --follow', 'Follow log output (like tail -f)')
|
|
509
|
-
.option('--clear', 'Clear the log file')
|
|
510
|
-
.action(async (options) => {
|
|
511
|
-
if (options.clear) {
|
|
512
|
-
DevProcessManager.clearLogs();
|
|
513
|
-
console.log(chalk.green('✓ Logs cleared'));
|
|
514
|
-
return;
|
|
515
|
-
}
|
|
516
|
-
const logFile = DevProcessManager.getLogFile();
|
|
517
|
-
const lines = DevProcessManager.readLogs(parseInt(options.lines));
|
|
518
|
-
if (lines.length === 0) {
|
|
519
|
-
console.log(chalk.yellow('No logs available.'));
|
|
520
|
-
console.log(chalk.gray('Log file:'), logFile);
|
|
521
|
-
return;
|
|
522
|
-
}
|
|
523
|
-
console.log(chalk.bold('Dev Server Logs'));
|
|
524
|
-
console.log(chalk.gray(`(Last ${lines.length} lines from ${logFile})`));
|
|
525
|
-
console.log(chalk.gray('─'.repeat(60)));
|
|
526
|
-
console.log('');
|
|
527
|
-
lines.forEach((line) => console.log(line));
|
|
528
|
-
if (options.follow) {
|
|
529
|
-
console.log('');
|
|
530
|
-
console.log(chalk.gray('Following logs... Press Ctrl+C to stop'));
|
|
531
|
-
let lastSize = fs.statSync(logFile).size;
|
|
532
|
-
fs.watchFile(logFile, { interval: 500 }, () => {
|
|
533
|
-
const newSize = fs.statSync(logFile).size;
|
|
534
|
-
if (newSize > lastSize) {
|
|
535
|
-
const fd = fs.openSync(logFile, 'r');
|
|
536
|
-
const buffer = Buffer.alloc(newSize - lastSize);
|
|
537
|
-
fs.readSync(fd, buffer, 0, buffer.length, lastSize);
|
|
538
|
-
fs.closeSync(fd);
|
|
539
|
-
process.stdout.write(buffer.toString());
|
|
540
|
-
lastSize = newSize;
|
|
541
|
-
}
|
|
542
|
-
});
|
|
543
|
-
await new Promise(() => { });
|
|
544
|
-
}
|
|
545
|
-
});
|
|
268
|
+
command.addCommand(statusCommand);
|
|
269
|
+
command.addCommand(stopCommand);
|
|
270
|
+
command.addCommand(logsCommand);
|
|
546
271
|
export { command, DevServer };
|
package/dist/commands/env.js
CHANGED
|
@@ -3,6 +3,7 @@ import chalk from 'chalk';
|
|
|
3
3
|
import ConfigManager from '../services/config/manager.js';
|
|
4
4
|
import ApiClient from '../services/api/client.js';
|
|
5
5
|
import Spinner from '../utils/spinner.js';
|
|
6
|
+
import { ApiKeyError, NetworkError, PayMongoError } from '../utils/errors.js';
|
|
6
7
|
const command = new Command('env');
|
|
7
8
|
command
|
|
8
9
|
.description('Manage PayMongo environments')
|
|
@@ -41,17 +42,40 @@ command
|
|
|
41
42
|
spinner.start('Validating API keys...');
|
|
42
43
|
const testConfig = { ...config, environment: environment };
|
|
43
44
|
const apiClient = new ApiClient({ config: testConfig });
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
try {
|
|
46
|
+
await apiClient.validateApiKey();
|
|
47
|
+
spinner.succeed('API keys validated');
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
46
50
|
spinner.fail('API key validation failed');
|
|
47
51
|
console.log('');
|
|
48
52
|
console.log(chalk.red('❌ Invalid API keys for the target environment.'));
|
|
53
|
+
if (error instanceof ApiKeyError) {
|
|
54
|
+
console.log(chalk.gray('The API keys appear to be invalid or expired.'));
|
|
55
|
+
}
|
|
56
|
+
else if (error instanceof NetworkError) {
|
|
57
|
+
console.log(chalk.gray('Network connectivity issue. Please check your internet connection.'));
|
|
58
|
+
}
|
|
59
|
+
else if (error instanceof PayMongoError) {
|
|
60
|
+
if (error.statusCode && error.statusCode >= 500) {
|
|
61
|
+
console.log(chalk.gray('PayMongo API is currently unavailable.'));
|
|
62
|
+
}
|
|
63
|
+
else if (error.statusCode && error.statusCode === 429) {
|
|
64
|
+
console.log(chalk.gray('Too many requests. Please wait a moment.'));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
console.log(chalk.gray(`API error: ${error.message}`));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
console.log(chalk.gray('Unexpected validation error.'));
|
|
72
|
+
}
|
|
73
|
+
console.log('');
|
|
49
74
|
console.log(chalk.gray('Use --force to skip validation, but note that commands may fail.'));
|
|
50
75
|
console.log('');
|
|
51
76
|
console.log(chalk.yellow('💡 Get your API keys from: https://dashboard.paymongo.com/developers'));
|
|
52
77
|
process.exit(1);
|
|
53
78
|
}
|
|
54
|
-
spinner.succeed('API keys validated');
|
|
55
79
|
}
|
|
56
80
|
spinner.start(`Switching to ${environment} environment...`);
|
|
57
81
|
const updatedConfig = { ...config, environment: environment };
|