@mohamed1_1ibrahim/dcli 1.0.0 → 1.1.1
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/README.md +93 -8
- package/package.json +1 -1
- package/src/commands/autoClone.js +56 -0
- package/src/commands/autoPing.js +42 -10
- package/src/commands/clone.js +45 -0
- package/src/commands/cloneAdd.js +20 -0
- package/src/commands/cloneRemove.js +16 -0
- package/src/commands/exportCmd.js +2 -0
- package/src/commands/gui.js +350 -0
- package/src/commands/importCmd.js +2 -0
- package/src/commands/info.js +2 -0
- package/src/commands/pingCmd.js +2 -1
- package/src/commands/show.js +30 -0
- package/src/commands/view.js +100 -0
- package/src/gui/index.html +406 -0
- package/src/main.js +65 -4
- package/src/utils/cloneConfig.js +56 -0
- package/src/utils/help.js +101 -4
- package/src/utils/mongodb.js +10 -0
- package/src/utils/resolve.js +14 -0
- package/src/utils/table.js +74 -0
package/README.md
CHANGED
|
@@ -11,11 +11,18 @@
|
|
|
11
11
|
- **add** — Register a database URI in the auto-ping list (with optional name)
|
|
12
12
|
- **remove** — Remove a database by URI or friendly name
|
|
13
13
|
- **auto-ping** — Schedule `dcli ping` to run automatically on Windows logon
|
|
14
|
+
- **view** — Browse collections and documents in a styled table
|
|
15
|
+
- **show** — Display the auto-ping or auto-clone database list
|
|
16
|
+
- **clone-add** — Register a database URI in the auto-clone list
|
|
17
|
+
- **clone-remove** — Remove a database from the auto-clone list
|
|
18
|
+
- **clone** — Clone a single database by friendly name to JSON
|
|
19
|
+
- **auto-clone** — Clone all databases in the clone list to JSON files
|
|
20
|
+
- **gui** — Launch the web GUI in your browser
|
|
14
21
|
|
|
15
22
|
## Installation
|
|
16
23
|
|
|
17
24
|
```bash
|
|
18
|
-
npm install -g dcli
|
|
25
|
+
npm install -g @mohamed1_1ibrahim/dcli
|
|
19
26
|
```
|
|
20
27
|
|
|
21
28
|
```bash
|
|
@@ -103,17 +110,93 @@ dcli remove dbName # remove by friendly name
|
|
|
103
110
|
|
|
104
111
|
### auto-ping
|
|
105
112
|
|
|
106
|
-
Schedule `dcli ping` to run automatically
|
|
107
|
-
Requires administrator privileges to create the task.
|
|
113
|
+
Schedule `dcli ping` to run automatically. Requires administrator privileges.
|
|
108
114
|
|
|
109
115
|
```bash
|
|
110
|
-
dcli auto-ping
|
|
111
|
-
dcli auto-ping --remove
|
|
116
|
+
dcli auto-ping # ONLOGON, 5 min delay (default)
|
|
117
|
+
dcli auto-ping --remove # remove the task
|
|
118
|
+
dcli auto-ping --schedule DAILY --at 09:00
|
|
119
|
+
dcli auto-ping --schedule HOURLY --every 2
|
|
120
|
+
dcli auto-ping --schedule ONCE --at 18:00
|
|
121
|
+
dcli auto-ping --schedule ONLOGON --delay 10
|
|
112
122
|
```
|
|
113
123
|
|
|
114
|
-
|
|
124
|
+
| Option | Description |
|
|
125
|
+
|--------|-------------|
|
|
126
|
+
| `--remove` | Remove the scheduled task |
|
|
127
|
+
| `--schedule <type>` | `ONLOGON`, `DAILY`, `HOURLY`, or `ONCE` (default: `ONLOGON`) |
|
|
128
|
+
| `--at <time>` | Time for `DAILY`/`ONCE` schedules (24h, e.g. `09:00`) |
|
|
129
|
+
| `--every <n>` | Interval in hours for `HOURLY` (default: 1) |
|
|
130
|
+
| `--delay <n>` | Delay in minutes for `ONLOGON` (default: 5) |
|
|
131
|
+
|
|
132
|
+
### view
|
|
133
|
+
|
|
134
|
+
Browse collections and documents in a styled table.
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
dcli view "mongodb://localhost:27017/mydb" # list collections
|
|
138
|
+
dcli view "mongodb://localhost:27017/mydb" users # browse documents
|
|
139
|
+
dcli view "mongodb://..." users --limit 5
|
|
140
|
+
dcli view "mongodb://..." users --fields name,email --sort name
|
|
141
|
+
dcli view "mongodb://..." users --all --json
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
| Option | Description |
|
|
145
|
+
|--------|-------------|
|
|
146
|
+
| `--limit <n>` | Maximum documents (default: 10) |
|
|
147
|
+
| `--fields <fields>` | Comma-separated fields to display |
|
|
148
|
+
| `--sort <field>` | Sort ascending by field |
|
|
149
|
+
| `--all` | Show all documents (no limit) |
|
|
150
|
+
| `--json` | Output raw JSON instead of a table |
|
|
115
151
|
|
|
116
|
-
|
|
152
|
+
### show
|
|
153
|
+
|
|
154
|
+
Display the auto-ping or auto-clone database list.
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
dcli show # show ping list (~/.dcli/refresh.json)
|
|
158
|
+
dcli show --clone # show clone list (~/.dcli/auto-clone.json)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### clone-add / clone-remove
|
|
162
|
+
|
|
163
|
+
Manage the auto-clone list (`~/.dcli/auto-clone.json`).
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
dcli clone-add "mongodb://..." -n dbName
|
|
167
|
+
dcli clone-remove dbName
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### clone
|
|
171
|
+
|
|
172
|
+
Clone a single database from the clone list to a JSON file.
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
dcli clone dbName
|
|
176
|
+
dcli clone dbName -o ./backups
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### auto-clone
|
|
180
|
+
|
|
181
|
+
Clone all databases in the clone list to JSON files.
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
dcli auto-clone
|
|
185
|
+
dcli auto-clone -o ./backups
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### gui
|
|
189
|
+
|
|
190
|
+
Launch the web GUI in your browser (port defaults to 3456).
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
dcli gui
|
|
194
|
+
dcli gui -p 8080
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Config Files
|
|
198
|
+
|
|
199
|
+
**Ping list** — `~/.dcli/refresh.json`:
|
|
117
200
|
|
|
118
201
|
```json
|
|
119
202
|
{
|
|
@@ -124,7 +207,9 @@ dcli auto-ping --remove # remove the task (run terminal as Admin)
|
|
|
124
207
|
}
|
|
125
208
|
```
|
|
126
209
|
|
|
127
|
-
|
|
210
|
+
**Clone list** — `~/.dcli/auto-clone.json` (same format as above).
|
|
211
|
+
|
|
212
|
+
Entries can be objects with `uri` and optional `name`, or plain strings (backward compatible). Many commands also accept a **friendly name** in place of a URI (e.g. `dcli ping dbName`).
|
|
128
213
|
|
|
129
214
|
## Export Format
|
|
130
215
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { writeFile, mkdir } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { connect, exportDatabase, extractDbName } from '../utils/mongodb.js';
|
|
4
|
+
import { readCloneConfig } from '../utils/cloneConfig.js';
|
|
5
|
+
import { success, error, info, highlight } from '../utils/logger.js';
|
|
6
|
+
|
|
7
|
+
export async function autoCloneCommand(options) {
|
|
8
|
+
try {
|
|
9
|
+
const config = await readCloneConfig();
|
|
10
|
+
const databases = config.databases;
|
|
11
|
+
|
|
12
|
+
if (databases.length === 0) {
|
|
13
|
+
info('No databases in the clone list. Add some with "dcli clone-add <uri>".');
|
|
14
|
+
process.exit(0);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const outputDir = options.output || process.cwd();
|
|
18
|
+
await mkdir(outputDir, { recursive: true });
|
|
19
|
+
|
|
20
|
+
highlight(`── Auto Clone: ${databases.length} database(s) ──`);
|
|
21
|
+
|
|
22
|
+
let cloned = 0;
|
|
23
|
+
let failed = 0;
|
|
24
|
+
|
|
25
|
+
for (const entry of databases) {
|
|
26
|
+
const label = entry.name || entry.uri;
|
|
27
|
+
try {
|
|
28
|
+
info(`Cloning: ${label}`);
|
|
29
|
+
const client = await connect(entry.uri);
|
|
30
|
+
const dbName = extractDbName(entry.uri);
|
|
31
|
+
const data = await exportDatabase(client);
|
|
32
|
+
await client.close();
|
|
33
|
+
|
|
34
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
35
|
+
const fileName = `clone-${timestamp}-${dbName}.json`;
|
|
36
|
+
const filePath = join(outputDir, fileName);
|
|
37
|
+
const exportData = {
|
|
38
|
+
database: dbName,
|
|
39
|
+
clonedAt: new Date().toISOString(),
|
|
40
|
+
data,
|
|
41
|
+
};
|
|
42
|
+
await writeFile(filePath, JSON.stringify(exportData, null, 2), 'utf-8');
|
|
43
|
+
success(`Cloned ${label} → ${fileName} (${Object.keys(data).length} collections)`);
|
|
44
|
+
cloned++;
|
|
45
|
+
} catch (err) {
|
|
46
|
+
error(`Failed to clone ${label}: ${err.message}`);
|
|
47
|
+
failed++;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
highlight(`── Done: ${cloned} cloned, ${failed} failed ──`);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
error(`Auto-clone failed: ${err.message}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/commands/autoPing.js
CHANGED
|
@@ -15,20 +15,52 @@ export async function autoPingCommand(options) {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
try {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
const schedule = (options.schedule || 'ONLOGON').toUpperCase();
|
|
19
|
+
const at = options.at;
|
|
20
|
+
const every = options.every;
|
|
21
|
+
const delay = options.delay || '5';
|
|
22
|
+
|
|
23
|
+
let parts = [`schtasks /create`, `/tn "${taskName}"`, `/f`];
|
|
24
|
+
|
|
25
|
+
switch (schedule) {
|
|
26
|
+
case 'DAILY': {
|
|
27
|
+
const time = at || '09:00';
|
|
28
|
+
parts.push(`/sc DAILY`, `/st ${time}`);
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
case 'HOURLY': {
|
|
32
|
+
const interval = every || '1';
|
|
33
|
+
parts.push(`/sc HOURLY`, `/mo ${interval}`);
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
case 'ONCE': {
|
|
37
|
+
const time = at || '09:00';
|
|
38
|
+
parts.push(`/sc ONCE`, `/st ${time}`);
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
default: {
|
|
42
|
+
const delayMinutes = Math.max(1, parseInt(delay, 10) || 5);
|
|
43
|
+
parts.push(`/sc ONLOGON`, `/delay ${String(delayMinutes).padStart(4, '0')}:00`);
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
parts.push(`/tr "cmd /c dcli ping"`);
|
|
49
|
+
const createCmd = parts.join(' ');
|
|
26
50
|
|
|
27
51
|
execSync(createCmd, { stdio: 'pipe' });
|
|
52
|
+
|
|
53
|
+
const messages = {
|
|
54
|
+
DAILY: `It will run "dcli ping" daily at ${at || '09:00'}.`,
|
|
55
|
+
HOURLY: `It will run "dcli ping" every ${every || '1'} hour(s).`,
|
|
56
|
+
ONCE: `It will run "dcli ping" once at ${at || '09:00'}.`,
|
|
57
|
+
ONLOGON: `It will run "dcli ping" ${delay ? `${delay} minute(s) after` : ''} you log on.`,
|
|
58
|
+
};
|
|
59
|
+
|
|
28
60
|
success(`Task "${taskName}" created successfully.`);
|
|
29
|
-
info(
|
|
61
|
+
info(messages[schedule] || messages.ONLOGON);
|
|
30
62
|
} catch (err) {
|
|
31
|
-
error(`Failed to create task: ${err.message.includes('Access is denied') ? 'Please run as Administrator (right-click terminal
|
|
63
|
+
error(`Failed to create task: ${err.message.includes('Access is denied') ? 'Please run as Administrator (right-click terminal \u2192 Run as administrator).' : err.message}`);
|
|
32
64
|
process.exit(1);
|
|
33
65
|
}
|
|
34
66
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { writeFile, mkdir } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { connect, exportDatabase, extractDbName } from '../utils/mongodb.js';
|
|
4
|
+
import { readCloneConfig } from '../utils/cloneConfig.js';
|
|
5
|
+
import { readConfig } from '../utils/config.js';
|
|
6
|
+
import { success, error, info, highlight } from '../utils/logger.js';
|
|
7
|
+
|
|
8
|
+
export async function cloneCommand(name, options) {
|
|
9
|
+
try {
|
|
10
|
+
const config = await readCloneConfig();
|
|
11
|
+
const entry = config.databases.find(e => (e.name && e.name === name) || e.uri === name);
|
|
12
|
+
|
|
13
|
+
if (!entry) {
|
|
14
|
+
error(`Database "${name}" not found in clone list.`);
|
|
15
|
+
info('Use "dcli clone-add <uri> -n <name>" to add it first.');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const uri = typeof entry === 'string' ? entry : entry.uri;
|
|
20
|
+
const label = entry.name || uri;
|
|
21
|
+
|
|
22
|
+
const outputDir = options.output || process.cwd();
|
|
23
|
+
await mkdir(outputDir, { recursive: true });
|
|
24
|
+
|
|
25
|
+
highlight(`Cloning: ${label}`);
|
|
26
|
+
const client = await connect(uri);
|
|
27
|
+
const dbName = extractDbName(uri);
|
|
28
|
+
const data = await exportDatabase(client);
|
|
29
|
+
await client.close();
|
|
30
|
+
|
|
31
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
32
|
+
const fileName = `clone-${timestamp}-${dbName}.json`;
|
|
33
|
+
const filePath = join(outputDir, fileName);
|
|
34
|
+
const exportData = {
|
|
35
|
+
database: dbName,
|
|
36
|
+
clonedAt: new Date().toISOString(),
|
|
37
|
+
data,
|
|
38
|
+
};
|
|
39
|
+
await writeFile(filePath, JSON.stringify(exportData, null, 2), 'utf-8');
|
|
40
|
+
success(`Cloned ${label} → ${fileName} (${Object.keys(data).length} collections)`);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
error(`Clone failed: ${err.message}`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { addCloneDatabase } from '../utils/cloneConfig.js';
|
|
2
|
+
import { success, warn, error } from '../utils/logger.js';
|
|
3
|
+
|
|
4
|
+
export async function cloneAddCommand(uri, options) {
|
|
5
|
+
try {
|
|
6
|
+
const name = options.name;
|
|
7
|
+
const result = await addCloneDatabase(uri, name);
|
|
8
|
+
const label = name ? `${name} (${uri})` : uri;
|
|
9
|
+
if (result === 'added') {
|
|
10
|
+
success(`Database added to clone list: ${label}`);
|
|
11
|
+
} else if (result === 'updated') {
|
|
12
|
+
success(`Name updated to "${name}" for ${uri}`);
|
|
13
|
+
} else {
|
|
14
|
+
warn(`Database is already in the clone list.`);
|
|
15
|
+
}
|
|
16
|
+
} catch (err) {
|
|
17
|
+
error(`Failed to add database: ${err.message}`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { removeCloneDatabase } from '../utils/cloneConfig.js';
|
|
2
|
+
import { success, warn, error } from '../utils/logger.js';
|
|
3
|
+
|
|
4
|
+
export async function cloneRemoveCommand(target) {
|
|
5
|
+
try {
|
|
6
|
+
const removed = await removeCloneDatabase(target);
|
|
7
|
+
if (removed) {
|
|
8
|
+
success(`Database removed from clone list.`);
|
|
9
|
+
} else {
|
|
10
|
+
warn(`No database found matching "${target}" in the clone list.`);
|
|
11
|
+
}
|
|
12
|
+
} catch (err) {
|
|
13
|
+
error(`Failed to remove database: ${err.message}`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -2,6 +2,7 @@ import { writeFile, mkdir, access } from 'node:fs/promises';
|
|
|
2
2
|
import { join, extname, dirname, basename } from 'node:path';
|
|
3
3
|
import { constants } from 'node:fs';
|
|
4
4
|
import { connect, exportDatabase, extractDbName } from '../utils/mongodb.js';
|
|
5
|
+
import { resolveName } from '../utils/resolve.js';
|
|
5
6
|
import { success, error, info, highlight } from '../utils/logger.js';
|
|
6
7
|
|
|
7
8
|
function ensureJsonExt(name) {
|
|
@@ -50,6 +51,7 @@ async function getUniquePath(filePath) {
|
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
export async function exportCommand(uri, options) {
|
|
54
|
+
uri = await resolveName(uri);
|
|
53
55
|
const dbName = extractDbName(uri);
|
|
54
56
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
55
57
|
const outputName = options.output || `data-${timestamp}-${dbName}`;
|