@iksdev/shard-cli 0.1.4 → 0.1.6

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.
Files changed (2) hide show
  1. package/bin/shard.js +94 -0
  2. package/package.json +1 -1
package/bin/shard.js CHANGED
@@ -20,6 +20,7 @@ Usage:
20
20
  shard login --username <name> --password <pass> [--server <url>]
21
21
  shard whoami [--server <url>]
22
22
  shard sync <folder> [--server <url>] [--dry-run] [--force] [--once] [--interval-ms <n>]
23
+ shard share <file> [--server <url>] [--limits <n>] [--temps <jours>]
23
24
  shard logout
24
25
  shard config show
25
26
  shard config set-server <url>
@@ -29,6 +30,7 @@ Examples:
29
30
  shard sync ./MonDossier
30
31
  shard sync ./MonDossier --once
31
32
  shard sync ./MonDossier --dry-run
33
+ shard share ./MonFichier.mp4 --limits 0 --temps 0
32
34
  `);
33
35
  }
34
36
 
@@ -288,6 +290,93 @@ async function uploadOneFile(server, token, file) {
288
290
  });
289
291
  }
290
292
 
293
+ async function findRemoteFileByNameAndSize(server, token, fileName, fileSize) {
294
+ const data = await httpJson(`${server}/api/files?limit=100&offset=0&sort=created_at&order=desc&search=${encodeURIComponent(fileName)}`, {
295
+ method: 'GET',
296
+ headers: { Authorization: `Bearer ${token}` }
297
+ });
298
+ const rows = Array.isArray(data.files) ? data.files : [];
299
+ return rows.find((row) => row.original_name === fileName && Number(row.file_size || 0) === Number(fileSize || 0)) || null;
300
+ }
301
+
302
+ function parseOptionalPositiveInt(raw, flagName) {
303
+ if (raw === undefined || raw === null) return undefined;
304
+ const n = parseInt(String(raw), 10);
305
+ if (Number.isNaN(n) || n < 0) {
306
+ throw new Error(`${flagName} doit etre un entier >= 0`);
307
+ }
308
+ return n;
309
+ }
310
+
311
+ async function shareFile(positionals, flags) {
312
+ const target = positionals[0];
313
+ if (!target) {
314
+ throw new Error('Usage: shard share <file> [--server <url>] [--limits <n>] [--temps <jours>]');
315
+ }
316
+
317
+ const absPath = path.resolve(process.cwd(), target);
318
+ if (!(await pathExists(absPath))) {
319
+ throw new Error(`Fichier introuvable: ${absPath}`);
320
+ }
321
+ const st = await fsp.stat(absPath);
322
+ if (!st.isFile()) {
323
+ throw new Error(`Ce n'est pas un fichier: ${absPath}`);
324
+ }
325
+
326
+ const limits = parseOptionalPositiveInt(flags.limits, '--limits');
327
+ const temps = parseOptionalPositiveInt(flags.temps, '--temps');
328
+
329
+ const config = await readConfig();
330
+ const server = getServer(flags, config);
331
+ const token = getToken(config);
332
+ if (!token) {
333
+ throw new Error('Non connecte. Lance: shard login --username ... --password ...');
334
+ }
335
+
336
+ await httpJson(`${server}/api/auth/verify`, {
337
+ method: 'POST',
338
+ headers: { Authorization: `Bearer ${token}` }
339
+ });
340
+
341
+ const fileName = path.basename(absPath);
342
+ let remote = await findRemoteFileByNameAndSize(server, token, fileName, st.size);
343
+ let fileId = remote?.id || null;
344
+
345
+ if (!fileId) {
346
+ console.log(`Upload necessaire: ${fileName} (${formatBytes(st.size)})`);
347
+ const uploaded = await uploadOneFile(server, token, {
348
+ absPath,
349
+ relPath: fileName,
350
+ size: st.size,
351
+ mtimeMs: Math.round(st.mtimeMs)
352
+ });
353
+ fileId = uploaded?.file?.id;
354
+ if (!fileId) {
355
+ throw new Error('Upload reussi mais ID fichier manquant');
356
+ }
357
+ }
358
+
359
+ const payload = { fileId };
360
+ if (limits !== undefined && limits > 0) payload.maxDownloads = limits;
361
+ if (temps !== undefined && temps > 0) payload.expiresInDays = temps;
362
+
363
+ const created = await httpJson(`${server}/api/share/create`, {
364
+ method: 'POST',
365
+ headers: {
366
+ Authorization: `Bearer ${token}`,
367
+ 'Content-Type': 'application/json'
368
+ },
369
+ body: JSON.stringify(payload)
370
+ });
371
+
372
+ const share = created?.share || {};
373
+ console.log(`Partage cree pour: ${fileName}`);
374
+ if (share.url) console.log(`URL: ${share.url}`);
375
+ if (share.token) console.log(`Token: ${share.token}`);
376
+ console.log(`Limite downloads: ${limits && limits > 0 ? limits : 'illimitee'}`);
377
+ console.log(`Expiration: ${temps && temps > 0 ? `${temps} jour(s)` : 'aucune'}`);
378
+ }
379
+
291
380
  function fileListToMap(files) {
292
381
  const map = new Map();
293
382
  for (const file of files) {
@@ -675,6 +764,11 @@ async function main() {
675
764
  return;
676
765
  }
677
766
 
767
+ if (command === 'share') {
768
+ await shareFile(positionals, flags);
769
+ return;
770
+ }
771
+
678
772
  if (command === 'config') {
679
773
  const sub = positionals[0];
680
774
  if (sub === 'show') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iksdev/shard-cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "CLI pour synchroniser un dossier local avec Shard",
5
5
  "bin": {
6
6
  "shard": "bin/shard.js"