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,216 +1,216 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { listShares, updateShare, getShareSummary, VISIBILITY_LEVELS } from '../core/shares.js';
|
|
3
|
-
import { getGroups } from '../core/groups.js';
|
|
4
|
-
import config from '../utils/config.js';
|
|
5
|
-
import path from 'path';
|
|
6
|
-
|
|
7
|
-
const VIS_COLORS = {
|
|
8
|
-
public: 'green', global: 'green', private: 'red',
|
|
9
|
-
group: 'blue', network: 'cyan', 'link-only': 'yellow',
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
function visLabel(v) {
|
|
13
|
-
const color = VIS_COLORS[v] || 'white';
|
|
14
|
-
return chalk[color](v.toUpperCase());
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export default function permissionsCommand(program) {
|
|
18
|
-
const cmd = program
|
|
19
|
-
.command('permissions')
|
|
20
|
-
.description('unified view of all shares and their permissions')
|
|
21
|
-
.addHelpText('after', `
|
|
22
|
-
Examples:
|
|
23
|
-
$
|
|
24
|
-
$
|
|
25
|
-
$
|
|
26
|
-
$
|
|
27
|
-
$
|
|
28
|
-
$
|
|
29
|
-
`)
|
|
30
|
-
.option('--compact', 'One-line-per-share view')
|
|
31
|
-
.option('--json', 'Output as JSON')
|
|
32
|
-
.action((opts) => {
|
|
33
|
-
const summary = getShareSummary();
|
|
34
|
-
const groups = getGroups();
|
|
35
|
-
|
|
36
|
-
if (opts.json) {
|
|
37
|
-
console.log(JSON.stringify(summary, null, 2));
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (summary.length === 0) {
|
|
42
|
-
console.log(chalk.gray('No active shares.'));
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
console.log('');
|
|
47
|
-
console.log(chalk.bold(` Shares & Permissions (${summary.length} total)`));
|
|
48
|
-
console.log(chalk.gray(' ─'.repeat(30)));
|
|
49
|
-
console.log('');
|
|
50
|
-
|
|
51
|
-
if (opts.compact) {
|
|
52
|
-
// Compact table view
|
|
53
|
-
const maxName = Math.max(12, ...summary.map(s => s.name.length));
|
|
54
|
-
const header = ` ${'NAME'.padEnd(maxName)} ${'VISIBILITY'.padEnd(12)} ${'STREAM'.padEnd(6)} ${'RECIPIENTS'.padEnd(24)} ID`;
|
|
55
|
-
console.log(chalk.gray(header));
|
|
56
|
-
console.log(chalk.gray(' ' + '─'.repeat(header.length)));
|
|
57
|
-
|
|
58
|
-
for (const s of summary) {
|
|
59
|
-
const recips = [];
|
|
60
|
-
if (s.recipients.length) recips.push(s.recipients.join(', '));
|
|
61
|
-
if (s.groups.length) {
|
|
62
|
-
const gNames = s.groups.map(gId => groups.find(g => g.id === gId)?.name || gId);
|
|
63
|
-
recips.push(`[${gNames.join(', ')}]`);
|
|
64
|
-
}
|
|
65
|
-
if (s.networks.length) recips.push(`{${s.networks.join(', ')}}`);
|
|
66
|
-
const recipStr = recips.join(', ') || chalk.gray('everyone');
|
|
67
|
-
|
|
68
|
-
console.log(` ${chalk.white(s.name.padEnd(maxName))} ${visLabel(s.visibility).padEnd(12 + 10)} ${s.streamable ? chalk.magenta('yes') : chalk.gray('no ').padEnd(6)} ${recipStr.substring(0, 24).padEnd(24)} ${chalk.gray(s.id)}`);
|
|
69
|
-
}
|
|
70
|
-
} else {
|
|
71
|
-
// Detailed view
|
|
72
|
-
for (const s of summary) {
|
|
73
|
-
const gNames = s.groups.map(gId => groups.find(g => g.id === gId)?.name || gId);
|
|
74
|
-
|
|
75
|
-
console.log(` ${chalk.bold.white(s.name)} ${chalk.gray(`(${s.id})`)}`);
|
|
76
|
-
console.log(` Path: ${chalk.gray(s.path)}`);
|
|
77
|
-
console.log(` Visibility: ${visLabel(s.visibility)}`);
|
|
78
|
-
console.log(` Streamable: ${s.streamable ? chalk.magenta('Yes — media streaming enabled') : chalk.gray('No')}`);
|
|
79
|
-
|
|
80
|
-
if (s.visibility === 'public' || s.visibility === 'global') {
|
|
81
|
-
console.log(` Access: ${chalk.green('Everyone')}`);
|
|
82
|
-
} else if (s.visibility === 'group' && gNames.length) {
|
|
83
|
-
console.log(` Groups: ${chalk.blue(gNames.join(', '))}`);
|
|
84
|
-
} else if (s.visibility === 'network' && s.networks.length) {
|
|
85
|
-
console.log(` Networks: ${chalk.cyan(s.networks.join(', '))}`);
|
|
86
|
-
} else if (s.visibility === 'link-only') {
|
|
87
|
-
console.log(` Access: ${chalk.yellow('Link-only (anyone with the link)')}`);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (s.recipients.length) {
|
|
91
|
-
console.log(` Recipients: ${chalk.white(s.recipients.join(', '))}`);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
console.log(` Recursive: ${s.recursive ? 'Yes' : 'No'}`);
|
|
95
|
-
if (s.hasMagnet) console.log(` Magnet: ${chalk.green('Active')}`);
|
|
96
|
-
if (s.hasPassword) console.log(` Password: ${chalk.yellow('Protected')}`);
|
|
97
|
-
console.log('');
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
cmd
|
|
103
|
-
.command('set <id>')
|
|
104
|
-
.description('edit share permissions')
|
|
105
|
-
.option('--visibility <type>', `Set visibility: ${VISIBILITY_LEVELS.join(', ')}`)
|
|
106
|
-
.option('--streamable', 'Enable media streaming for this share')
|
|
107
|
-
.option('--no-streamable', 'Disable media streaming')
|
|
108
|
-
.option('--add-pal <name>', 'Add a pal as recipient')
|
|
109
|
-
.option('--remove-pal <name>', 'Remove a pal from recipients')
|
|
110
|
-
.option('--add-group <name>', 'Add a group')
|
|
111
|
-
.option('--remove-group <name>', 'Remove a group')
|
|
112
|
-
.option('--add-network <id>', 'Add a network')
|
|
113
|
-
.option('--remove-network <id>', 'Remove a network')
|
|
114
|
-
.option('--recursive', 'Enable recursive sharing')
|
|
115
|
-
.option('--no-recursive', 'Disable recursive sharing')
|
|
116
|
-
.option('--writable', 'Allow recipients to write/modify files')
|
|
117
|
-
.option('--read-only', 'Restrict recipients to read-only access')
|
|
118
|
-
.action(async (id, opts) => {
|
|
119
|
-
try {
|
|
120
|
-
const shares = listShares();
|
|
121
|
-
const share = shares.find(s => s.id === id || s.path === path.resolve(id));
|
|
122
|
-
if (!share) {
|
|
123
|
-
console.log(chalk.red('Share not found.'));
|
|
124
|
-
process.exitCode = 1;
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const updates = {};
|
|
129
|
-
|
|
130
|
-
if (opts.visibility) {
|
|
131
|
-
if (!VISIBILITY_LEVELS.includes(opts.visibility)) {
|
|
132
|
-
console.log(chalk.red(`Invalid visibility. Options: ${VISIBILITY_LEVELS.join(', ')}`));
|
|
133
|
-
process.exitCode = 1;
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
updates.visibility = opts.visibility;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (opts.streamable !== undefined) {
|
|
140
|
-
updates.streamable = opts.streamable;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (opts.recursive !== undefined) {
|
|
144
|
-
updates.recursive = opts.recursive;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (opts.writable) {
|
|
148
|
-
updates.permissions = { ...share.permissions, write: true };
|
|
149
|
-
}
|
|
150
|
-
if (opts.readOnly) {
|
|
151
|
-
updates.permissions = { ...share.permissions, write: false };
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (opts.addPal) {
|
|
155
|
-
const { getFriends } = await import('../core/users.js');
|
|
156
|
-
const friends = getFriends();
|
|
157
|
-
const pal = friends.find(f => f.name === opts.addPal || f.handle === opts.addPal);
|
|
158
|
-
if (!pal) { console.log(chalk.red(`Pal '${opts.addPal}' not found.`)); process.exitCode = 1; return; }
|
|
159
|
-
const recipients = share.recipients || [];
|
|
160
|
-
if (!recipients.find(r => r.id === pal.id)) {
|
|
161
|
-
recipients.push({ id: pal.id, name: pal.name, handle: pal.handle });
|
|
162
|
-
updates.recipients = recipients;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (opts.removePal) {
|
|
167
|
-
const recipients = (share.recipients || []).filter(
|
|
168
|
-
r => r.name !== opts.removePal && r.handle !== opts.removePal
|
|
169
|
-
);
|
|
170
|
-
updates.recipients = recipients;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (opts.addGroup) {
|
|
174
|
-
const { getGroup } = await import('../core/groups.js');
|
|
175
|
-
const group = getGroup(opts.addGroup);
|
|
176
|
-
if (!group) { console.log(chalk.red(`Group '${opts.addGroup}' not found.`)); process.exitCode = 1; return; }
|
|
177
|
-
const groups = share.sharedWithGroups || [];
|
|
178
|
-
if (!groups.includes(group.id)) {
|
|
179
|
-
groups.push(group.id);
|
|
180
|
-
updates.sharedWithGroups = groups;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (opts.removeGroup) {
|
|
185
|
-
const { getGroup } = await import('../core/groups.js');
|
|
186
|
-
const group = getGroup(opts.removeGroup);
|
|
187
|
-
updates.sharedWithGroups = (share.sharedWithGroups || []).filter(gId => gId !== group?.id);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (opts.addNetwork) {
|
|
191
|
-
const networks = share.sharedWithNetworks || [];
|
|
192
|
-
if (!networks.includes(opts.addNetwork)) networks.push(opts.addNetwork);
|
|
193
|
-
updates.sharedWithNetworks = networks;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (opts.removeNetwork) {
|
|
197
|
-
updates.sharedWithNetworks = (share.sharedWithNetworks || []).filter(n => n !== opts.removeNetwork);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (Object.keys(updates).length === 0) {
|
|
201
|
-
console.log(chalk.yellow('No changes specified.'));
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const updated = updateShare(id, updates);
|
|
206
|
-
console.log(chalk.green(`Share updated: ${updated.name || path.basename(updated.path)}`));
|
|
207
|
-
|
|
208
|
-
for (const [key, value] of Object.entries(updates)) {
|
|
209
|
-
console.log(chalk.gray(` ${key}: ${JSON.stringify(value)}`));
|
|
210
|
-
}
|
|
211
|
-
} catch (err) {
|
|
212
|
-
console.log(chalk.red(`Error: ${err.message}`));
|
|
213
|
-
process.exitCode = 1;
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
}
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { listShares, updateShare, getShareSummary, VISIBILITY_LEVELS } from '../core/shares.js';
|
|
3
|
+
import { getGroups } from '../core/groups.js';
|
|
4
|
+
import config from '../utils/config.js';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
|
|
7
|
+
const VIS_COLORS = {
|
|
8
|
+
public: 'green', global: 'green', private: 'red',
|
|
9
|
+
group: 'blue', network: 'cyan', 'link-only': 'yellow',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function visLabel(v) {
|
|
13
|
+
const color = VIS_COLORS[v] || 'white';
|
|
14
|
+
return chalk[color](v.toUpperCase());
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default function permissionsCommand(program) {
|
|
18
|
+
const cmd = program
|
|
19
|
+
.command('permissions')
|
|
20
|
+
.description('unified view of all shares and their permissions')
|
|
21
|
+
.addHelpText('after', `
|
|
22
|
+
Examples:
|
|
23
|
+
$ pal permissions Show all shares with permissions
|
|
24
|
+
$ pal permissions --compact Compact one-line-per-share view
|
|
25
|
+
$ pal permissions set <id> --visibility group --group team
|
|
26
|
+
$ pal permissions set <id> --streamable Mark share as streamable media
|
|
27
|
+
$ pal permissions set <id> --add-pal alice
|
|
28
|
+
$ pal permissions set <id> --remove-pal bob
|
|
29
|
+
`)
|
|
30
|
+
.option('--compact', 'One-line-per-share view')
|
|
31
|
+
.option('--json', 'Output as JSON')
|
|
32
|
+
.action((opts) => {
|
|
33
|
+
const summary = getShareSummary();
|
|
34
|
+
const groups = getGroups();
|
|
35
|
+
|
|
36
|
+
if (opts.json) {
|
|
37
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (summary.length === 0) {
|
|
42
|
+
console.log(chalk.gray('No active shares.'));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log('');
|
|
47
|
+
console.log(chalk.bold(` Shares & Permissions (${summary.length} total)`));
|
|
48
|
+
console.log(chalk.gray(' ─'.repeat(30)));
|
|
49
|
+
console.log('');
|
|
50
|
+
|
|
51
|
+
if (opts.compact) {
|
|
52
|
+
// Compact table view
|
|
53
|
+
const maxName = Math.max(12, ...summary.map(s => s.name.length));
|
|
54
|
+
const header = ` ${'NAME'.padEnd(maxName)} ${'VISIBILITY'.padEnd(12)} ${'STREAM'.padEnd(6)} ${'RECIPIENTS'.padEnd(24)} ID`;
|
|
55
|
+
console.log(chalk.gray(header));
|
|
56
|
+
console.log(chalk.gray(' ' + '─'.repeat(header.length)));
|
|
57
|
+
|
|
58
|
+
for (const s of summary) {
|
|
59
|
+
const recips = [];
|
|
60
|
+
if (s.recipients.length) recips.push(s.recipients.join(', '));
|
|
61
|
+
if (s.groups.length) {
|
|
62
|
+
const gNames = s.groups.map(gId => groups.find(g => g.id === gId)?.name || gId);
|
|
63
|
+
recips.push(`[${gNames.join(', ')}]`);
|
|
64
|
+
}
|
|
65
|
+
if (s.networks.length) recips.push(`{${s.networks.join(', ')}}`);
|
|
66
|
+
const recipStr = recips.join(', ') || chalk.gray('everyone');
|
|
67
|
+
|
|
68
|
+
console.log(` ${chalk.white(s.name.padEnd(maxName))} ${visLabel(s.visibility).padEnd(12 + 10)} ${s.streamable ? chalk.magenta('yes') : chalk.gray('no ').padEnd(6)} ${recipStr.substring(0, 24).padEnd(24)} ${chalk.gray(s.id)}`);
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
// Detailed view
|
|
72
|
+
for (const s of summary) {
|
|
73
|
+
const gNames = s.groups.map(gId => groups.find(g => g.id === gId)?.name || gId);
|
|
74
|
+
|
|
75
|
+
console.log(` ${chalk.bold.white(s.name)} ${chalk.gray(`(${s.id})`)}`);
|
|
76
|
+
console.log(` Path: ${chalk.gray(s.path)}`);
|
|
77
|
+
console.log(` Visibility: ${visLabel(s.visibility)}`);
|
|
78
|
+
console.log(` Streamable: ${s.streamable ? chalk.magenta('Yes — media streaming enabled') : chalk.gray('No')}`);
|
|
79
|
+
|
|
80
|
+
if (s.visibility === 'public' || s.visibility === 'global') {
|
|
81
|
+
console.log(` Access: ${chalk.green('Everyone')}`);
|
|
82
|
+
} else if (s.visibility === 'group' && gNames.length) {
|
|
83
|
+
console.log(` Groups: ${chalk.blue(gNames.join(', '))}`);
|
|
84
|
+
} else if (s.visibility === 'network' && s.networks.length) {
|
|
85
|
+
console.log(` Networks: ${chalk.cyan(s.networks.join(', '))}`);
|
|
86
|
+
} else if (s.visibility === 'link-only') {
|
|
87
|
+
console.log(` Access: ${chalk.yellow('Link-only (anyone with the link)')}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (s.recipients.length) {
|
|
91
|
+
console.log(` Recipients: ${chalk.white(s.recipients.join(', '))}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
console.log(` Recursive: ${s.recursive ? 'Yes' : 'No'}`);
|
|
95
|
+
if (s.hasMagnet) console.log(` Magnet: ${chalk.green('Active')}`);
|
|
96
|
+
if (s.hasPassword) console.log(` Password: ${chalk.yellow('Protected')}`);
|
|
97
|
+
console.log('');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
cmd
|
|
103
|
+
.command('set <id>')
|
|
104
|
+
.description('edit share permissions')
|
|
105
|
+
.option('--visibility <type>', `Set visibility: ${VISIBILITY_LEVELS.join(', ')}`)
|
|
106
|
+
.option('--streamable', 'Enable media streaming for this share')
|
|
107
|
+
.option('--no-streamable', 'Disable media streaming')
|
|
108
|
+
.option('--add-pal <name>', 'Add a pal as recipient')
|
|
109
|
+
.option('--remove-pal <name>', 'Remove a pal from recipients')
|
|
110
|
+
.option('--add-group <name>', 'Add a group')
|
|
111
|
+
.option('--remove-group <name>', 'Remove a group')
|
|
112
|
+
.option('--add-network <id>', 'Add a network')
|
|
113
|
+
.option('--remove-network <id>', 'Remove a network')
|
|
114
|
+
.option('--recursive', 'Enable recursive sharing')
|
|
115
|
+
.option('--no-recursive', 'Disable recursive sharing')
|
|
116
|
+
.option('--writable', 'Allow recipients to write/modify files')
|
|
117
|
+
.option('--read-only', 'Restrict recipients to read-only access')
|
|
118
|
+
.action(async (id, opts) => {
|
|
119
|
+
try {
|
|
120
|
+
const shares = listShares();
|
|
121
|
+
const share = shares.find(s => s.id === id || s.path === path.resolve(id));
|
|
122
|
+
if (!share) {
|
|
123
|
+
console.log(chalk.red('Share not found.'));
|
|
124
|
+
process.exitCode = 1;
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const updates = {};
|
|
129
|
+
|
|
130
|
+
if (opts.visibility) {
|
|
131
|
+
if (!VISIBILITY_LEVELS.includes(opts.visibility)) {
|
|
132
|
+
console.log(chalk.red(`Invalid visibility. Options: ${VISIBILITY_LEVELS.join(', ')}`));
|
|
133
|
+
process.exitCode = 1;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
updates.visibility = opts.visibility;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (opts.streamable !== undefined) {
|
|
140
|
+
updates.streamable = opts.streamable;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (opts.recursive !== undefined) {
|
|
144
|
+
updates.recursive = opts.recursive;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (opts.writable) {
|
|
148
|
+
updates.permissions = { ...share.permissions, write: true };
|
|
149
|
+
}
|
|
150
|
+
if (opts.readOnly) {
|
|
151
|
+
updates.permissions = { ...share.permissions, write: false };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (opts.addPal) {
|
|
155
|
+
const { getFriends } = await import('../core/users.js');
|
|
156
|
+
const friends = getFriends();
|
|
157
|
+
const pal = friends.find(f => f.name === opts.addPal || f.handle === opts.addPal);
|
|
158
|
+
if (!pal) { console.log(chalk.red(`Pal '${opts.addPal}' not found.`)); process.exitCode = 1; return; }
|
|
159
|
+
const recipients = share.recipients || [];
|
|
160
|
+
if (!recipients.find(r => r.id === pal.id)) {
|
|
161
|
+
recipients.push({ id: pal.id, name: pal.name, handle: pal.handle });
|
|
162
|
+
updates.recipients = recipients;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (opts.removePal) {
|
|
167
|
+
const recipients = (share.recipients || []).filter(
|
|
168
|
+
r => r.name !== opts.removePal && r.handle !== opts.removePal
|
|
169
|
+
);
|
|
170
|
+
updates.recipients = recipients;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (opts.addGroup) {
|
|
174
|
+
const { getGroup } = await import('../core/groups.js');
|
|
175
|
+
const group = getGroup(opts.addGroup);
|
|
176
|
+
if (!group) { console.log(chalk.red(`Group '${opts.addGroup}' not found.`)); process.exitCode = 1; return; }
|
|
177
|
+
const groups = share.sharedWithGroups || [];
|
|
178
|
+
if (!groups.includes(group.id)) {
|
|
179
|
+
groups.push(group.id);
|
|
180
|
+
updates.sharedWithGroups = groups;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (opts.removeGroup) {
|
|
185
|
+
const { getGroup } = await import('../core/groups.js');
|
|
186
|
+
const group = getGroup(opts.removeGroup);
|
|
187
|
+
updates.sharedWithGroups = (share.sharedWithGroups || []).filter(gId => gId !== group?.id);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (opts.addNetwork) {
|
|
191
|
+
const networks = share.sharedWithNetworks || [];
|
|
192
|
+
if (!networks.includes(opts.addNetwork)) networks.push(opts.addNetwork);
|
|
193
|
+
updates.sharedWithNetworks = networks;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (opts.removeNetwork) {
|
|
197
|
+
updates.sharedWithNetworks = (share.sharedWithNetworks || []).filter(n => n !== opts.removeNetwork);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (Object.keys(updates).length === 0) {
|
|
201
|
+
console.log(chalk.yellow('No changes specified.'));
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const updated = updateShare(id, updates);
|
|
206
|
+
console.log(chalk.green(`Share updated: ${updated.name || path.basename(updated.path)}`));
|
|
207
|
+
|
|
208
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
209
|
+
console.log(chalk.gray(` ${key}: ${JSON.stringify(value)}`));
|
|
210
|
+
}
|
|
211
|
+
} catch (err) {
|
|
212
|
+
console.log(chalk.red(`Error: ${err.message}`));
|
|
213
|
+
process.exitCode = 1;
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
package/lib/commands/pin.js
CHANGED
|
@@ -1,97 +1,97 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import crypto from 'crypto';
|
|
3
|
-
import config from '../utils/config.js';
|
|
4
|
-
import readline from 'readline';
|
|
5
|
-
|
|
6
|
-
function promptPin(prompt) {
|
|
7
|
-
return new Promise((resolve) => {
|
|
8
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
9
|
-
rl.question(prompt, (answer) => {
|
|
10
|
-
rl.close();
|
|
11
|
-
resolve(answer.trim());
|
|
12
|
-
});
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export default function pinCommand(program) {
|
|
17
|
-
const cmd = program
|
|
18
|
-
.command('pin')
|
|
19
|
-
.description('manage PIN lock for app security')
|
|
20
|
-
.addHelpText('after', `
|
|
21
|
-
Examples:
|
|
22
|
-
$
|
|
23
|
-
$
|
|
24
|
-
$
|
|
25
|
-
`)
|
|
26
|
-
.action(() => {
|
|
27
|
-
showPinStatus();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
cmd
|
|
31
|
-
.command('status')
|
|
32
|
-
.description('show if PIN lock is enabled')
|
|
33
|
-
.action(() => {
|
|
34
|
-
showPinStatus();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
cmd
|
|
38
|
-
.command('set')
|
|
39
|
-
.description('set or update PIN lock')
|
|
40
|
-
.action(async () => {
|
|
41
|
-
const pin = await promptPin('Enter new PIN: ');
|
|
42
|
-
if (!pin || pin.length < 4) {
|
|
43
|
-
console.log(chalk.red('PIN must be at least 4 characters.'));
|
|
44
|
-
process.exitCode = 1;
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
const confirm = await promptPin('Confirm PIN: ');
|
|
48
|
-
if (pin !== confirm) {
|
|
49
|
-
console.log(chalk.red('PINs do not match.'));
|
|
50
|
-
process.exitCode = 1;
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
const salt = crypto.randomBytes(16).toString('hex');
|
|
54
|
-
const hash = crypto.createHash('sha256').update(salt + pin).digest('hex');
|
|
55
|
-
config.set('pinHash', JSON.stringify({ salt, hash }));
|
|
56
|
-
console.log(chalk.green('\u2714 PIN lock enabled.'));
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
cmd
|
|
60
|
-
.command('remove')
|
|
61
|
-
.description('remove PIN lock')
|
|
62
|
-
.action(async () => {
|
|
63
|
-
const existing = config.get('pinHash');
|
|
64
|
-
if (!existing) {
|
|
65
|
-
console.log(chalk.gray('No PIN is set.'));
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
const pin = await promptPin('Enter current PIN to remove: ');
|
|
69
|
-
let match = false;
|
|
70
|
-
try {
|
|
71
|
-
const parsed = JSON.parse(existing);
|
|
72
|
-
if (parsed.salt && parsed.hash) {
|
|
73
|
-
const hash = crypto.createHash('sha256').update(parsed.salt + pin).digest('hex');
|
|
74
|
-
match = hash === parsed.hash;
|
|
75
|
-
}
|
|
76
|
-
} catch {
|
|
77
|
-
const hash = crypto.createHash('sha256').update(pin).digest('hex');
|
|
78
|
-
match = hash === existing;
|
|
79
|
-
}
|
|
80
|
-
if (!match) {
|
|
81
|
-
console.log(chalk.red('Incorrect PIN.'));
|
|
82
|
-
process.exitCode = 1;
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
config.delete('pinHash');
|
|
86
|
-
console.log(chalk.green('\u2714 PIN lock removed.'));
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function showPinStatus() {
|
|
91
|
-
const pinHash = config.get('pinHash');
|
|
92
|
-
if (pinHash) {
|
|
93
|
-
console.log(`PIN lock: ${chalk.green('Enabled')}`);
|
|
94
|
-
} else {
|
|
95
|
-
console.log(`PIN lock: ${chalk.gray('Not set')}`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
import config from '../utils/config.js';
|
|
4
|
+
import readline from 'readline';
|
|
5
|
+
|
|
6
|
+
function promptPin(prompt) {
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
9
|
+
rl.question(prompt, (answer) => {
|
|
10
|
+
rl.close();
|
|
11
|
+
resolve(answer.trim());
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function pinCommand(program) {
|
|
17
|
+
const cmd = program
|
|
18
|
+
.command('pin')
|
|
19
|
+
.description('manage PIN lock for app security')
|
|
20
|
+
.addHelpText('after', `
|
|
21
|
+
Examples:
|
|
22
|
+
$ pal pin status Show if PIN is set
|
|
23
|
+
$ pal pin set Set a new PIN
|
|
24
|
+
$ pal pin remove Remove PIN lock
|
|
25
|
+
`)
|
|
26
|
+
.action(() => {
|
|
27
|
+
showPinStatus();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
cmd
|
|
31
|
+
.command('status')
|
|
32
|
+
.description('show if PIN lock is enabled')
|
|
33
|
+
.action(() => {
|
|
34
|
+
showPinStatus();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
cmd
|
|
38
|
+
.command('set')
|
|
39
|
+
.description('set or update PIN lock')
|
|
40
|
+
.action(async () => {
|
|
41
|
+
const pin = await promptPin('Enter new PIN: ');
|
|
42
|
+
if (!pin || pin.length < 4) {
|
|
43
|
+
console.log(chalk.red('PIN must be at least 4 characters.'));
|
|
44
|
+
process.exitCode = 1;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const confirm = await promptPin('Confirm PIN: ');
|
|
48
|
+
if (pin !== confirm) {
|
|
49
|
+
console.log(chalk.red('PINs do not match.'));
|
|
50
|
+
process.exitCode = 1;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const salt = crypto.randomBytes(16).toString('hex');
|
|
54
|
+
const hash = crypto.createHash('sha256').update(salt + pin).digest('hex');
|
|
55
|
+
config.set('pinHash', JSON.stringify({ salt, hash }));
|
|
56
|
+
console.log(chalk.green('\u2714 PIN lock enabled.'));
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
cmd
|
|
60
|
+
.command('remove')
|
|
61
|
+
.description('remove PIN lock')
|
|
62
|
+
.action(async () => {
|
|
63
|
+
const existing = config.get('pinHash');
|
|
64
|
+
if (!existing) {
|
|
65
|
+
console.log(chalk.gray('No PIN is set.'));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const pin = await promptPin('Enter current PIN to remove: ');
|
|
69
|
+
let match = false;
|
|
70
|
+
try {
|
|
71
|
+
const parsed = JSON.parse(existing);
|
|
72
|
+
if (parsed.salt && parsed.hash) {
|
|
73
|
+
const hash = crypto.createHash('sha256').update(parsed.salt + pin).digest('hex');
|
|
74
|
+
match = hash === parsed.hash;
|
|
75
|
+
}
|
|
76
|
+
} catch {
|
|
77
|
+
const hash = crypto.createHash('sha256').update(pin).digest('hex');
|
|
78
|
+
match = hash === existing;
|
|
79
|
+
}
|
|
80
|
+
if (!match) {
|
|
81
|
+
console.log(chalk.red('Incorrect PIN.'));
|
|
82
|
+
process.exitCode = 1;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
config.delete('pinHash');
|
|
86
|
+
console.log(chalk.green('\u2714 PIN lock removed.'));
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function showPinStatus() {
|
|
91
|
+
const pinHash = config.get('pinHash');
|
|
92
|
+
if (pinHash) {
|
|
93
|
+
console.log(`PIN lock: ${chalk.green('Enabled')}`);
|
|
94
|
+
} else {
|
|
95
|
+
console.log(`PIN lock: ${chalk.gray('Not set')}`);
|
|
96
|
+
}
|
|
97
|
+
}
|