@iksdev/shard-cli 0.1.45 → 0.1.46
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/bin/shard.js +396 -86
- package/package.json +1 -1
package/bin/shard.js
CHANGED
|
@@ -88,32 +88,32 @@ function printHelp() {
|
|
|
88
88
|
║ Shard ║
|
|
89
89
|
╚══════════════════════════════════════════════════╝
|
|
90
90
|
|
|
91
|
-
Commandes disponibles:
|
|
92
|
-
shard login Se connecter au serveur
|
|
93
|
-
shard whoami Afficher l'utilisateur connecte
|
|
94
|
-
shard account Afficher username, email et plan
|
|
95
|
-
shard sync <dossier> Synchroniser un dossier local
|
|
96
|
-
shard share <fichier> Partager un fichier via relay
|
|
97
|
-
shard logout Se deconnecter
|
|
91
|
+
Commandes disponibles:
|
|
92
|
+
shard login Se connecter au serveur
|
|
93
|
+
shard whoami Afficher l'utilisateur connecte
|
|
94
|
+
shard account Afficher username, email et plan
|
|
95
|
+
shard sync <dossier> Synchroniser un dossier local
|
|
96
|
+
shard share <fichier> Partager un fichier via relay
|
|
97
|
+
shard logout Se deconnecter
|
|
98
98
|
shard config show Afficher la configuration
|
|
99
99
|
shard config set-server <url> Changer de serveur
|
|
100
100
|
|
|
101
101
|
Mode interactif:
|
|
102
102
|
Lance une commande sans arguments et la CLI te guidera etape par etape.
|
|
103
103
|
|
|
104
|
-
Options avancees:
|
|
105
|
-
login --username <n> --password <pass> [--server <url>]
|
|
106
|
-
whoami [--server <url>]
|
|
107
|
-
account [--server <url>]
|
|
108
|
-
sync <dossier> [--server <url>] [--dry-run] [--force] [--once] [--interval-ms <n>]
|
|
109
|
-
share <fichier> [--server <url>] [--limits <n>] [--temps <jours>] [--upload]
|
|
104
|
+
Options avancees:
|
|
105
|
+
login --username <n> --password <pass> [--server <url>]
|
|
106
|
+
whoami [--server <url>]
|
|
107
|
+
account [--server <url>]
|
|
108
|
+
sync <dossier> [--server <url>] [--dry-run] [--force] [--once] [--interval-ms <n>]
|
|
109
|
+
share <fichier> [--server <url>] [--limits <n>] [--temps <jours>] [--upload]
|
|
110
110
|
|
|
111
111
|
Exemples:
|
|
112
|
-
shard login
|
|
113
|
-
shard account
|
|
114
|
-
shard sync ./MonDossier
|
|
115
|
-
shard sync ./MonDossier --once
|
|
116
|
-
shard share ./MonFichier.mp4
|
|
112
|
+
shard login
|
|
113
|
+
shard account
|
|
114
|
+
shard sync ./MonDossier
|
|
115
|
+
shard sync ./MonDossier --once
|
|
116
|
+
shard share ./MonFichier.mp4
|
|
117
117
|
shard share ./MonFichier.mp4 --upload
|
|
118
118
|
|
|
119
119
|
Serveur par defaut: https://shard-0ow4.onrender.com
|
|
@@ -300,7 +300,7 @@ async function login(flags) {
|
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
-
async function whoami(flags) {
|
|
303
|
+
async function whoami(flags) {
|
|
304
304
|
const config = await readConfig();
|
|
305
305
|
const server = getServer(flags, config);
|
|
306
306
|
const token = getToken(config);
|
|
@@ -316,28 +316,28 @@ async function whoami(flags) {
|
|
|
316
316
|
const user = data.user || {};
|
|
317
317
|
console.log(`Server: ${server}`);
|
|
318
318
|
console.log(`User: ${user.username || user.userId || 'inconnu'}`);
|
|
319
|
-
if (user.email) console.log(`Email: ${user.email}`);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
async function account(flags) {
|
|
323
|
-
const config = await readConfig();
|
|
324
|
-
const server = getServer(flags, config);
|
|
325
|
-
const token = getToken(config);
|
|
326
|
-
if (!token) {
|
|
327
|
-
throw new Error('Non connecte. Lance: shard login --username ... --password ...');
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
const data = await httpJson(`${server}/api/auth/profile`, {
|
|
331
|
-
method: 'GET',
|
|
332
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
const user = data.user || {};
|
|
336
|
-
const plan = String(user.billing_plan || 'free').toLowerCase();
|
|
337
|
-
console.log(`Username: ${user.username || 'inconnu'}`);
|
|
338
|
-
console.log(`Email: ${user.email || 'inconnu'}`);
|
|
339
|
-
console.log(`Plan: ${plan}`);
|
|
340
|
-
}
|
|
319
|
+
if (user.email) console.log(`Email: ${user.email}`);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async function account(flags) {
|
|
323
|
+
const config = await readConfig();
|
|
324
|
+
const server = getServer(flags, config);
|
|
325
|
+
const token = getToken(config);
|
|
326
|
+
if (!token) {
|
|
327
|
+
throw new Error('Non connecte. Lance: shard login --username ... --password ...');
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const data = await httpJson(`${server}/api/auth/profile`, {
|
|
331
|
+
method: 'GET',
|
|
332
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
const user = data.user || {};
|
|
336
|
+
const plan = String(user.billing_plan || 'free').toLowerCase();
|
|
337
|
+
console.log(`Username: ${user.username || 'inconnu'}`);
|
|
338
|
+
console.log(`Email: ${user.email || 'inconnu'}`);
|
|
339
|
+
console.log(`Plan: ${plan}`);
|
|
340
|
+
}
|
|
341
341
|
|
|
342
342
|
async function logout() {
|
|
343
343
|
const config = await readConfig();
|
|
@@ -1144,7 +1144,299 @@ async function syncFolder(positionals, flags) {
|
|
|
1144
1144
|
}
|
|
1145
1145
|
}
|
|
1146
1146
|
|
|
1147
|
-
|
|
1147
|
+
// ─── Couleurs ANSI ───────────────────────────────────────────────────────────
|
|
1148
|
+
|
|
1149
|
+
const C = {
|
|
1150
|
+
reset: '\x1b[0m',
|
|
1151
|
+
bold: '\x1b[1m',
|
|
1152
|
+
dim: '\x1b[2m',
|
|
1153
|
+
cyan: '\x1b[36m',
|
|
1154
|
+
blue: '\x1b[34m',
|
|
1155
|
+
green: '\x1b[32m',
|
|
1156
|
+
yellow: '\x1b[33m',
|
|
1157
|
+
red: '\x1b[31m',
|
|
1158
|
+
white: '\x1b[97m',
|
|
1159
|
+
gray: '\x1b[90m',
|
|
1160
|
+
bgBlue: '\x1b[44m',
|
|
1161
|
+
bCyan: '\x1b[1m\x1b[36m',
|
|
1162
|
+
bWhite: '\x1b[1m\x1b[97m',
|
|
1163
|
+
bGreen: '\x1b[1m\x1b[32m',
|
|
1164
|
+
bRed: '\x1b[1m\x1b[31m',
|
|
1165
|
+
bYellow: '\x1b[1m\x1b[33m',
|
|
1166
|
+
};
|
|
1167
|
+
|
|
1168
|
+
function c(color, text) { return `${color}${text}${C.reset}`; }
|
|
1169
|
+
|
|
1170
|
+
// ─── Panel interactif ────────────────────────────────────────────────────────
|
|
1171
|
+
|
|
1172
|
+
const W = 64;
|
|
1173
|
+
|
|
1174
|
+
function clearScreen() {
|
|
1175
|
+
process.stdout.write('\x1Bc');
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
function line(char = '─', color = C.gray) {
|
|
1179
|
+
return c(color, char.repeat(W));
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
function boxLine(content = '', color = C.cyan) {
|
|
1183
|
+
const visible = content.replace(/\x1b\[[0-9;]*m/g, '');
|
|
1184
|
+
const pad = W - 2 - visible.length;
|
|
1185
|
+
return `${c(color, '│')} ${content}${' '.repeat(Math.max(0, pad))}${c(color, '│')}`;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
function centered(text, totalWidth = W - 2) {
|
|
1189
|
+
const visible = text.replace(/\x1b\[[0-9;]*m/g, '');
|
|
1190
|
+
const left = Math.floor((totalWidth - visible.length) / 2);
|
|
1191
|
+
const right = totalWidth - visible.length - left;
|
|
1192
|
+
return ' '.repeat(left) + text + ' '.repeat(right);
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
function printPanel(username, plan, statusMsg) {
|
|
1196
|
+
const now = new Date();
|
|
1197
|
+
const timeStr = now.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' });
|
|
1198
|
+
|
|
1199
|
+
console.log('');
|
|
1200
|
+
console.log(`${c(C.cyan, '╔' + '═'.repeat(W - 2) + '╗')}`);
|
|
1201
|
+
console.log(`${c(C.cyan, '║')}${centered(c(C.bCyan, '◈ S H A R D ◈'), W - 2)}${c(C.cyan, '║')}`);
|
|
1202
|
+
console.log(`${c(C.cyan, '╠' + '═'.repeat(W - 2) + '╣')}`);
|
|
1203
|
+
|
|
1204
|
+
// Infos utilisateur
|
|
1205
|
+
if (username) {
|
|
1206
|
+
const userInfo = `${c(C.gray, 'connecté ')}${c(C.bWhite, username)}${plan ? c(C.gray, ' · ') + c(C.bYellow, plan.toUpperCase()) : ''}`;
|
|
1207
|
+
console.log(boxLine(centered(userInfo, W - 2), C.cyan));
|
|
1208
|
+
}
|
|
1209
|
+
console.log(`${c(C.cyan, '╠' + '═'.repeat(W - 2) + '╣')}`);
|
|
1210
|
+
|
|
1211
|
+
// Menu
|
|
1212
|
+
const entries = [
|
|
1213
|
+
{ key: '1', icon: '⬆', label: 'share', desc: 'Partager un fichier via relay' },
|
|
1214
|
+
{ key: '2', icon: '↻', label: 'sync', desc: 'Synchroniser un dossier local' },
|
|
1215
|
+
{ key: '3', icon: '◉', label: 'account', desc: 'Infos du compte' },
|
|
1216
|
+
{ key: '4', icon: '?', label: 'whoami', desc: 'Utilisateur connecté' },
|
|
1217
|
+
{ key: '5', icon: '⚙', label: 'config', desc: 'Configuration serveur' },
|
|
1218
|
+
{ key: '6', icon: '✕', label: 'logout', desc: 'Se déconnecter et quitter' },
|
|
1219
|
+
];
|
|
1220
|
+
|
|
1221
|
+
console.log(boxLine('', C.cyan));
|
|
1222
|
+
for (const e of entries) {
|
|
1223
|
+
const keyPart = ` ${c(C.bgBlue + C.bWhite, ` ${e.key} `)}`;
|
|
1224
|
+
const iconPart = ` ${c(C.cyan, e.icon)}`;
|
|
1225
|
+
const lblPart = ` ${c(C.bWhite, e.label.padEnd(10))}`;
|
|
1226
|
+
const dscPart = c(C.gray, e.desc);
|
|
1227
|
+
console.log(boxLine(`${keyPart}${iconPart}${lblPart}${dscPart}`, C.cyan));
|
|
1228
|
+
}
|
|
1229
|
+
console.log(boxLine('', C.cyan));
|
|
1230
|
+
|
|
1231
|
+
console.log(`${c(C.cyan, '╠' + '═'.repeat(W - 2) + '╣')}`);
|
|
1232
|
+
|
|
1233
|
+
// Status / message
|
|
1234
|
+
if (statusMsg) {
|
|
1235
|
+
console.log(boxLine(centered(statusMsg, W - 2), C.cyan));
|
|
1236
|
+
} else {
|
|
1237
|
+
console.log(boxLine(centered(c(C.gray, `${timeStr} · tape un numéro ou le nom de la commande`), W - 2), C.cyan));
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
console.log(`${c(C.cyan, '╚' + '═'.repeat(W - 2) + '╝')}`);
|
|
1241
|
+
console.log('');
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
function printSubHeader(title, icon = '◈') {
|
|
1245
|
+
console.log('');
|
|
1246
|
+
console.log(line('─', C.cyan));
|
|
1247
|
+
console.log(` ${c(C.cyan, icon)} ${c(C.bWhite, title)}`);
|
|
1248
|
+
console.log(line('─', C.gray));
|
|
1249
|
+
console.log('');
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
function printSuccess(msg) { console.log(`\n ${c(C.bGreen, '✔')} ${c(C.green, msg)}`); }
|
|
1253
|
+
function printError(msg) { console.log(`\n ${c(C.bRed, '✘')} ${c(C.red, msg)}`); }
|
|
1254
|
+
function printInfo(msg) { console.log(` ${c(C.cyan, '·')} ${c(C.white, msg)}`); }
|
|
1255
|
+
|
|
1256
|
+
async function askPanelInput(prompt) {
|
|
1257
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1258
|
+
const answer = await new Promise((resolve) => rl.question(prompt, resolve));
|
|
1259
|
+
rl.close();
|
|
1260
|
+
return answer.trim();
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
async function pressEnter() {
|
|
1264
|
+
await askPanelInput(`\n ${c(C.gray, 'Appuie sur Entrée pour revenir au menu…')}`);
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
async function panelShare() {
|
|
1268
|
+
clearScreen();
|
|
1269
|
+
printSubHeader('Partager un fichier', '⬆');
|
|
1270
|
+
|
|
1271
|
+
const target = await askText(` ${c(C.cyan, '›')} Chemin du fichier`);
|
|
1272
|
+
if (!target) return;
|
|
1273
|
+
|
|
1274
|
+
const limits = await askText(` ${c(C.cyan, '›')} Limite de téléchargements ${c(C.gray, '(0 = illimité)')}`, '0');
|
|
1275
|
+
const temps = await askText(` ${c(C.cyan, '›')} Expiration en jours ${c(C.gray, '(0 = aucune)')}`, '0');
|
|
1276
|
+
const modeRaw = await askText(` ${c(C.cyan, '›')} Mode upload serveur ? ${c(C.gray, '[o/n]')}`, 'n');
|
|
1277
|
+
|
|
1278
|
+
const flags = {};
|
|
1279
|
+
if (parseInt(limits, 10) > 0) flags.limits = limits;
|
|
1280
|
+
if (parseInt(temps, 10) > 0) flags.temps = temps;
|
|
1281
|
+
if (modeRaw.toLowerCase() === 'o') flags.upload = true;
|
|
1282
|
+
|
|
1283
|
+
console.log('');
|
|
1284
|
+
console.log(line('─', C.gray));
|
|
1285
|
+
try {
|
|
1286
|
+
await shareFile([target], flags);
|
|
1287
|
+
printSuccess('Partage créé avec succès.');
|
|
1288
|
+
} catch (err) {
|
|
1289
|
+
if (err?.data?.code === 'PLAN_FILE_LIMIT_EXCEEDED') {
|
|
1290
|
+
printPlanLimitBox(err);
|
|
1291
|
+
} else {
|
|
1292
|
+
printError(err.message);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
await pressEnter();
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
async function panelSync() {
|
|
1299
|
+
clearScreen();
|
|
1300
|
+
printSubHeader('Synchroniser un dossier', '↻');
|
|
1301
|
+
|
|
1302
|
+
const target = await askText(` ${c(C.cyan, '›')} Chemin du dossier`);
|
|
1303
|
+
if (!target) return;
|
|
1304
|
+
|
|
1305
|
+
const onceRaw = await askText(` ${c(C.cyan, '›')} Sync unique (pas de surveillance) ? ${c(C.gray, '[o/n]')}`, 'n');
|
|
1306
|
+
const dryRaw = await askText(` ${c(C.cyan, '›')} Simulation (dry-run) ? ${c(C.gray, '[o/n]')}`, 'n');
|
|
1307
|
+
const forceRaw = await askText(` ${c(C.cyan, '›')} Forcer le re-upload de tout ? ${c(C.gray, '[o/n]')}`, 'n');
|
|
1308
|
+
|
|
1309
|
+
const flags = {};
|
|
1310
|
+
if (onceRaw.toLowerCase() === 'o') flags.once = true;
|
|
1311
|
+
if (dryRaw.toLowerCase() === 'o') flags['dry-run'] = true;
|
|
1312
|
+
if (forceRaw.toLowerCase() === 'o') flags.force = true;
|
|
1313
|
+
|
|
1314
|
+
console.log('');
|
|
1315
|
+
console.log(line('─', C.gray));
|
|
1316
|
+
try {
|
|
1317
|
+
await syncFolder([target], flags);
|
|
1318
|
+
if (flags.once || flags['dry-run']) {
|
|
1319
|
+
printSuccess('Synchronisation terminée.');
|
|
1320
|
+
await pressEnter();
|
|
1321
|
+
}
|
|
1322
|
+
// mode watch : syncFolder bloque, on revient au menu quand il se termine
|
|
1323
|
+
} catch (err) {
|
|
1324
|
+
printError(err.message);
|
|
1325
|
+
await pressEnter();
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
async function panelAccount() {
|
|
1330
|
+
clearScreen();
|
|
1331
|
+
printSubHeader('Informations du compte', '◉');
|
|
1332
|
+
try {
|
|
1333
|
+
await account({});
|
|
1334
|
+
} catch (err) {
|
|
1335
|
+
printError(err.message);
|
|
1336
|
+
}
|
|
1337
|
+
await pressEnter();
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
async function panelWhoami() {
|
|
1341
|
+
clearScreen();
|
|
1342
|
+
printSubHeader('Utilisateur connecté', '?');
|
|
1343
|
+
try {
|
|
1344
|
+
await whoami({});
|
|
1345
|
+
} catch (err) {
|
|
1346
|
+
printError(err.message);
|
|
1347
|
+
}
|
|
1348
|
+
await pressEnter();
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
async function panelConfig() {
|
|
1352
|
+
clearScreen();
|
|
1353
|
+
printSubHeader('Configuration', '⚙');
|
|
1354
|
+
|
|
1355
|
+
printInfo(`${c(C.gray, '[a]')} Afficher la configuration actuelle`);
|
|
1356
|
+
printInfo(`${c(C.gray, '[b]')} Changer de serveur`);
|
|
1357
|
+
console.log('');
|
|
1358
|
+
|
|
1359
|
+
const choice = await askPanelInput(` ${c(C.cyan, '›')} Choix ${c(C.gray, '[a/b]')} : `);
|
|
1360
|
+
|
|
1361
|
+
if (choice.toLowerCase() === 'a') {
|
|
1362
|
+
console.log('');
|
|
1363
|
+
console.log(line('─', C.gray));
|
|
1364
|
+
try {
|
|
1365
|
+
await showConfig();
|
|
1366
|
+
} catch (err) {
|
|
1367
|
+
printError(err.message);
|
|
1368
|
+
}
|
|
1369
|
+
} else if (choice.toLowerCase() === 'b') {
|
|
1370
|
+
const url = await askText(` ${c(C.cyan, '›')} Nouvelle URL du serveur`);
|
|
1371
|
+
if (url) {
|
|
1372
|
+
try {
|
|
1373
|
+
await setServer([url]);
|
|
1374
|
+
printSuccess(`Serveur mis à jour : ${url}`);
|
|
1375
|
+
} catch (err) {
|
|
1376
|
+
printError(err.message);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
} else {
|
|
1380
|
+
printError('Choix invalide.');
|
|
1381
|
+
}
|
|
1382
|
+
await pressEnter();
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
async function runPanel(username, plan) {
|
|
1386
|
+
// Logout automatique à la fermeture du process
|
|
1387
|
+
process.on('exit', () => {
|
|
1388
|
+
try {
|
|
1389
|
+
const raw = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
1390
|
+
const parsed = JSON.parse(raw);
|
|
1391
|
+
parsed.token = '';
|
|
1392
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(parsed, null, 2), 'utf8');
|
|
1393
|
+
} catch (_) {}
|
|
1394
|
+
});
|
|
1395
|
+
|
|
1396
|
+
const handleStop = async (sig) => {
|
|
1397
|
+
process.stdout.write(`\n\n ${c(C.gray, `Fermeture (${sig})…`)}\n`);
|
|
1398
|
+
try {
|
|
1399
|
+
const cfg = await readConfig();
|
|
1400
|
+
await writeConfig({ ...cfg, token: '' });
|
|
1401
|
+
process.stdout.write(` ${c(C.green, '✔')} Session fermée. À bientôt !\n\n`);
|
|
1402
|
+
} catch (_) {}
|
|
1403
|
+
process.exit(0);
|
|
1404
|
+
};
|
|
1405
|
+
process.on('SIGINT', handleStop);
|
|
1406
|
+
process.on('SIGTERM', handleStop);
|
|
1407
|
+
|
|
1408
|
+
let statusMsg = null;
|
|
1409
|
+
|
|
1410
|
+
while (true) {
|
|
1411
|
+
clearScreen();
|
|
1412
|
+
printPanel(username, plan, statusMsg);
|
|
1413
|
+
statusMsg = null;
|
|
1414
|
+
|
|
1415
|
+
const answer = (await askPanelInput(` ${c(C.bCyan, '›')} `)).toLowerCase();
|
|
1416
|
+
|
|
1417
|
+
if (answer === '1' || answer === 'share') { await panelShare(); }
|
|
1418
|
+
else if (answer === '2' || answer === 'sync') { await panelSync(); }
|
|
1419
|
+
else if (answer === '3' || answer === 'account') { await panelAccount(); }
|
|
1420
|
+
else if (answer === '4' || answer === 'whoami') { await panelWhoami(); }
|
|
1421
|
+
else if (answer === '5' || answer === 'config') { await panelConfig(); }
|
|
1422
|
+
else if (answer === '6' || answer === 'logout') {
|
|
1423
|
+
clearScreen();
|
|
1424
|
+
process.removeListener('SIGINT', handleStop);
|
|
1425
|
+
process.removeListener('SIGTERM', handleStop);
|
|
1426
|
+
await logout();
|
|
1427
|
+
console.log(`\n ${c(C.bGreen, '✔')} Déconnecté avec succès. À bientôt !\n`);
|
|
1428
|
+
process.exit(0);
|
|
1429
|
+
} else if (answer === '') {
|
|
1430
|
+
// reboucle silencieusement
|
|
1431
|
+
} else {
|
|
1432
|
+
statusMsg = c(C.bRed, `✘ Option inconnue : "${answer}" · choisis 1–6`);
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
// ─── main ─────────────────────────────────────────────────────────────────────
|
|
1438
|
+
|
|
1439
|
+
async function main() {
|
|
1148
1440
|
const { command, positionals, flags } = parseArgs(process.argv.slice(2));
|
|
1149
1441
|
|
|
1150
1442
|
// Vérifie la mise à jour à chaque commande (sauf --help)
|
|
@@ -1171,19 +1463,37 @@ async function main() {
|
|
|
1171
1463
|
|
|
1172
1464
|
if (command === 'login') {
|
|
1173
1465
|
await login(flags);
|
|
1466
|
+
// Après un login réussi en mode interactif, on lance le panel
|
|
1467
|
+
if (isInteractive()) {
|
|
1468
|
+
const config = await readConfig();
|
|
1469
|
+
const server = getServer(flags, config);
|
|
1470
|
+
const token = getToken(config);
|
|
1471
|
+
let username = String(flags.username || '').trim();
|
|
1472
|
+
// Récupère le username depuis le serveur si possible
|
|
1473
|
+
if (!username && token) {
|
|
1474
|
+
try {
|
|
1475
|
+
const data = await httpJson(`${server}/api/auth/verify`, {
|
|
1476
|
+
method: 'POST',
|
|
1477
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
1478
|
+
});
|
|
1479
|
+
username = data.user?.username || data.user?.userId || '';
|
|
1480
|
+
} catch (_) {}
|
|
1481
|
+
}
|
|
1482
|
+
await runPanel(username);
|
|
1483
|
+
}
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
if (command === 'whoami') {
|
|
1488
|
+
await whoami(flags);
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
if (command === 'account') {
|
|
1493
|
+
await account(flags);
|
|
1174
1494
|
return;
|
|
1175
1495
|
}
|
|
1176
1496
|
|
|
1177
|
-
if (command === 'whoami') {
|
|
1178
|
-
await whoami(flags);
|
|
1179
|
-
return;
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
if (command === 'account') {
|
|
1183
|
-
await account(flags);
|
|
1184
|
-
return;
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
1497
|
if (command === 'logout') {
|
|
1188
1498
|
await logout();
|
|
1189
1499
|
return;
|
|
@@ -1212,37 +1522,37 @@ async function main() {
|
|
|
1212
1522
|
throw new Error('Usage: shard config show | shard config set-server <url>');
|
|
1213
1523
|
}
|
|
1214
1524
|
|
|
1215
|
-
throw new Error(`Commande inconnue: ${command}`);
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
function printPlanLimitBox(error) {
|
|
1219
|
-
const data = error?.data || {};
|
|
1220
|
-
const plan = String(data.plan || '').toUpperCase() || 'FREE';
|
|
1221
|
-
const maxFileSize = Number(data.maxFileSize || 0);
|
|
1222
|
-
const fileSize = Number(data.fileSize || 0);
|
|
1223
|
-
const maxLabel = maxFileSize > 0 ? formatBytes(maxFileSize) : '?';
|
|
1224
|
-
const fileLabel = fileSize > 0 ? formatBytes(fileSize) : '?';
|
|
1225
|
-
const lines = [
|
|
1226
|
-
'',
|
|
1227
|
-
'╔════════════════════════════════════════════════════╗',
|
|
1228
|
-
`║ ⚠ Limite du plan ${plan}`.padEnd(52) + ' ║',
|
|
1229
|
-
'╠════════════════════════════════════════════════════╣',
|
|
1230
|
-
`║ Taille fichier : ${fileLabel}`.padEnd(52) + ' ║',
|
|
1231
|
-
`║ Limite max : ${maxLabel} / fichier`.padEnd(52) + ' ║',
|
|
1232
|
-
'║ ║',
|
|
1233
|
-
'║ Passe a une offre superieure pour continuer. ║',
|
|
1234
|
-
'╚════════════════════════════════════════════════════╝',
|
|
1235
|
-
''
|
|
1236
|
-
];
|
|
1237
|
-
console.error(lines.join('\n'));
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
main().catch((error) => {
|
|
1241
|
-
if (error?.data?.code === 'PLAN_FILE_LIMIT_EXCEEDED') {
|
|
1242
|
-
printPlanLimitBox(error);
|
|
1243
|
-
process.exitCode = 1;
|
|
1244
|
-
return;
|
|
1245
|
-
}
|
|
1246
|
-
console.error(`Erreur: ${error.message}`);
|
|
1247
|
-
process.exitCode = 1;
|
|
1248
|
-
});
|
|
1525
|
+
throw new Error(`Commande inconnue: ${command}`);
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
function printPlanLimitBox(error) {
|
|
1529
|
+
const data = error?.data || {};
|
|
1530
|
+
const plan = String(data.plan || '').toUpperCase() || 'FREE';
|
|
1531
|
+
const maxFileSize = Number(data.maxFileSize || 0);
|
|
1532
|
+
const fileSize = Number(data.fileSize || 0);
|
|
1533
|
+
const maxLabel = maxFileSize > 0 ? formatBytes(maxFileSize) : '?';
|
|
1534
|
+
const fileLabel = fileSize > 0 ? formatBytes(fileSize) : '?';
|
|
1535
|
+
const lines = [
|
|
1536
|
+
'',
|
|
1537
|
+
'╔════════════════════════════════════════════════════╗',
|
|
1538
|
+
`║ ⚠ Limite du plan ${plan}`.padEnd(52) + ' ║',
|
|
1539
|
+
'╠════════════════════════════════════════════════════╣',
|
|
1540
|
+
`║ Taille fichier : ${fileLabel}`.padEnd(52) + ' ║',
|
|
1541
|
+
`║ Limite max : ${maxLabel} / fichier`.padEnd(52) + ' ║',
|
|
1542
|
+
'║ ║',
|
|
1543
|
+
'║ Passe a une offre superieure pour continuer. ║',
|
|
1544
|
+
'╚════════════════════════════════════════════════════╝',
|
|
1545
|
+
''
|
|
1546
|
+
];
|
|
1547
|
+
console.error(lines.join('\n'));
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
main().catch((error) => {
|
|
1551
|
+
if (error?.data?.code === 'PLAN_FILE_LIMIT_EXCEEDED') {
|
|
1552
|
+
printPlanLimitBox(error);
|
|
1553
|
+
process.exitCode = 1;
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
console.error(`Erreur: ${error.message}`);
|
|
1557
|
+
process.exitCode = 1;
|
|
1558
|
+
});
|