@vibe-db/cli 1.3.0 ā 1.4.0
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/CHANGELOG.md +63 -0
- package/README.md +141 -1
- package/bin/vibedb.js +71 -0
- package/package.json +1 -1
- package/src/api.js +95 -0
- package/src/commands/backup-create.js +48 -0
- package/src/commands/backup-delete.js +64 -0
- package/src/commands/backup-list.js +67 -0
- package/src/commands/backup-restore.js +73 -0
- package/src/commands/usage.js +66 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the VibeDB CLI will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [1.4.0] - 2026-01-12
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Usage Command**: New `vibedb usage` command to view storage usage and estimated costs
|
|
9
|
+
- Shows database storage and backup storage breakdown
|
|
10
|
+
- Displays estimated charges for current billing period
|
|
11
|
+
- Shows pricing information
|
|
12
|
+
- **Backup Management Commands**: Complete CLI support for backup operations
|
|
13
|
+
- `vibedb backup list <database_id>` - List all backups for a database
|
|
14
|
+
- `vibedb backup create <database_id>` - Create a manual backup
|
|
15
|
+
- `vibedb backup restore <database_id> <backup_id>` - Restore from backup (with confirmation)
|
|
16
|
+
- `vibedb backup delete <database_id> <backup_id>` - Delete a backup (with confirmation)
|
|
17
|
+
- Added safety confirmations for destructive operations (restore, delete)
|
|
18
|
+
- Improved error messages for missing authentication
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- Enhanced API module with backup-related functions
|
|
22
|
+
- Updated README with comprehensive documentation for new commands
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- Fixed handling of undefined estimated charges in usage display
|
|
26
|
+
|
|
27
|
+
## [1.3.0] - 2026-01-11
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
- **Team Support**: Multi-team functionality
|
|
31
|
+
- `vibedb teams list` - List all teams
|
|
32
|
+
- `vibedb teams switch <team_id>` - Switch between teams
|
|
33
|
+
- `vibedb teams current` - Show current team
|
|
34
|
+
- Auto-selects personal team on login
|
|
35
|
+
- **Billing Commands**: Stripe billing integration
|
|
36
|
+
- `vibedb billing info` - View billing information
|
|
37
|
+
- `vibedb billing subscribe` - Subscribe to VibeDB
|
|
38
|
+
- `vibedb billing cancel` - Cancel subscription
|
|
39
|
+
- `vibedb billing invoices` - View invoices
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
- Database operations now scoped to current team
|
|
43
|
+
- Config file now stores current team ID
|
|
44
|
+
|
|
45
|
+
## [1.2.0] - Earlier
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
- Basic database operations
|
|
49
|
+
- `vibedb list` - List databases
|
|
50
|
+
- `vibedb init` - Download VIBEDB.md prompt file
|
|
51
|
+
- Authentication commands
|
|
52
|
+
- `vibedb signup` - Create account
|
|
53
|
+
- `vibedb login` - Login to existing account
|
|
54
|
+
- `vibedb logout` - Logout and clear credentials
|
|
55
|
+
- Configuration management in `~/.vibedb`
|
|
56
|
+
|
|
57
|
+
## Future Plans
|
|
58
|
+
|
|
59
|
+
- Database creation directly from CLI
|
|
60
|
+
- Database deletion with recovery options
|
|
61
|
+
- Connection testing
|
|
62
|
+
- Query execution from CLI
|
|
63
|
+
- Import/export utilities
|
package/README.md
CHANGED
|
@@ -136,6 +136,141 @@ Show currently selected team.
|
|
|
136
136
|
**Output:**
|
|
137
137
|
- Current team details (ID, name, role, type)
|
|
138
138
|
|
|
139
|
+
### `vibedb usage`
|
|
140
|
+
Show current usage and costs.
|
|
141
|
+
|
|
142
|
+
**Requirements:**
|
|
143
|
+
- Must be logged in
|
|
144
|
+
|
|
145
|
+
**Output:**
|
|
146
|
+
- Storage breakdown (database + backup storage)
|
|
147
|
+
- Estimated charges for current period
|
|
148
|
+
- Pricing information
|
|
149
|
+
|
|
150
|
+
**Example:**
|
|
151
|
+
```bash
|
|
152
|
+
vibedb usage
|
|
153
|
+
|
|
154
|
+
š Usage & Costs
|
|
155
|
+
|
|
156
|
+
Storage Usage:
|
|
157
|
+
Database Storage: 45.23 MB
|
|
158
|
+
Backup Storage: 12.67 MB
|
|
159
|
+
āāāāāāāāāāāāāāāāā
|
|
160
|
+
Total Storage: 57.90 MB
|
|
161
|
+
|
|
162
|
+
Estimated Charges:
|
|
163
|
+
Current Period: $0.0823 USD
|
|
164
|
+
|
|
165
|
+
Pricing:
|
|
166
|
+
Storage: $0.00014 per GB-hour
|
|
167
|
+
Compute: $0.000167 per minute
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### `vibedb backup list <database_id>`
|
|
171
|
+
List all backups for a database.
|
|
172
|
+
|
|
173
|
+
**Requirements:**
|
|
174
|
+
- Must be logged in
|
|
175
|
+
- Valid database ID
|
|
176
|
+
|
|
177
|
+
**Output:**
|
|
178
|
+
- List of backups with ID, type (auto/manual), size, and creation date
|
|
179
|
+
|
|
180
|
+
**Example:**
|
|
181
|
+
```bash
|
|
182
|
+
vibedb backup list db_abc123
|
|
183
|
+
|
|
184
|
+
š¾ Backups
|
|
185
|
+
|
|
186
|
+
Found 3 backup(s):
|
|
187
|
+
|
|
188
|
+
1. d4339369-a3bb-492c-afe3-2124265e9b56
|
|
189
|
+
Type: [Manual]
|
|
190
|
+
Size: 12.34 MB
|
|
191
|
+
Created: 1/12/2026, 8:30:06 PM
|
|
192
|
+
|
|
193
|
+
2. a1234567-b8cd-9012-efgh-345678901234
|
|
194
|
+
Type: [Auto]
|
|
195
|
+
Size: 11.98 MB
|
|
196
|
+
Created: 1/12/2026, 2:00:00 AM
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### `vibedb backup create <database_id>`
|
|
200
|
+
Create a manual backup.
|
|
201
|
+
|
|
202
|
+
**Requirements:**
|
|
203
|
+
- Must be logged in
|
|
204
|
+
- Valid database ID
|
|
205
|
+
|
|
206
|
+
**Output:**
|
|
207
|
+
- Backup ID and creation timestamp
|
|
208
|
+
|
|
209
|
+
**Example:**
|
|
210
|
+
```bash
|
|
211
|
+
vibedb backup create db_abc123
|
|
212
|
+
|
|
213
|
+
ā Backup created successfully!
|
|
214
|
+
|
|
215
|
+
Backup ID: d4339369-a3bb-492c-afe3-2124265e9b56
|
|
216
|
+
Type: [Manual]
|
|
217
|
+
Created: 1/12/2026, 8:30:06 PM
|
|
218
|
+
Size: 12.34 MB
|
|
219
|
+
|
|
220
|
+
Restore this backup with:
|
|
221
|
+
vibedb backup restore db_abc123 d4339369-a3bb-492c-afe3-2124265e9b56
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### `vibedb backup restore <database_id> <backup_id>`
|
|
225
|
+
Restore a database from a backup.
|
|
226
|
+
|
|
227
|
+
**Requirements:**
|
|
228
|
+
- Must be logged in
|
|
229
|
+
- Valid database ID and backup ID
|
|
230
|
+
- Confirmation required (interactive)
|
|
231
|
+
|
|
232
|
+
**Warning:** This will replace ALL current data in the database!
|
|
233
|
+
|
|
234
|
+
**Example:**
|
|
235
|
+
```bash
|
|
236
|
+
vibedb backup restore db_abc123 d4339369-a3bb-492c-afe3-2124265e9b56
|
|
237
|
+
|
|
238
|
+
š Restore Backup
|
|
239
|
+
|
|
240
|
+
ā ļø WARNING:
|
|
241
|
+
This will replace ALL current data in the database with the backup.
|
|
242
|
+
This action cannot be undone.
|
|
243
|
+
|
|
244
|
+
Database ID: db_abc123
|
|
245
|
+
Backup ID: d4339369-a3bb-492c-afe3-2124265e9b56
|
|
246
|
+
|
|
247
|
+
Are you sure you want to restore this backup? (y/N): y
|
|
248
|
+
|
|
249
|
+
ā Backup restored successfully!
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### `vibedb backup delete <database_id> <backup_id>`
|
|
253
|
+
Delete a backup.
|
|
254
|
+
|
|
255
|
+
**Requirements:**
|
|
256
|
+
- Must be logged in
|
|
257
|
+
- Valid database ID and backup ID
|
|
258
|
+
- Confirmation required (interactive)
|
|
259
|
+
|
|
260
|
+
**Example:**
|
|
261
|
+
```bash
|
|
262
|
+
vibedb backup delete db_abc123 d4339369-a3bb-492c-afe3-2124265e9b56
|
|
263
|
+
|
|
264
|
+
šļø Delete Backup
|
|
265
|
+
|
|
266
|
+
Database ID: db_abc123
|
|
267
|
+
Backup ID: d4339369-a3bb-492c-afe3-2124265e9b56
|
|
268
|
+
|
|
269
|
+
Are you sure you want to delete this backup? (y/N): y
|
|
270
|
+
|
|
271
|
+
ā Backup deleted successfully!
|
|
272
|
+
```
|
|
273
|
+
|
|
139
274
|
## Configuration
|
|
140
275
|
|
|
141
276
|
The CLI stores your credentials in `~/.vibedb`:
|
|
@@ -274,7 +409,12 @@ The CLI interacts with these VibeDB API endpoints:
|
|
|
274
409
|
- `POST /v1/auth/login` - Authenticate
|
|
275
410
|
- `GET /v1/databases` - List databases
|
|
276
411
|
- `GET /v1/prompt-file` - Download prompt file
|
|
277
|
-
- `GET /v1/account` - Get account info
|
|
412
|
+
- `GET /v1/account` - Get account info and usage
|
|
413
|
+
- `GET /v1/teams` - List teams
|
|
414
|
+
- `GET /v1/databases/{id}/backups` - List backups
|
|
415
|
+
- `POST /v1/databases/{id}/backups` - Create backup
|
|
416
|
+
- `POST /v1/databases/{id}/backups/{backup_id}/restore` - Restore backup
|
|
417
|
+
- `DELETE /v1/databases/{id}/backups/{backup_id}` - Delete backup
|
|
278
418
|
|
|
279
419
|
Full API docs: https://api.vibedb.dev/docs
|
|
280
420
|
|
package/bin/vibedb.js
CHANGED
|
@@ -15,6 +15,11 @@ const billingInvoicesCommand = require('../src/commands/billing-invoices');
|
|
|
15
15
|
const teamsListCommand = require('../src/commands/teams-list');
|
|
16
16
|
const teamsSwitchCommand = require('../src/commands/teams-switch');
|
|
17
17
|
const teamsCurrentCommand = require('../src/commands/teams-current');
|
|
18
|
+
const usageCommand = require('../src/commands/usage');
|
|
19
|
+
const backupListCommand = require('../src/commands/backup-list');
|
|
20
|
+
const backupCreateCommand = require('../src/commands/backup-create');
|
|
21
|
+
const backupRestoreCommand = require('../src/commands/backup-restore');
|
|
22
|
+
const backupDeleteCommand = require('../src/commands/backup-delete');
|
|
18
23
|
|
|
19
24
|
const program = new Command();
|
|
20
25
|
|
|
@@ -179,6 +184,72 @@ teams
|
|
|
179
184
|
}
|
|
180
185
|
});
|
|
181
186
|
|
|
187
|
+
// Usage command
|
|
188
|
+
program
|
|
189
|
+
.command('usage')
|
|
190
|
+
.description('Show current usage and costs')
|
|
191
|
+
.action(async () => {
|
|
192
|
+
try {
|
|
193
|
+
await usageCommand();
|
|
194
|
+
} catch (error) {
|
|
195
|
+
console.error(chalk.red('Unexpected error:'), error.message);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Backup commands
|
|
201
|
+
const backup = program
|
|
202
|
+
.command('backup')
|
|
203
|
+
.description('Manage database backups');
|
|
204
|
+
|
|
205
|
+
backup
|
|
206
|
+
.command('list <database_id>')
|
|
207
|
+
.description('List all backups for a database')
|
|
208
|
+
.action(async (databaseId) => {
|
|
209
|
+
try {
|
|
210
|
+
await backupListCommand(databaseId);
|
|
211
|
+
} catch (error) {
|
|
212
|
+
console.error(chalk.red('Unexpected error:'), error.message);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
backup
|
|
218
|
+
.command('create <database_id>')
|
|
219
|
+
.description('Create a manual backup')
|
|
220
|
+
.action(async (databaseId) => {
|
|
221
|
+
try {
|
|
222
|
+
await backupCreateCommand(databaseId);
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.error(chalk.red('Unexpected error:'), error.message);
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
backup
|
|
230
|
+
.command('restore <database_id> <backup_id>')
|
|
231
|
+
.description('Restore a database from a backup')
|
|
232
|
+
.action(async (databaseId, backupId) => {
|
|
233
|
+
try {
|
|
234
|
+
await backupRestoreCommand(databaseId, backupId);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error(chalk.red('Unexpected error:'), error.message);
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
backup
|
|
242
|
+
.command('delete <database_id> <backup_id>')
|
|
243
|
+
.description('Delete a backup')
|
|
244
|
+
.action(async (databaseId, backupId) => {
|
|
245
|
+
try {
|
|
246
|
+
await backupDeleteCommand(databaseId, backupId);
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error(chalk.red('Unexpected error:'), error.message);
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
182
253
|
// Global error handler
|
|
183
254
|
process.on('unhandledRejection', (error) => {
|
|
184
255
|
console.error(chalk.red.bold('\nā Unhandled error:'), error.message);
|
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -263,6 +263,97 @@ async function getTeam(apiKey, teamId) {
|
|
|
263
263
|
}
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
+
/**
|
|
267
|
+
* List backups for a database
|
|
268
|
+
*/
|
|
269
|
+
async function listBackups(apiKey, databaseId) {
|
|
270
|
+
try {
|
|
271
|
+
const response = await axios.get(`${API_BASE_URL}/v1/databases/${databaseId}/backups`, {
|
|
272
|
+
headers: {
|
|
273
|
+
Authorization: `Bearer ${apiKey}`,
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
return response.data.backups || [];
|
|
277
|
+
} catch (error) {
|
|
278
|
+
if (error.response) {
|
|
279
|
+
const { error: errorCode, message } = error.response.data;
|
|
280
|
+
throw new Error(message || errorCode || 'Failed to list backups');
|
|
281
|
+
}
|
|
282
|
+
throw new Error(`Network error: ${error.message}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Create a backup for a database
|
|
288
|
+
*/
|
|
289
|
+
async function createBackup(apiKey, databaseId) {
|
|
290
|
+
try {
|
|
291
|
+
const response = await axios.post(
|
|
292
|
+
`${API_BASE_URL}/v1/databases/${databaseId}/backups`,
|
|
293
|
+
{},
|
|
294
|
+
{
|
|
295
|
+
headers: {
|
|
296
|
+
Authorization: `Bearer ${apiKey}`,
|
|
297
|
+
},
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
return response.data;
|
|
301
|
+
} catch (error) {
|
|
302
|
+
if (error.response) {
|
|
303
|
+
const { error: errorCode, message } = error.response.data;
|
|
304
|
+
throw new Error(message || errorCode || 'Failed to create backup');
|
|
305
|
+
}
|
|
306
|
+
throw new Error(`Network error: ${error.message}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Restore a database from a backup
|
|
312
|
+
*/
|
|
313
|
+
async function restoreBackup(apiKey, databaseId, backupId) {
|
|
314
|
+
try {
|
|
315
|
+
const response = await axios.post(
|
|
316
|
+
`${API_BASE_URL}/v1/databases/${databaseId}/backups/${backupId}/restore`,
|
|
317
|
+
{},
|
|
318
|
+
{
|
|
319
|
+
headers: {
|
|
320
|
+
Authorization: `Bearer ${apiKey}`,
|
|
321
|
+
},
|
|
322
|
+
}
|
|
323
|
+
);
|
|
324
|
+
return response.data;
|
|
325
|
+
} catch (error) {
|
|
326
|
+
if (error.response) {
|
|
327
|
+
const { error: errorCode, message } = error.response.data;
|
|
328
|
+
throw new Error(message || errorCode || 'Failed to restore backup');
|
|
329
|
+
}
|
|
330
|
+
throw new Error(`Network error: ${error.message}`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Delete a backup
|
|
336
|
+
*/
|
|
337
|
+
async function deleteBackup(apiKey, databaseId, backupId) {
|
|
338
|
+
try {
|
|
339
|
+
const response = await axios.delete(
|
|
340
|
+
`${API_BASE_URL}/v1/databases/${databaseId}/backups/${backupId}`,
|
|
341
|
+
{
|
|
342
|
+
headers: {
|
|
343
|
+
Authorization: `Bearer ${apiKey}`,
|
|
344
|
+
},
|
|
345
|
+
}
|
|
346
|
+
);
|
|
347
|
+
return response.data;
|
|
348
|
+
} catch (error) {
|
|
349
|
+
if (error.response) {
|
|
350
|
+
const { error: errorCode, message } = error.response.data;
|
|
351
|
+
throw new Error(message || errorCode || 'Failed to delete backup');
|
|
352
|
+
}
|
|
353
|
+
throw new Error(`Network error: ${error.message}`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
266
357
|
module.exports = {
|
|
267
358
|
signup,
|
|
268
359
|
login,
|
|
@@ -277,4 +368,8 @@ module.exports = {
|
|
|
277
368
|
pollDeviceAuth,
|
|
278
369
|
listTeams,
|
|
279
370
|
getTeam,
|
|
371
|
+
listBackups,
|
|
372
|
+
createBackup,
|
|
373
|
+
restoreBackup,
|
|
374
|
+
deleteBackup,
|
|
280
375
|
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const api = require('../api');
|
|
3
|
+
const config = require('../config');
|
|
4
|
+
|
|
5
|
+
async function backupCreateCommand(databaseId) {
|
|
6
|
+
console.log(chalk.blue.bold('\nš¾ Create Backup\n'));
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
// Check if logged in
|
|
10
|
+
if (!config.isLoggedIn()) {
|
|
11
|
+
console.error(chalk.red.bold('ā Not logged in'));
|
|
12
|
+
console.log(chalk.gray('\nPlease run'), chalk.cyan('vibedb login'), chalk.gray('or'), chalk.cyan('vibedb signup'), chalk.gray('first.'));
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!databaseId) {
|
|
17
|
+
console.error(chalk.red.bold('ā Database ID is required'));
|
|
18
|
+
console.log(chalk.gray('\nUsage:'), chalk.cyan('vibedb backup create <database-id>'));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const apiKey = config.getAPIKey();
|
|
23
|
+
|
|
24
|
+
console.log(chalk.gray(`Creating backup for database ${databaseId}...`));
|
|
25
|
+
|
|
26
|
+
const backup = await api.createBackup(apiKey, databaseId);
|
|
27
|
+
|
|
28
|
+
console.log(chalk.green.bold('\nā Backup created successfully!\n'));
|
|
29
|
+
console.log(chalk.gray('Backup ID:'), chalk.cyan(backup.id));
|
|
30
|
+
console.log(chalk.gray('Type:'), chalk.green('[Manual]'));
|
|
31
|
+
console.log(chalk.gray('Created:'), chalk.white(new Date(backup.created_at).toLocaleString()));
|
|
32
|
+
|
|
33
|
+
if (backup.size_mb) {
|
|
34
|
+
console.log(chalk.gray('Size:'), chalk.cyan(`${backup.size_mb.toFixed(2)} MB`));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log();
|
|
38
|
+
console.log(chalk.gray('Restore this backup with:'));
|
|
39
|
+
console.log(chalk.cyan(`vibedb backup restore ${databaseId} ${backup.id}`));
|
|
40
|
+
console.log();
|
|
41
|
+
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error(chalk.red.bold('\nā Failed to create backup:'), chalk.red(error.message));
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = backupCreateCommand;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const readline = require('readline');
|
|
3
|
+
const api = require('../api');
|
|
4
|
+
const config = require('../config');
|
|
5
|
+
|
|
6
|
+
async function askConfirmation(question) {
|
|
7
|
+
const rl = readline.createInterface({
|
|
8
|
+
input: process.stdin,
|
|
9
|
+
output: process.stdout,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
rl.question(question, (answer) => {
|
|
14
|
+
rl.close();
|
|
15
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function backupDeleteCommand(databaseId, backupId) {
|
|
21
|
+
console.log(chalk.blue.bold('\nšļø Delete Backup\n'));
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
// Check if logged in
|
|
25
|
+
if (!config.isLoggedIn()) {
|
|
26
|
+
console.error(chalk.red.bold('ā Not logged in'));
|
|
27
|
+
console.log(chalk.gray('\nPlease run'), chalk.cyan('vibedb login'), chalk.gray('or'), chalk.cyan('vibedb signup'), chalk.gray('first.'));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!databaseId || !backupId) {
|
|
32
|
+
console.error(chalk.red.bold('ā Database ID and Backup ID are required'));
|
|
33
|
+
console.log(chalk.gray('\nUsage:'), chalk.cyan('vibedb backup delete <database-id> <backup-id>'));
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const apiKey = config.getAPIKey();
|
|
38
|
+
|
|
39
|
+
console.log(chalk.gray('Database ID:'), chalk.cyan(databaseId));
|
|
40
|
+
console.log(chalk.gray('Backup ID:'), chalk.cyan(backupId));
|
|
41
|
+
console.log();
|
|
42
|
+
|
|
43
|
+
const confirmed = await askConfirmation(chalk.white('Are you sure you want to delete this backup? (y/N): '));
|
|
44
|
+
|
|
45
|
+
if (!confirmed) {
|
|
46
|
+
console.log(chalk.gray('\nDeletion cancelled.'));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
console.log(chalk.gray('\nDeleting backup...'));
|
|
51
|
+
|
|
52
|
+
await api.deleteBackup(apiKey, databaseId, backupId);
|
|
53
|
+
|
|
54
|
+
console.log(chalk.green.bold('\nā Backup deleted successfully!\n'));
|
|
55
|
+
console.log(chalk.gray('Backup ID:'), chalk.cyan(backupId));
|
|
56
|
+
console.log();
|
|
57
|
+
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error(chalk.red.bold('\nā Failed to delete backup:'), chalk.red(error.message));
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = backupDeleteCommand;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const api = require('../api');
|
|
3
|
+
const config = require('../config');
|
|
4
|
+
|
|
5
|
+
async function backupListCommand(databaseId) {
|
|
6
|
+
console.log(chalk.blue.bold('\nš¾ Backups\n'));
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
// Check if logged in
|
|
10
|
+
if (!config.isLoggedIn()) {
|
|
11
|
+
console.error(chalk.red.bold('ā Not logged in'));
|
|
12
|
+
console.log(chalk.gray('\nPlease run'), chalk.cyan('vibedb login'), chalk.gray('or'), chalk.cyan('vibedb signup'), chalk.gray('first.'));
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!databaseId) {
|
|
17
|
+
console.error(chalk.red.bold('ā Database ID is required'));
|
|
18
|
+
console.log(chalk.gray('\nUsage:'), chalk.cyan('vibedb backup list <database-id>'));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const apiKey = config.getAPIKey();
|
|
23
|
+
|
|
24
|
+
console.log(chalk.gray(`Fetching backups for database ${databaseId}...`));
|
|
25
|
+
|
|
26
|
+
const backups = await api.listBackups(apiKey, databaseId);
|
|
27
|
+
|
|
28
|
+
if (backups.length === 0) {
|
|
29
|
+
console.log(chalk.yellow('\nNo backups found for this database.'));
|
|
30
|
+
console.log(chalk.gray('Create one with:'), chalk.cyan(`vibedb backup create ${databaseId}`));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.log();
|
|
35
|
+
console.log(chalk.gray(`Found ${backups.length} backup(s):\n`));
|
|
36
|
+
|
|
37
|
+
// Display backups in a table format
|
|
38
|
+
backups.forEach((backup, index) => {
|
|
39
|
+
const isAutomatic = backup.type === 'automatic';
|
|
40
|
+
const typeLabel = isAutomatic ? chalk.blue('[Auto]') : chalk.green('[Manual]');
|
|
41
|
+
const sizeLabel = backup.size_mb ? `${backup.size_mb.toFixed(2)} MB` : 'N/A';
|
|
42
|
+
const createdAt = new Date(backup.created_at).toLocaleString();
|
|
43
|
+
|
|
44
|
+
console.log(chalk.white.bold(`${index + 1}. ${backup.id}`));
|
|
45
|
+
console.log(chalk.gray(' Type:'), typeLabel);
|
|
46
|
+
console.log(chalk.gray(' Size:'), chalk.cyan(sizeLabel));
|
|
47
|
+
console.log(chalk.gray(' Created:'), chalk.white(createdAt));
|
|
48
|
+
|
|
49
|
+
if (backup.deleted_at) {
|
|
50
|
+
console.log(chalk.gray(' Status:'), chalk.red('Deleted'));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
console.log(chalk.gray('Commands:'));
|
|
57
|
+
console.log(chalk.gray(' Restore:'), chalk.cyan(`vibedb backup restore ${databaseId} <backup-id>`));
|
|
58
|
+
console.log(chalk.gray(' Delete:'), chalk.cyan(`vibedb backup delete ${databaseId} <backup-id>`));
|
|
59
|
+
console.log();
|
|
60
|
+
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error(chalk.red.bold('\nā Failed to list backups:'), chalk.red(error.message));
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = backupListCommand;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const readline = require('readline');
|
|
3
|
+
const api = require('../api');
|
|
4
|
+
const config = require('../config');
|
|
5
|
+
|
|
6
|
+
async function askConfirmation(question) {
|
|
7
|
+
const rl = readline.createInterface({
|
|
8
|
+
input: process.stdin,
|
|
9
|
+
output: process.stdout,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
rl.question(question, (answer) => {
|
|
14
|
+
rl.close();
|
|
15
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function backupRestoreCommand(databaseId, backupId) {
|
|
21
|
+
console.log(chalk.blue.bold('\nš Restore Backup\n'));
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
// Check if logged in
|
|
25
|
+
if (!config.isLoggedIn()) {
|
|
26
|
+
console.error(chalk.red.bold('ā Not logged in'));
|
|
27
|
+
console.log(chalk.gray('\nPlease run'), chalk.cyan('vibedb login'), chalk.gray('or'), chalk.cyan('vibedb signup'), chalk.gray('first.'));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!databaseId || !backupId) {
|
|
32
|
+
console.error(chalk.red.bold('ā Database ID and Backup ID are required'));
|
|
33
|
+
console.log(chalk.gray('\nUsage:'), chalk.cyan('vibedb backup restore <database-id> <backup-id>'));
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const apiKey = config.getAPIKey();
|
|
38
|
+
|
|
39
|
+
// Warning message
|
|
40
|
+
console.log(chalk.yellow.bold('ā ļø WARNING:'));
|
|
41
|
+
console.log(chalk.yellow('This will replace ALL current data in the database with the backup.'));
|
|
42
|
+
console.log(chalk.yellow('This action cannot be undone.\n'));
|
|
43
|
+
|
|
44
|
+
console.log(chalk.gray('Database ID:'), chalk.cyan(databaseId));
|
|
45
|
+
console.log(chalk.gray('Backup ID:'), chalk.cyan(backupId));
|
|
46
|
+
console.log();
|
|
47
|
+
|
|
48
|
+
const confirmed = await askConfirmation(chalk.white('Are you sure you want to restore this backup? (y/N): '));
|
|
49
|
+
|
|
50
|
+
if (!confirmed) {
|
|
51
|
+
console.log(chalk.gray('\nRestore cancelled.'));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.log(chalk.gray('\nRestoring backup...'));
|
|
56
|
+
|
|
57
|
+
const result = await api.restoreBackup(apiKey, databaseId, backupId);
|
|
58
|
+
|
|
59
|
+
console.log(chalk.green.bold('\nā Backup restored successfully!\n'));
|
|
60
|
+
console.log(chalk.gray('Database ID:'), chalk.cyan(databaseId));
|
|
61
|
+
console.log(chalk.gray('Backup ID:'), chalk.cyan(backupId));
|
|
62
|
+
console.log(chalk.gray('Restored at:'), chalk.white(new Date().toLocaleString()));
|
|
63
|
+
console.log();
|
|
64
|
+
console.log(chalk.green('Your database has been restored to the backup state.'));
|
|
65
|
+
console.log();
|
|
66
|
+
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error(chalk.red.bold('\nā Failed to restore backup:'), chalk.red(error.message));
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = backupRestoreCommand;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const api = require('../api');
|
|
3
|
+
const config = require('../config');
|
|
4
|
+
|
|
5
|
+
async function usageCommand() {
|
|
6
|
+
console.log(chalk.blue.bold('\nš Usage & Costs\n'));
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
// Check if logged in
|
|
10
|
+
if (!config.isLoggedIn()) {
|
|
11
|
+
console.error(chalk.red.bold('ā Not logged in'));
|
|
12
|
+
console.log(chalk.gray('\nPlease run'), chalk.cyan('vibedb login'), chalk.gray('or'), chalk.cyan('vibedb signup'), chalk.gray('first.'));
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const apiKey = config.getAPIKey();
|
|
17
|
+
const currentTeamId = config.getCurrentTeamId();
|
|
18
|
+
|
|
19
|
+
console.log(chalk.gray('Fetching usage data...'));
|
|
20
|
+
|
|
21
|
+
const account = await api.getAccount(apiKey, currentTeamId);
|
|
22
|
+
|
|
23
|
+
console.log();
|
|
24
|
+
|
|
25
|
+
// Storage breakdown
|
|
26
|
+
console.log(chalk.white.bold('Storage Usage:'));
|
|
27
|
+
|
|
28
|
+
if (account.storage_breakdown) {
|
|
29
|
+
const { database_storage_mb, backup_storage_mb, total_storage_mb } = account.storage_breakdown;
|
|
30
|
+
|
|
31
|
+
console.log(chalk.gray(' Database Storage:'), chalk.white(`${database_storage_mb.toFixed(2)} MB`));
|
|
32
|
+
console.log(chalk.gray(' Backup Storage:'), chalk.white(`${backup_storage_mb.toFixed(2)} MB`));
|
|
33
|
+
console.log(chalk.gray(' āāāāāāāāāāāāāāāāā'));
|
|
34
|
+
console.log(chalk.gray(' Total Storage:'), chalk.cyan.bold(`${total_storage_mb.toFixed(2)} MB`));
|
|
35
|
+
} else {
|
|
36
|
+
console.log(chalk.gray(' Total Storage:'), chalk.cyan.bold(`${account.storage_total_mb.toFixed(2)} MB`));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log();
|
|
40
|
+
|
|
41
|
+
// Cost information
|
|
42
|
+
console.log(chalk.white.bold('Estimated Charges:'));
|
|
43
|
+
const charges = account.estimated_charges_usd || 0;
|
|
44
|
+
console.log(chalk.gray(' Current Period:'), chalk.green.bold(`$${charges.toFixed(4)} USD`));
|
|
45
|
+
console.log();
|
|
46
|
+
|
|
47
|
+
// Pricing info
|
|
48
|
+
console.log(chalk.gray('Pricing:'));
|
|
49
|
+
console.log(chalk.gray(' Storage: $0.00014 per GB-hour'));
|
|
50
|
+
console.log(chalk.gray(' Compute: $0.000167 per minute'));
|
|
51
|
+
console.log();
|
|
52
|
+
|
|
53
|
+
// Show current team if applicable
|
|
54
|
+
if (currentTeamId) {
|
|
55
|
+
console.log(chalk.gray('Team:'), chalk.cyan(currentTeamId));
|
|
56
|
+
console.log(chalk.gray('Run'), chalk.cyan('vibedb teams list'), chalk.gray('to see all your teams.'));
|
|
57
|
+
console.log();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error(chalk.red.bold('\nā Failed to fetch usage:'), chalk.red(error.message));
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = usageCommand;
|