@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 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 5 minutes after Windows logon.
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 # create the task (run terminal as Admin)
111
- dcli auto-ping --remove # remove the task (run terminal as Admin)
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
- ## Config File
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
- `~/.dcli/refresh.json`:
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
- Entries can be objects with `uri` and optional `name`, or plain strings (backward compatible).
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohamed1_1ibrahim/dcli",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "Database CLI - MongoDB data management tool (export, import, ping, info)",
5
5
  "main": "src/main.js",
6
6
  "bin": {
@@ -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
+ }
@@ -15,20 +15,52 @@ export async function autoPingCommand(options) {
15
15
  }
16
16
 
17
17
  try {
18
- const createCmd = [
19
- `schtasks /create`,
20
- `/tn "${taskName}"`,
21
- `/sc ONLOGON`,
22
- `/delay 0000:05`,
23
- `/tr "cmd /c dcli ping"`,
24
- `/f`,
25
- ].join(' ');
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(`It will run "dcli ping" 5 minutes after you log on.`);
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 Run as administrator).' : err.message}`);
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}`;