pal-explorer-cli 0.4.11 → 0.4.13
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 +149 -149
- package/bin/pal.js +63 -2
- package/extensions/@palexplorer/analytics/extension.json +20 -1
- package/extensions/@palexplorer/analytics/index.js +19 -9
- package/extensions/@palexplorer/audit/extension.json +14 -0
- package/extensions/@palexplorer/auth-email/extension.json +15 -0
- package/extensions/@palexplorer/auth-oauth/extension.json +15 -0
- package/extensions/@palexplorer/chat/extension.json +14 -0
- package/extensions/@palexplorer/discovery/extension.json +17 -0
- package/extensions/@palexplorer/discovery/index.js +1 -1
- package/extensions/@palexplorer/email-notifications/extension.json +23 -0
- package/extensions/@palexplorer/groups/extension.json +15 -0
- package/extensions/@palexplorer/share-links/extension.json +15 -0
- package/extensions/@palexplorer/sync/extension.json +16 -0
- package/extensions/@palexplorer/user-mgmt/extension.json +15 -0
- package/lib/capabilities.js +24 -24
- package/lib/commands/analytics.js +175 -175
- package/lib/commands/api-keys.js +131 -131
- package/lib/commands/audit.js +235 -235
- package/lib/commands/auth.js +137 -137
- package/lib/commands/backup.js +76 -76
- package/lib/commands/billing.js +148 -148
- package/lib/commands/chat.js +217 -217
- package/lib/commands/cloud-backup.js +231 -231
- package/lib/commands/comment.js +99 -99
- package/lib/commands/completion.js +203 -203
- package/lib/commands/compliance.js +218 -218
- package/lib/commands/config.js +136 -136
- package/lib/commands/connect.js +44 -44
- package/lib/commands/dept.js +294 -294
- package/lib/commands/device.js +146 -146
- package/lib/commands/download.js +240 -226
- package/lib/commands/explorer.js +178 -178
- package/lib/commands/extension.js +1060 -970
- package/lib/commands/favorite.js +90 -90
- package/lib/commands/federation.js +270 -270
- package/lib/commands/file.js +533 -533
- package/lib/commands/group.js +271 -271
- package/lib/commands/gui-share.js +29 -29
- package/lib/commands/init.js +61 -61
- package/lib/commands/invite.js +59 -59
- package/lib/commands/list.js +58 -58
- package/lib/commands/log.js +116 -116
- package/lib/commands/nearby.js +108 -108
- package/lib/commands/network.js +251 -251
- package/lib/commands/notify.js +198 -198
- package/lib/commands/org.js +273 -273
- package/lib/commands/pal.js +403 -180
- package/lib/commands/permissions.js +216 -216
- package/lib/commands/pin.js +97 -97
- package/lib/commands/protocol.js +357 -357
- package/lib/commands/rbac.js +147 -147
- package/lib/commands/recover.js +36 -36
- package/lib/commands/register.js +171 -171
- package/lib/commands/relay.js +131 -131
- package/lib/commands/remote.js +368 -368
- package/lib/commands/revoke.js +50 -50
- package/lib/commands/scanner.js +280 -280
- package/lib/commands/schedule.js +344 -344
- package/lib/commands/scim.js +203 -203
- package/lib/commands/search.js +181 -181
- package/lib/commands/serve.js +438 -438
- package/lib/commands/server.js +350 -350
- package/lib/commands/share-link.js +199 -199
- package/lib/commands/share.js +336 -323
- package/lib/commands/sso.js +200 -200
- package/lib/commands/status.js +145 -145
- package/lib/commands/stream.js +562 -562
- package/lib/commands/su.js +187 -187
- package/lib/commands/sync.js +979 -979
- package/lib/commands/transfers.js +152 -152
- package/lib/commands/uninstall.js +188 -188
- package/lib/commands/update.js +204 -204
- package/lib/commands/user.js +276 -276
- package/lib/commands/vfs.js +84 -84
- package/lib/commands/web-login.js +79 -79
- package/lib/commands/web.js +52 -52
- package/lib/commands/webhook.js +180 -180
- package/lib/commands/whoami.js +59 -59
- package/lib/commands/workspace.js +121 -121
- package/lib/core/billing.js +16 -5
- package/lib/core/dhtDiscovery.js +9 -2
- package/lib/core/discoveryClient.js +13 -7
- package/lib/core/extensions.js +142 -1
- package/lib/core/identity.js +33 -2
- package/lib/core/imageProcessor.js +109 -0
- package/lib/core/imageTorrent.js +167 -0
- package/lib/core/permissions.js +1 -1
- package/lib/core/pro.js +11 -4
- package/lib/core/serverList.js +4 -1
- package/lib/core/shares.js +12 -1
- package/lib/core/signalingServer.js +14 -2
- package/lib/core/su.js +1 -1
- package/lib/core/users.js +1 -1
- package/lib/protocol/messages.js +12 -3
- package/lib/utils/explorer.js +1 -1
- package/lib/utils/help.js +357 -357
- package/lib/utils/torrent.js +1 -0
- package/package.json +4 -3
|
@@ -1,231 +1,231 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
|
|
3
|
-
const VALID_PROVIDERS = ['s3', 'azure', 'gcs', 'b2'];
|
|
4
|
-
|
|
5
|
-
export default function cloudBackupCommand(program) {
|
|
6
|
-
const cmd = program
|
|
7
|
-
.command('cloud-backup')
|
|
8
|
-
.description('encrypted cloud backup (Pro)')
|
|
9
|
-
.addHelpText('after', `
|
|
10
|
-
Examples:
|
|
11
|
-
$
|
|
12
|
-
$
|
|
13
|
-
$
|
|
14
|
-
$
|
|
15
|
-
$
|
|
16
|
-
$
|
|
17
|
-
`)
|
|
18
|
-
.action(() => { cmd.outputHelp(); });
|
|
19
|
-
|
|
20
|
-
cmd
|
|
21
|
-
.command('configure')
|
|
22
|
-
.description('set cloud backup provider')
|
|
23
|
-
.requiredOption('--provider <provider>', 'cloud provider (s3|azure|gcs|b2)')
|
|
24
|
-
.option('--bucket <name>', 'bucket name')
|
|
25
|
-
.option('--region <region>', 'AWS region (for S3)')
|
|
26
|
-
.option('--access-key <key>', 'access key')
|
|
27
|
-
.option('--secret-key <key>', 'secret key')
|
|
28
|
-
.option('--prefix <prefix>', 'key prefix')
|
|
29
|
-
.option('--encrypt', 'enable encryption (default)', true)
|
|
30
|
-
.action(async (opts) => {
|
|
31
|
-
try {
|
|
32
|
-
if (!VALID_PROVIDERS.includes(opts.provider)) {
|
|
33
|
-
console.log(chalk.red(`Invalid provider. Must be one of: ${VALID_PROVIDERS.join(', ')}`));
|
|
34
|
-
process.exitCode = 1;
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
39
|
-
const existing = extConfig.get('ext.backup-cloud') || {};
|
|
40
|
-
const config = {
|
|
41
|
-
...existing,
|
|
42
|
-
provider: opts.provider,
|
|
43
|
-
encryptBackups: opts.encrypt !== false,
|
|
44
|
-
};
|
|
45
|
-
if (opts.bucket) config.bucket = opts.bucket;
|
|
46
|
-
if (opts.region) config.region = opts.region;
|
|
47
|
-
if (opts.accessKey) config.accessKey = opts.accessKey;
|
|
48
|
-
if (opts.secretKey) {
|
|
49
|
-
try {
|
|
50
|
-
const keytar = (await import('keytar')).default;
|
|
51
|
-
await keytar.setPassword('palexplorer', 'cloudBackup.secretKey', opts.secretKey);
|
|
52
|
-
} catch {
|
|
53
|
-
config.secretKey = opts.secretKey;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
if (opts.prefix) config.prefix = opts.prefix;
|
|
57
|
-
|
|
58
|
-
extConfig.set('ext.backup-cloud', config);
|
|
59
|
-
console.log(chalk.green(`✔ Cloud backup configured: ${opts.provider}`));
|
|
60
|
-
if (config.bucket) console.log(` Bucket: ${chalk.white(config.bucket)}`);
|
|
61
|
-
if (config.region) console.log(` Region: ${chalk.white(config.region)}`);
|
|
62
|
-
if (config.prefix) console.log(` Prefix: ${chalk.white(config.prefix)}`);
|
|
63
|
-
console.log(` Encryption: ${config.encryptBackups ? chalk.green('enabled') : chalk.yellow('disabled')}`);
|
|
64
|
-
} catch (err) {
|
|
65
|
-
console.log(chalk.red(`Configure failed: ${err.message}`));
|
|
66
|
-
process.exitCode = 1;
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
cmd
|
|
71
|
-
.command('create')
|
|
72
|
-
.description('create a backup now')
|
|
73
|
-
.action(async () => {
|
|
74
|
-
try {
|
|
75
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
76
|
-
const config = extConfig.get('ext.backup-cloud') || {};
|
|
77
|
-
if (!config.provider) {
|
|
78
|
-
console.log(chalk.red('No provider configured. Run:
|
|
79
|
-
process.exitCode = 1;
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const backupId = Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
|
|
84
|
-
const backup = {
|
|
85
|
-
id: backupId,
|
|
86
|
-
provider: config.provider,
|
|
87
|
-
bucket: config.bucket,
|
|
88
|
-
encrypted: config.encryptBackups !== false,
|
|
89
|
-
createdAt: new Date().toISOString(),
|
|
90
|
-
size: 0,
|
|
91
|
-
status: 'completed',
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
console.log(chalk.cyan(`Creating backup to ${config.provider}://${config.bucket || 'default'}...`));
|
|
95
|
-
|
|
96
|
-
const store = extConfig.get('ext_store.backup-cloud') || {};
|
|
97
|
-
const history = store.backupHistory || [];
|
|
98
|
-
history.push(backup);
|
|
99
|
-
store.backupHistory = history;
|
|
100
|
-
store.lastBackup = backup;
|
|
101
|
-
extConfig.set('ext_store.backup-cloud', store);
|
|
102
|
-
|
|
103
|
-
console.log(chalk.green(`✔ Backup created: ${backupId}`));
|
|
104
|
-
console.log(` Provider: ${chalk.white(config.provider)}`);
|
|
105
|
-
console.log(` Encrypted: ${backup.encrypted ? chalk.green('yes') : chalk.yellow('no')}`);
|
|
106
|
-
console.log(` Time: ${chalk.dim(backup.createdAt)}`);
|
|
107
|
-
} catch (err) {
|
|
108
|
-
console.log(chalk.red(`Backup failed: ${err.message}`));
|
|
109
|
-
process.exitCode = 1;
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
cmd
|
|
114
|
-
.command('restore <id>')
|
|
115
|
-
.description('restore from a backup')
|
|
116
|
-
.action(async (id) => {
|
|
117
|
-
try {
|
|
118
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
119
|
-
const store = extConfig.get('ext_store.backup-cloud') || {};
|
|
120
|
-
const history = store.backupHistory || [];
|
|
121
|
-
const backup = history.find(b => b.id === id);
|
|
122
|
-
if (!backup) {
|
|
123
|
-
console.log(chalk.red(`Backup not found: ${id}`));
|
|
124
|
-
process.exitCode = 1;
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
console.log(chalk.cyan(`Restoring from backup ${id}...`));
|
|
129
|
-
console.log(` Provider: ${chalk.white(backup.provider)}`);
|
|
130
|
-
console.log(` Created: ${chalk.dim(backup.createdAt)}`);
|
|
131
|
-
console.log(chalk.green('✔ Backup restored successfully.'));
|
|
132
|
-
console.log(chalk.dim(' Note: Restart palexplorer to apply restored settings.'));
|
|
133
|
-
} catch (err) {
|
|
134
|
-
console.log(chalk.red(`Restore failed: ${err.message}`));
|
|
135
|
-
process.exitCode = 1;
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
cmd
|
|
140
|
-
.command('list')
|
|
141
|
-
.description('list available backups')
|
|
142
|
-
.action(async () => {
|
|
143
|
-
try {
|
|
144
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
145
|
-
const store = extConfig.get('ext_store.backup-cloud') || {};
|
|
146
|
-
const history = store.backupHistory || [];
|
|
147
|
-
if (history.length === 0) {
|
|
148
|
-
console.log(chalk.dim('No backups found.'));
|
|
149
|
-
console.log(chalk.dim('
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
console.log(chalk.bold(`Cloud Backups (${history.length})\n`));
|
|
153
|
-
for (const b of history) {
|
|
154
|
-
const statusColor = b.status === 'completed' ? chalk.green : b.status === 'failed' ? chalk.red : chalk.yellow;
|
|
155
|
-
const encrypted = b.encrypted ? chalk.green('🔒') : '';
|
|
156
|
-
console.log(` ${chalk.white(b.id)} ${statusColor(b.status)} ${encrypted}`);
|
|
157
|
-
console.log(` Provider: ${chalk.dim(b.provider)} Created: ${chalk.dim(b.createdAt)}`);
|
|
158
|
-
}
|
|
159
|
-
} catch (err) {
|
|
160
|
-
console.log(chalk.red(`List failed: ${err.message}`));
|
|
161
|
-
process.exitCode = 1;
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
cmd
|
|
166
|
-
.command('delete <id>')
|
|
167
|
-
.description('delete a backup')
|
|
168
|
-
.action(async (id) => {
|
|
169
|
-
try {
|
|
170
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
171
|
-
const store = extConfig.get('ext_store.backup-cloud') || {};
|
|
172
|
-
const history = store.backupHistory || [];
|
|
173
|
-
const idx = history.findIndex(b => b.id === id);
|
|
174
|
-
if (idx === -1) {
|
|
175
|
-
console.log(chalk.red(`Backup not found: ${id}`));
|
|
176
|
-
process.exitCode = 1;
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
const removed = history.splice(idx, 1)[0];
|
|
180
|
-
store.backupHistory = history;
|
|
181
|
-
if (store.lastBackup?.id === id) {
|
|
182
|
-
store.lastBackup = history.length > 0 ? history[history.length - 1] : null;
|
|
183
|
-
}
|
|
184
|
-
extConfig.set('ext_store.backup-cloud', store);
|
|
185
|
-
console.log(chalk.green(`✔ Backup deleted: ${id}`));
|
|
186
|
-
console.log(` Provider: ${chalk.dim(removed.provider)} Created: ${chalk.dim(removed.createdAt)}`);
|
|
187
|
-
} catch (err) {
|
|
188
|
-
console.log(chalk.red(`Delete failed: ${err.message}`));
|
|
189
|
-
process.exitCode = 1;
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
cmd
|
|
194
|
-
.command('status')
|
|
195
|
-
.description('show last backup info, provider, schedule')
|
|
196
|
-
.action(async () => {
|
|
197
|
-
try {
|
|
198
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
199
|
-
const config = extConfig.get('ext.backup-cloud') || {};
|
|
200
|
-
const store = extConfig.get('ext_store.backup-cloud') || {};
|
|
201
|
-
|
|
202
|
-
if (!config.provider) {
|
|
203
|
-
console.log(chalk.dim('Cloud backup not configured.'));
|
|
204
|
-
console.log(chalk.dim('
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
console.log(chalk.bold('Cloud Backup Status\n'));
|
|
209
|
-
console.log(` Provider: ${chalk.cyan(config.provider)}`);
|
|
210
|
-
console.log(` Bucket: ${chalk.white(config.bucket || 'not set')}`);
|
|
211
|
-
if (config.region) console.log(` Region: ${chalk.white(config.region)}`);
|
|
212
|
-
if (config.prefix) console.log(` Prefix: ${chalk.white(config.prefix)}`);
|
|
213
|
-
console.log(` Encryption: ${config.encryptBackups !== false ? chalk.green('enabled') : chalk.yellow('disabled')}`);
|
|
214
|
-
console.log(` Schedule: ${chalk.white(config.schedule || 'manual')}`);
|
|
215
|
-
|
|
216
|
-
const history = store.backupHistory || [];
|
|
217
|
-
console.log(` Backups: ${chalk.white(history.length)}`);
|
|
218
|
-
|
|
219
|
-
if (store.lastBackup) {
|
|
220
|
-
console.log('');
|
|
221
|
-
console.log(chalk.cyan(' Last Backup:'));
|
|
222
|
-
console.log(` ID: ${chalk.white(store.lastBackup.id)}`);
|
|
223
|
-
console.log(` Status: ${store.lastBackup.status === 'completed' ? chalk.green('completed') : chalk.red(store.lastBackup.status)}`);
|
|
224
|
-
console.log(` Created: ${chalk.dim(store.lastBackup.createdAt)}`);
|
|
225
|
-
}
|
|
226
|
-
} catch (err) {
|
|
227
|
-
console.log(chalk.red(`Status failed: ${err.message}`));
|
|
228
|
-
process.exitCode = 1;
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
}
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
const VALID_PROVIDERS = ['s3', 'azure', 'gcs', 'b2'];
|
|
4
|
+
|
|
5
|
+
export default function cloudBackupCommand(program) {
|
|
6
|
+
const cmd = program
|
|
7
|
+
.command('cloud-backup')
|
|
8
|
+
.description('encrypted cloud backup (Pro)')
|
|
9
|
+
.addHelpText('after', `
|
|
10
|
+
Examples:
|
|
11
|
+
$ pal cloud-backup configure --provider s3 --bucket my-backups --region us-east-1
|
|
12
|
+
$ pal cloud-backup create Create a backup now
|
|
13
|
+
$ pal cloud-backup list List available backups
|
|
14
|
+
$ pal cloud-backup restore <id> Restore from a backup
|
|
15
|
+
$ pal cloud-backup delete <id> Delete a backup
|
|
16
|
+
$ pal cloud-backup status Show backup status
|
|
17
|
+
`)
|
|
18
|
+
.action(() => { cmd.outputHelp(); });
|
|
19
|
+
|
|
20
|
+
cmd
|
|
21
|
+
.command('configure')
|
|
22
|
+
.description('set cloud backup provider')
|
|
23
|
+
.requiredOption('--provider <provider>', 'cloud provider (s3|azure|gcs|b2)')
|
|
24
|
+
.option('--bucket <name>', 'bucket name')
|
|
25
|
+
.option('--region <region>', 'AWS region (for S3)')
|
|
26
|
+
.option('--access-key <key>', 'access key')
|
|
27
|
+
.option('--secret-key <key>', 'secret key')
|
|
28
|
+
.option('--prefix <prefix>', 'key prefix')
|
|
29
|
+
.option('--encrypt', 'enable encryption (default)', true)
|
|
30
|
+
.action(async (opts) => {
|
|
31
|
+
try {
|
|
32
|
+
if (!VALID_PROVIDERS.includes(opts.provider)) {
|
|
33
|
+
console.log(chalk.red(`Invalid provider. Must be one of: ${VALID_PROVIDERS.join(', ')}`));
|
|
34
|
+
process.exitCode = 1;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
39
|
+
const existing = extConfig.get('ext.backup-cloud') || {};
|
|
40
|
+
const config = {
|
|
41
|
+
...existing,
|
|
42
|
+
provider: opts.provider,
|
|
43
|
+
encryptBackups: opts.encrypt !== false,
|
|
44
|
+
};
|
|
45
|
+
if (opts.bucket) config.bucket = opts.bucket;
|
|
46
|
+
if (opts.region) config.region = opts.region;
|
|
47
|
+
if (opts.accessKey) config.accessKey = opts.accessKey;
|
|
48
|
+
if (opts.secretKey) {
|
|
49
|
+
try {
|
|
50
|
+
const keytar = (await import('keytar')).default;
|
|
51
|
+
await keytar.setPassword('palexplorer', 'cloudBackup.secretKey', opts.secretKey);
|
|
52
|
+
} catch {
|
|
53
|
+
config.secretKey = opts.secretKey;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (opts.prefix) config.prefix = opts.prefix;
|
|
57
|
+
|
|
58
|
+
extConfig.set('ext.backup-cloud', config);
|
|
59
|
+
console.log(chalk.green(`✔ Cloud backup configured: ${opts.provider}`));
|
|
60
|
+
if (config.bucket) console.log(` Bucket: ${chalk.white(config.bucket)}`);
|
|
61
|
+
if (config.region) console.log(` Region: ${chalk.white(config.region)}`);
|
|
62
|
+
if (config.prefix) console.log(` Prefix: ${chalk.white(config.prefix)}`);
|
|
63
|
+
console.log(` Encryption: ${config.encryptBackups ? chalk.green('enabled') : chalk.yellow('disabled')}`);
|
|
64
|
+
} catch (err) {
|
|
65
|
+
console.log(chalk.red(`Configure failed: ${err.message}`));
|
|
66
|
+
process.exitCode = 1;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
cmd
|
|
71
|
+
.command('create')
|
|
72
|
+
.description('create a backup now')
|
|
73
|
+
.action(async () => {
|
|
74
|
+
try {
|
|
75
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
76
|
+
const config = extConfig.get('ext.backup-cloud') || {};
|
|
77
|
+
if (!config.provider) {
|
|
78
|
+
console.log(chalk.red('No provider configured. Run: pal cloud-backup configure --provider <provider>'));
|
|
79
|
+
process.exitCode = 1;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const backupId = Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
|
|
84
|
+
const backup = {
|
|
85
|
+
id: backupId,
|
|
86
|
+
provider: config.provider,
|
|
87
|
+
bucket: config.bucket,
|
|
88
|
+
encrypted: config.encryptBackups !== false,
|
|
89
|
+
createdAt: new Date().toISOString(),
|
|
90
|
+
size: 0,
|
|
91
|
+
status: 'completed',
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
console.log(chalk.cyan(`Creating backup to ${config.provider}://${config.bucket || 'default'}...`));
|
|
95
|
+
|
|
96
|
+
const store = extConfig.get('ext_store.backup-cloud') || {};
|
|
97
|
+
const history = store.backupHistory || [];
|
|
98
|
+
history.push(backup);
|
|
99
|
+
store.backupHistory = history;
|
|
100
|
+
store.lastBackup = backup;
|
|
101
|
+
extConfig.set('ext_store.backup-cloud', store);
|
|
102
|
+
|
|
103
|
+
console.log(chalk.green(`✔ Backup created: ${backupId}`));
|
|
104
|
+
console.log(` Provider: ${chalk.white(config.provider)}`);
|
|
105
|
+
console.log(` Encrypted: ${backup.encrypted ? chalk.green('yes') : chalk.yellow('no')}`);
|
|
106
|
+
console.log(` Time: ${chalk.dim(backup.createdAt)}`);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
console.log(chalk.red(`Backup failed: ${err.message}`));
|
|
109
|
+
process.exitCode = 1;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
cmd
|
|
114
|
+
.command('restore <id>')
|
|
115
|
+
.description('restore from a backup')
|
|
116
|
+
.action(async (id) => {
|
|
117
|
+
try {
|
|
118
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
119
|
+
const store = extConfig.get('ext_store.backup-cloud') || {};
|
|
120
|
+
const history = store.backupHistory || [];
|
|
121
|
+
const backup = history.find(b => b.id === id);
|
|
122
|
+
if (!backup) {
|
|
123
|
+
console.log(chalk.red(`Backup not found: ${id}`));
|
|
124
|
+
process.exitCode = 1;
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
console.log(chalk.cyan(`Restoring from backup ${id}...`));
|
|
129
|
+
console.log(` Provider: ${chalk.white(backup.provider)}`);
|
|
130
|
+
console.log(` Created: ${chalk.dim(backup.createdAt)}`);
|
|
131
|
+
console.log(chalk.green('✔ Backup restored successfully.'));
|
|
132
|
+
console.log(chalk.dim(' Note: Restart palexplorer to apply restored settings.'));
|
|
133
|
+
} catch (err) {
|
|
134
|
+
console.log(chalk.red(`Restore failed: ${err.message}`));
|
|
135
|
+
process.exitCode = 1;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
cmd
|
|
140
|
+
.command('list')
|
|
141
|
+
.description('list available backups')
|
|
142
|
+
.action(async () => {
|
|
143
|
+
try {
|
|
144
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
145
|
+
const store = extConfig.get('ext_store.backup-cloud') || {};
|
|
146
|
+
const history = store.backupHistory || [];
|
|
147
|
+
if (history.length === 0) {
|
|
148
|
+
console.log(chalk.dim('No backups found.'));
|
|
149
|
+
console.log(chalk.dim(' pal cloud-backup create'));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
console.log(chalk.bold(`Cloud Backups (${history.length})\n`));
|
|
153
|
+
for (const b of history) {
|
|
154
|
+
const statusColor = b.status === 'completed' ? chalk.green : b.status === 'failed' ? chalk.red : chalk.yellow;
|
|
155
|
+
const encrypted = b.encrypted ? chalk.green('🔒') : '';
|
|
156
|
+
console.log(` ${chalk.white(b.id)} ${statusColor(b.status)} ${encrypted}`);
|
|
157
|
+
console.log(` Provider: ${chalk.dim(b.provider)} Created: ${chalk.dim(b.createdAt)}`);
|
|
158
|
+
}
|
|
159
|
+
} catch (err) {
|
|
160
|
+
console.log(chalk.red(`List failed: ${err.message}`));
|
|
161
|
+
process.exitCode = 1;
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
cmd
|
|
166
|
+
.command('delete <id>')
|
|
167
|
+
.description('delete a backup')
|
|
168
|
+
.action(async (id) => {
|
|
169
|
+
try {
|
|
170
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
171
|
+
const store = extConfig.get('ext_store.backup-cloud') || {};
|
|
172
|
+
const history = store.backupHistory || [];
|
|
173
|
+
const idx = history.findIndex(b => b.id === id);
|
|
174
|
+
if (idx === -1) {
|
|
175
|
+
console.log(chalk.red(`Backup not found: ${id}`));
|
|
176
|
+
process.exitCode = 1;
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const removed = history.splice(idx, 1)[0];
|
|
180
|
+
store.backupHistory = history;
|
|
181
|
+
if (store.lastBackup?.id === id) {
|
|
182
|
+
store.lastBackup = history.length > 0 ? history[history.length - 1] : null;
|
|
183
|
+
}
|
|
184
|
+
extConfig.set('ext_store.backup-cloud', store);
|
|
185
|
+
console.log(chalk.green(`✔ Backup deleted: ${id}`));
|
|
186
|
+
console.log(` Provider: ${chalk.dim(removed.provider)} Created: ${chalk.dim(removed.createdAt)}`);
|
|
187
|
+
} catch (err) {
|
|
188
|
+
console.log(chalk.red(`Delete failed: ${err.message}`));
|
|
189
|
+
process.exitCode = 1;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
cmd
|
|
194
|
+
.command('status')
|
|
195
|
+
.description('show last backup info, provider, schedule')
|
|
196
|
+
.action(async () => {
|
|
197
|
+
try {
|
|
198
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
199
|
+
const config = extConfig.get('ext.backup-cloud') || {};
|
|
200
|
+
const store = extConfig.get('ext_store.backup-cloud') || {};
|
|
201
|
+
|
|
202
|
+
if (!config.provider) {
|
|
203
|
+
console.log(chalk.dim('Cloud backup not configured.'));
|
|
204
|
+
console.log(chalk.dim(' pal cloud-backup configure --provider <provider>'));
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
console.log(chalk.bold('Cloud Backup Status\n'));
|
|
209
|
+
console.log(` Provider: ${chalk.cyan(config.provider)}`);
|
|
210
|
+
console.log(` Bucket: ${chalk.white(config.bucket || 'not set')}`);
|
|
211
|
+
if (config.region) console.log(` Region: ${chalk.white(config.region)}`);
|
|
212
|
+
if (config.prefix) console.log(` Prefix: ${chalk.white(config.prefix)}`);
|
|
213
|
+
console.log(` Encryption: ${config.encryptBackups !== false ? chalk.green('enabled') : chalk.yellow('disabled')}`);
|
|
214
|
+
console.log(` Schedule: ${chalk.white(config.schedule || 'manual')}`);
|
|
215
|
+
|
|
216
|
+
const history = store.backupHistory || [];
|
|
217
|
+
console.log(` Backups: ${chalk.white(history.length)}`);
|
|
218
|
+
|
|
219
|
+
if (store.lastBackup) {
|
|
220
|
+
console.log('');
|
|
221
|
+
console.log(chalk.cyan(' Last Backup:'));
|
|
222
|
+
console.log(` ID: ${chalk.white(store.lastBackup.id)}`);
|
|
223
|
+
console.log(` Status: ${store.lastBackup.status === 'completed' ? chalk.green('completed') : chalk.red(store.lastBackup.status)}`);
|
|
224
|
+
console.log(` Created: ${chalk.dim(store.lastBackup.createdAt)}`);
|
|
225
|
+
}
|
|
226
|
+
} catch (err) {
|
|
227
|
+
console.log(chalk.red(`Status failed: ${err.message}`));
|
|
228
|
+
process.exitCode = 1;
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
package/lib/commands/comment.js
CHANGED
|
@@ -1,99 +1,99 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import config from '../utils/config.js';
|
|
3
|
-
import {
|
|
4
|
-
getShareComments,
|
|
5
|
-
addShareComment,
|
|
6
|
-
deleteShareComment
|
|
7
|
-
} from '../core/groups.js';
|
|
8
|
-
import { checkLimit } from '../core/pro.js';
|
|
9
|
-
|
|
10
|
-
export default function commentCommand(program) {
|
|
11
|
-
const cmd = program
|
|
12
|
-
.command('comment')
|
|
13
|
-
.description('manage comments on shares')
|
|
14
|
-
.addHelpText('after', `
|
|
15
|
-
Examples:
|
|
16
|
-
$
|
|
17
|
-
$
|
|
18
|
-
$
|
|
19
|
-
`);
|
|
20
|
-
|
|
21
|
-
cmd
|
|
22
|
-
.command('list <shareId>')
|
|
23
|
-
.description('list comments on a share')
|
|
24
|
-
.action((shareId) => {
|
|
25
|
-
const comments = getShareComments(shareId);
|
|
26
|
-
if (comments.length === 0) {
|
|
27
|
-
console.log(chalk.gray('No comments on this share.'));
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
console.log('');
|
|
31
|
-
console.log(chalk.cyan(`Comments on ${shareId}:`));
|
|
32
|
-
for (const c of comments) {
|
|
33
|
-
const time = new Date(c.createdAt).toLocaleString();
|
|
34
|
-
const author = c.authorHandle ? `@${c.authorHandle}` : c.authorName || 'unknown';
|
|
35
|
-
console.log(` ${chalk.gray(c.id.slice(0, 8))} ${chalk.white(author)} ${chalk.gray(time)}`);
|
|
36
|
-
console.log(` ${c.text}`);
|
|
37
|
-
}
|
|
38
|
-
console.log('');
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
cmd
|
|
42
|
-
.command('add <shareId> <text>')
|
|
43
|
-
.description('add a comment to a share')
|
|
44
|
-
.action((shareId, text) => {
|
|
45
|
-
if (!text || !text.trim()) {
|
|
46
|
-
console.log(chalk.red('Error: comment text cannot be empty.'));
|
|
47
|
-
process.exitCode = 1;
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
try {
|
|
51
|
-
const comments = getShareComments(shareId);
|
|
52
|
-
checkLimit('maxCommentsPerShare', comments.length);
|
|
53
|
-
|
|
54
|
-
const identity = config.get('identity');
|
|
55
|
-
const comment = addShareComment(shareId, {
|
|
56
|
-
authorHandle: identity?.handle || null,
|
|
57
|
-
authorName: identity?.name || 'Anonymous',
|
|
58
|
-
text,
|
|
59
|
-
});
|
|
60
|
-
console.log(chalk.green(`Comment added.`));
|
|
61
|
-
console.log(chalk.gray(`ID: ${comment.id}`));
|
|
62
|
-
} catch (err) {
|
|
63
|
-
console.log(chalk.red(err.message));
|
|
64
|
-
process.exitCode = 1;
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
cmd
|
|
69
|
-
.command('delete <shareId> <commentId>')
|
|
70
|
-
.description('delete a comment from a share')
|
|
71
|
-
.action((shareId, commentId) => {
|
|
72
|
-
try {
|
|
73
|
-
const identity = config.get('identity');
|
|
74
|
-
const comments = getShareComments(shareId);
|
|
75
|
-
const comment = comments.find(c => c.id === commentId);
|
|
76
|
-
if (!comment) {
|
|
77
|
-
console.log(chalk.red('Comment not found.'));
|
|
78
|
-
process.exitCode = 1;
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
const myHandle = identity?.handle || null;
|
|
82
|
-
const myName = identity?.name || 'Anonymous';
|
|
83
|
-
if (comment.authorHandle !== myHandle && comment.authorName !== myName) {
|
|
84
|
-
console.log(chalk.red('Error: you can only delete your own comments.'));
|
|
85
|
-
process.exitCode = 1;
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
deleteShareComment(shareId, commentId);
|
|
89
|
-
console.log(chalk.green('Comment deleted.'));
|
|
90
|
-
} catch (err) {
|
|
91
|
-
console.log(chalk.red(err.message));
|
|
92
|
-
process.exitCode = 1;
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
cmd.action(() => {
|
|
97
|
-
console.log(chalk.gray('Use `
|
|
98
|
-
});
|
|
99
|
-
}
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import config from '../utils/config.js';
|
|
3
|
+
import {
|
|
4
|
+
getShareComments,
|
|
5
|
+
addShareComment,
|
|
6
|
+
deleteShareComment
|
|
7
|
+
} from '../core/groups.js';
|
|
8
|
+
import { checkLimit } from '../core/pro.js';
|
|
9
|
+
|
|
10
|
+
export default function commentCommand(program) {
|
|
11
|
+
const cmd = program
|
|
12
|
+
.command('comment')
|
|
13
|
+
.description('manage comments on shares')
|
|
14
|
+
.addHelpText('after', `
|
|
15
|
+
Examples:
|
|
16
|
+
$ pal comment list <shareId> List comments on a share
|
|
17
|
+
$ pal comment add <shareId> "nice files!" Add a comment
|
|
18
|
+
$ pal comment delete <shareId> <commentId> Delete a comment
|
|
19
|
+
`);
|
|
20
|
+
|
|
21
|
+
cmd
|
|
22
|
+
.command('list <shareId>')
|
|
23
|
+
.description('list comments on a share')
|
|
24
|
+
.action((shareId) => {
|
|
25
|
+
const comments = getShareComments(shareId);
|
|
26
|
+
if (comments.length === 0) {
|
|
27
|
+
console.log(chalk.gray('No comments on this share.'));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
console.log('');
|
|
31
|
+
console.log(chalk.cyan(`Comments on ${shareId}:`));
|
|
32
|
+
for (const c of comments) {
|
|
33
|
+
const time = new Date(c.createdAt).toLocaleString();
|
|
34
|
+
const author = c.authorHandle ? `@${c.authorHandle}` : c.authorName || 'unknown';
|
|
35
|
+
console.log(` ${chalk.gray(c.id.slice(0, 8))} ${chalk.white(author)} ${chalk.gray(time)}`);
|
|
36
|
+
console.log(` ${c.text}`);
|
|
37
|
+
}
|
|
38
|
+
console.log('');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
cmd
|
|
42
|
+
.command('add <shareId> <text>')
|
|
43
|
+
.description('add a comment to a share')
|
|
44
|
+
.action((shareId, text) => {
|
|
45
|
+
if (!text || !text.trim()) {
|
|
46
|
+
console.log(chalk.red('Error: comment text cannot be empty.'));
|
|
47
|
+
process.exitCode = 1;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const comments = getShareComments(shareId);
|
|
52
|
+
checkLimit('maxCommentsPerShare', comments.length);
|
|
53
|
+
|
|
54
|
+
const identity = config.get('identity');
|
|
55
|
+
const comment = addShareComment(shareId, {
|
|
56
|
+
authorHandle: identity?.handle || null,
|
|
57
|
+
authorName: identity?.name || 'Anonymous',
|
|
58
|
+
text,
|
|
59
|
+
});
|
|
60
|
+
console.log(chalk.green(`Comment added.`));
|
|
61
|
+
console.log(chalk.gray(`ID: ${comment.id}`));
|
|
62
|
+
} catch (err) {
|
|
63
|
+
console.log(chalk.red(err.message));
|
|
64
|
+
process.exitCode = 1;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
cmd
|
|
69
|
+
.command('delete <shareId> <commentId>')
|
|
70
|
+
.description('delete a comment from a share')
|
|
71
|
+
.action((shareId, commentId) => {
|
|
72
|
+
try {
|
|
73
|
+
const identity = config.get('identity');
|
|
74
|
+
const comments = getShareComments(shareId);
|
|
75
|
+
const comment = comments.find(c => c.id === commentId);
|
|
76
|
+
if (!comment) {
|
|
77
|
+
console.log(chalk.red('Comment not found.'));
|
|
78
|
+
process.exitCode = 1;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const myHandle = identity?.handle || null;
|
|
82
|
+
const myName = identity?.name || 'Anonymous';
|
|
83
|
+
if (comment.authorHandle !== myHandle && comment.authorName !== myName) {
|
|
84
|
+
console.log(chalk.red('Error: you can only delete your own comments.'));
|
|
85
|
+
process.exitCode = 1;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
deleteShareComment(shareId, commentId);
|
|
89
|
+
console.log(chalk.green('Comment deleted.'));
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.log(chalk.red(err.message));
|
|
92
|
+
process.exitCode = 1;
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
cmd.action(() => {
|
|
97
|
+
console.log(chalk.gray('Use `pal comment list <shareId>` to view comments.'));
|
|
98
|
+
});
|
|
99
|
+
}
|