@within-7/jetr 0.1.2 → 0.3.0
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/dist/cli.js +361 -75
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
+
import { resolve as resolve2, basename } from "path";
|
|
5
|
+
import { existsSync as existsSync4, statSync as statSync2 } from "fs";
|
|
4
6
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
7
|
+
import chalk2 from "chalk";
|
|
6
8
|
import ora from "ora";
|
|
7
9
|
|
|
8
10
|
// src/config.ts
|
|
@@ -128,34 +130,149 @@ var api = {
|
|
|
128
130
|
|
|
129
131
|
// src/deploy.ts
|
|
130
132
|
import { createHash } from "crypto";
|
|
131
|
-
import { readFileSync as
|
|
132
|
-
import { resolve, join as
|
|
133
|
+
import { readFileSync as readFileSync3, statSync } from "fs";
|
|
134
|
+
import { resolve, join as join3, posix } from "path";
|
|
133
135
|
import { glob } from "glob";
|
|
136
|
+
|
|
137
|
+
// src/ignore.ts
|
|
138
|
+
import ignore from "ignore";
|
|
139
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
140
|
+
import { join as join2 } from "path";
|
|
141
|
+
var DEFAULT_JETRIGNORE = `# Dependencies
|
|
142
|
+
node_modules/
|
|
143
|
+
.pnp
|
|
144
|
+
.pnp.js
|
|
145
|
+
|
|
146
|
+
# Build outputs
|
|
147
|
+
dist/
|
|
148
|
+
build/
|
|
149
|
+
out/
|
|
150
|
+
.next/
|
|
151
|
+
.nuxt/
|
|
152
|
+
.output/
|
|
153
|
+
.cache/
|
|
154
|
+
|
|
155
|
+
# Logs
|
|
156
|
+
*.log
|
|
157
|
+
npm-debug.log*
|
|
158
|
+
yarn-debug.log*
|
|
159
|
+
yarn-error.log*
|
|
160
|
+
pnpm-debug.log*
|
|
161
|
+
|
|
162
|
+
# Environment
|
|
163
|
+
.env
|
|
164
|
+
.env.*
|
|
165
|
+
.env.local
|
|
166
|
+
.env.*.local
|
|
167
|
+
|
|
168
|
+
# IDE & Editor
|
|
169
|
+
.vscode/
|
|
170
|
+
.idea/
|
|
171
|
+
*.swp
|
|
172
|
+
*.swo
|
|
173
|
+
*~
|
|
174
|
+
.project
|
|
175
|
+
.classpath
|
|
176
|
+
.settings/
|
|
177
|
+
|
|
178
|
+
# OS files
|
|
179
|
+
.DS_Store
|
|
180
|
+
Thumbs.db
|
|
181
|
+
desktop.ini
|
|
182
|
+
|
|
183
|
+
# Git
|
|
184
|
+
.git/
|
|
185
|
+
.gitignore
|
|
186
|
+
|
|
187
|
+
# Testing
|
|
188
|
+
coverage/
|
|
189
|
+
.nyc_output/
|
|
190
|
+
|
|
191
|
+
# Package managers
|
|
192
|
+
package-lock.json
|
|
193
|
+
yarn.lock
|
|
194
|
+
pnpm-lock.yaml
|
|
195
|
+
|
|
196
|
+
# Misc
|
|
197
|
+
*.zip
|
|
198
|
+
*.tar.gz
|
|
199
|
+
`;
|
|
200
|
+
var ALWAYS_IGNORE = [".jetrignore", ".jetrkeep", ".jetrrc"];
|
|
201
|
+
function loadIgnore(dir) {
|
|
202
|
+
const ig = ignore();
|
|
203
|
+
ig.add(ALWAYS_IGNORE);
|
|
204
|
+
const ignorePath = join2(dir, ".jetrignore");
|
|
205
|
+
if (existsSync2(ignorePath)) {
|
|
206
|
+
const content = readFileSync2(ignorePath, "utf-8");
|
|
207
|
+
ig.add(content);
|
|
208
|
+
} else {
|
|
209
|
+
ig.add(DEFAULT_JETRIGNORE);
|
|
210
|
+
}
|
|
211
|
+
return ig;
|
|
212
|
+
}
|
|
213
|
+
function loadKeep(dir) {
|
|
214
|
+
const keepPath = join2(dir, ".jetrkeep");
|
|
215
|
+
if (!existsSync2(keepPath)) return null;
|
|
216
|
+
const keep = ignore();
|
|
217
|
+
keep.add(readFileSync2(keepPath, "utf-8"));
|
|
218
|
+
return keep;
|
|
219
|
+
}
|
|
220
|
+
function shouldInclude(relativePath, ig, keep) {
|
|
221
|
+
if (ig.ignores(relativePath) || ig.ignores(relativePath + "/")) {
|
|
222
|
+
if (keep && keep.ignores(relativePath)) {
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
function createJetrignore(dir) {
|
|
230
|
+
const ignorePath = join2(dir, ".jetrignore");
|
|
231
|
+
if (existsSync2(ignorePath)) return false;
|
|
232
|
+
writeFileSync2(ignorePath, DEFAULT_JETRIGNORE);
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// src/deploy.ts
|
|
134
237
|
function hashFile(filePath) {
|
|
135
|
-
const content =
|
|
238
|
+
const content = readFileSync3(filePath);
|
|
136
239
|
const hash = createHash("sha256").update(content).digest("hex");
|
|
137
240
|
return `sha256:${hash}`;
|
|
138
241
|
}
|
|
139
242
|
async function deploy(siteName, directory, onProgress) {
|
|
140
243
|
const absDir = resolve(directory);
|
|
244
|
+
const ig = loadIgnore(absDir);
|
|
245
|
+
const keep = loadKeep(absDir);
|
|
141
246
|
onProgress?.("Scanning files...");
|
|
142
|
-
const
|
|
247
|
+
const allFiles = await glob("**/*", {
|
|
143
248
|
cwd: absDir,
|
|
144
249
|
nodir: true,
|
|
145
|
-
dot:
|
|
250
|
+
dot: true
|
|
146
251
|
});
|
|
252
|
+
let ignoredCount = 0;
|
|
253
|
+
const filePaths = [];
|
|
254
|
+
for (const fp of allFiles) {
|
|
255
|
+
const normalizedPath = fp.split("/").join(posix.sep);
|
|
256
|
+
if (shouldInclude(normalizedPath, ig, keep)) {
|
|
257
|
+
filePaths.push(fp);
|
|
258
|
+
} else {
|
|
259
|
+
ignoredCount++;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
147
262
|
if (filePaths.length === 0) {
|
|
148
|
-
throw new Error(
|
|
263
|
+
throw new Error(
|
|
264
|
+
`No files to deploy in ${directory} (${allFiles.length} files found, all ignored)`
|
|
265
|
+
);
|
|
149
266
|
}
|
|
267
|
+
onProgress?.(`Found ${filePaths.length} files (${ignoredCount} ignored)`);
|
|
150
268
|
const manifest = {};
|
|
151
269
|
for (const fp of filePaths) {
|
|
152
|
-
const absPath =
|
|
270
|
+
const absPath = join3(absDir, fp);
|
|
153
271
|
const hash = hashFile(absPath);
|
|
154
272
|
const size = statSync(absPath).size;
|
|
155
273
|
const normalizedPath = fp.split("/").join(posix.sep);
|
|
156
274
|
manifest[normalizedPath] = { hash, size };
|
|
157
275
|
}
|
|
158
|
-
onProgress?.(`Found ${filePaths.length} files`);
|
|
159
276
|
onProgress?.("Computing diff...");
|
|
160
277
|
const diff = await api.deployDiff(siteName, manifest);
|
|
161
278
|
onProgress?.(
|
|
@@ -164,8 +281,8 @@ async function deploy(siteName, directory, onProgress) {
|
|
|
164
281
|
if (diff.upload.length > 0) {
|
|
165
282
|
for (let i = 0; i < diff.upload.length; i++) {
|
|
166
283
|
const fp = diff.upload[i];
|
|
167
|
-
const absPath =
|
|
168
|
-
const content =
|
|
284
|
+
const absPath = join3(absDir, fp);
|
|
285
|
+
const content = readFileSync3(absPath);
|
|
169
286
|
onProgress?.(`Uploading (${i + 1}/${diff.upload.length}) ${fp}`);
|
|
170
287
|
await api.uploadFile(siteName, fp, content, diff.deploy_id, manifest[fp].hash);
|
|
171
288
|
}
|
|
@@ -177,50 +294,202 @@ async function deploy(siteName, directory, onProgress) {
|
|
|
177
294
|
filesUploaded: result.files_uploaded,
|
|
178
295
|
filesDeleted: result.files_deleted,
|
|
179
296
|
filesUnchanged: diff.unchanged.length,
|
|
297
|
+
filesIgnored: ignoredCount,
|
|
180
298
|
totalSize: result.total_size
|
|
181
299
|
};
|
|
182
300
|
}
|
|
183
301
|
|
|
302
|
+
// src/rc.ts
|
|
303
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
304
|
+
import { join as join4 } from "path";
|
|
305
|
+
import { createInterface } from "readline";
|
|
306
|
+
import chalk from "chalk";
|
|
307
|
+
var RC_FILE = ".jetrrc";
|
|
308
|
+
function loadRc(dir) {
|
|
309
|
+
const rcPath = join4(dir, RC_FILE);
|
|
310
|
+
if (!existsSync3(rcPath)) return {};
|
|
311
|
+
try {
|
|
312
|
+
return JSON.parse(readFileSync4(rcPath, "utf-8"));
|
|
313
|
+
} catch {
|
|
314
|
+
return {};
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function saveRc(dir, config) {
|
|
318
|
+
writeFileSync3(join4(dir, RC_FILE), JSON.stringify(config, null, 2) + "\n");
|
|
319
|
+
}
|
|
320
|
+
function updateRc(config, siteName) {
|
|
321
|
+
if (!config.directory) {
|
|
322
|
+
config.directory = { default: siteName, history: [siteName] };
|
|
323
|
+
} else {
|
|
324
|
+
config.directory.default = siteName;
|
|
325
|
+
if (!config.directory.history.includes(siteName)) {
|
|
326
|
+
config.directory.history.push(siteName);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return config;
|
|
330
|
+
}
|
|
331
|
+
async function resolveSiteName(dir, explicitName) {
|
|
332
|
+
if (explicitName) return explicitName;
|
|
333
|
+
const rc = loadRc(dir);
|
|
334
|
+
const saved = rc.directory;
|
|
335
|
+
if (!saved) return null;
|
|
336
|
+
const history = saved.history || [saved.default];
|
|
337
|
+
const options = [
|
|
338
|
+
saved.default,
|
|
339
|
+
...history.filter((h) => h !== saved.default)
|
|
340
|
+
];
|
|
341
|
+
if (options.length === 1) {
|
|
342
|
+
console.log(chalk.dim(`Using saved site: ${options[0]}`));
|
|
343
|
+
return options[0];
|
|
344
|
+
}
|
|
345
|
+
return promptSelection(options);
|
|
346
|
+
}
|
|
347
|
+
async function promptSelection(options) {
|
|
348
|
+
const rl = createInterface({
|
|
349
|
+
input: process.stdin,
|
|
350
|
+
output: process.stdout
|
|
351
|
+
});
|
|
352
|
+
console.log();
|
|
353
|
+
console.log(chalk.bold("Previous deployments:"));
|
|
354
|
+
for (let i = 0; i < options.length; i++) {
|
|
355
|
+
const marker = i === 0 ? chalk.green(" (default)") : "";
|
|
356
|
+
console.log(` ${chalk.cyan(String(i + 1))}) ${options[i]}${marker}`);
|
|
357
|
+
}
|
|
358
|
+
console.log(
|
|
359
|
+
` ${chalk.cyan(String(options.length + 1))}) ${chalk.dim("Enter new site name")}`
|
|
360
|
+
);
|
|
361
|
+
console.log();
|
|
362
|
+
return new Promise((resolve3) => {
|
|
363
|
+
rl.question(chalk.bold("Select [1]: "), (answer) => {
|
|
364
|
+
rl.close();
|
|
365
|
+
const num = parseInt(answer, 10);
|
|
366
|
+
if (!answer || isNaN(num) || num < 1 || num > options.length + 1) {
|
|
367
|
+
resolve3(options[0]);
|
|
368
|
+
} else if (num <= options.length) {
|
|
369
|
+
resolve3(options[num - 1]);
|
|
370
|
+
} else {
|
|
371
|
+
const rl2 = createInterface({
|
|
372
|
+
input: process.stdin,
|
|
373
|
+
output: process.stdout
|
|
374
|
+
});
|
|
375
|
+
rl2.question(chalk.bold("Site name: "), (name) => {
|
|
376
|
+
rl2.close();
|
|
377
|
+
resolve3(name.trim());
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
184
384
|
// src/cli.ts
|
|
185
385
|
var program = new Command();
|
|
186
|
-
program.name("jetr").description("
|
|
386
|
+
program.name("jetr").description("Deploy static sites instantly").version("0.2.0");
|
|
387
|
+
program.argument("[directory]", "Directory to deploy (default: current directory)").argument("[name]", "Site name (reads from .jetrrc if omitted)").option("--no-ignore", "Skip .jetrignore rules").action(async (directory, name) => {
|
|
388
|
+
const dir = resolve2(directory || ".");
|
|
389
|
+
if (!existsSync4(dir)) {
|
|
390
|
+
console.error(chalk2.red(`Path not found: ${dir}`));
|
|
391
|
+
process.exit(1);
|
|
392
|
+
}
|
|
393
|
+
if (!statSync2(dir).isDirectory()) {
|
|
394
|
+
console.error(chalk2.red(`Not a directory: ${dir}`));
|
|
395
|
+
process.exit(1);
|
|
396
|
+
}
|
|
397
|
+
const config = loadConfig();
|
|
398
|
+
if (!config.token) {
|
|
399
|
+
console.error(chalk2.red("Not logged in."));
|
|
400
|
+
console.error(`Run: ${chalk2.cyan("jetr login <token>")}`);
|
|
401
|
+
console.error(
|
|
402
|
+
chalk2.dim("Get your token from auth-gateway: POST /user/login")
|
|
403
|
+
);
|
|
404
|
+
process.exit(1);
|
|
405
|
+
}
|
|
406
|
+
let siteName = await resolveSiteName(dir, name);
|
|
407
|
+
if (!siteName) {
|
|
408
|
+
const dirName = basename(resolve2(dir));
|
|
409
|
+
const { createInterface: createInterface2 } = await import("readline");
|
|
410
|
+
const rl = createInterface2({
|
|
411
|
+
input: process.stdin,
|
|
412
|
+
output: process.stdout
|
|
413
|
+
});
|
|
414
|
+
siteName = await new Promise((res) => {
|
|
415
|
+
rl.question(
|
|
416
|
+
chalk2.bold(`Site name [${dirName}]: `),
|
|
417
|
+
(answer) => {
|
|
418
|
+
rl.close();
|
|
419
|
+
res(answer.trim() || dirName);
|
|
420
|
+
}
|
|
421
|
+
);
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
siteName = siteName.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
|
|
425
|
+
if (!siteName) {
|
|
426
|
+
console.error(chalk2.red("Invalid site name"));
|
|
427
|
+
process.exit(1);
|
|
428
|
+
}
|
|
429
|
+
const spinner = ora("").start();
|
|
430
|
+
try {
|
|
431
|
+
spinner.text = "Checking site...";
|
|
432
|
+
try {
|
|
433
|
+
await api.getSite(siteName);
|
|
434
|
+
} catch {
|
|
435
|
+
spinner.text = `Creating site ${siteName}...`;
|
|
436
|
+
await api.createSite(siteName);
|
|
437
|
+
}
|
|
438
|
+
const result = await deploy(siteName, dir, (msg) => {
|
|
439
|
+
spinner.text = msg;
|
|
440
|
+
});
|
|
441
|
+
spinner.succeed("Deployed!");
|
|
442
|
+
console.log();
|
|
443
|
+
console.log(` ${chalk2.bold("Site:")} ${chalk2.cyan(siteName)}`);
|
|
444
|
+
console.log(` ${chalk2.bold("URL:")} ${chalk2.green(result.url)}`);
|
|
445
|
+
console.log(
|
|
446
|
+
` ${chalk2.bold("Files:")} ${result.filesUploaded} uploaded` + (result.filesDeleted > 0 ? `, ${result.filesDeleted} deleted` : "") + (result.filesUnchanged > 0 ? `, ${result.filesUnchanged} unchanged` : "") + (result.filesIgnored > 0 ? chalk2.dim(` (${result.filesIgnored} ignored)`) : "")
|
|
447
|
+
);
|
|
448
|
+
console.log(` ${chalk2.bold("Size:")} ${formatBytes(result.totalSize)}`);
|
|
449
|
+
console.log();
|
|
450
|
+
const rc = loadRc(dir);
|
|
451
|
+
saveRc(dir, updateRc(rc, siteName));
|
|
452
|
+
} catch (e) {
|
|
453
|
+
spinner.fail(e.message);
|
|
454
|
+
process.exit(1);
|
|
455
|
+
}
|
|
456
|
+
});
|
|
187
457
|
program.command("login").description("Save JWT token for authentication").argument("<token>", "JWT token from auth-gateway login").option("--api-url <url>", "API base URL").action((token, opts) => {
|
|
188
458
|
saveConfig({ token, ...opts.apiUrl ? { apiUrl: opts.apiUrl } : {} });
|
|
189
|
-
console.log(
|
|
459
|
+
console.log(chalk2.green("\u2713 Token saved to ~/.jetr/config.json"));
|
|
190
460
|
});
|
|
191
461
|
program.command("whoami").description("Show current config").action(() => {
|
|
192
462
|
const config = loadConfig();
|
|
193
|
-
console.log(
|
|
194
|
-
|
|
463
|
+
console.log(
|
|
464
|
+
`API: ${config.apiUrl}`
|
|
465
|
+
);
|
|
466
|
+
console.log(
|
|
467
|
+
`Token: ${config.token ? config.token.slice(0, 20) + "..." : chalk2.red("not set")}`
|
|
468
|
+
);
|
|
195
469
|
});
|
|
196
|
-
program.command("
|
|
470
|
+
program.command("init").description("Create .jetrignore with default patterns").argument("[directory]", "Target directory", ".").action(async (directory) => {
|
|
471
|
+
const created = createJetrignore(resolve2(directory));
|
|
472
|
+
if (created) {
|
|
473
|
+
console.log(chalk2.green("\u2713 Created .jetrignore with default patterns"));
|
|
474
|
+
} else {
|
|
475
|
+
console.log(chalk2.yellow(".jetrignore already exists"));
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
program.command("create").description("Create a new site").argument("<name>", "Site name").option("-p, --password <password>", "Set access password").option("-e, --expires <seconds>", "Expire after N seconds", parseInt).action(async (name, opts) => {
|
|
197
479
|
const spinner = ora("Creating site...").start();
|
|
198
480
|
try {
|
|
199
481
|
const site = await api.createSite(name, {
|
|
200
482
|
password: opts.password,
|
|
201
483
|
expires_in: opts.expires
|
|
202
484
|
});
|
|
203
|
-
spinner.succeed(`Created ${
|
|
204
|
-
console.log(` URL: ${
|
|
205
|
-
if (site.password_protected)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
});
|
|
212
|
-
program.command("deploy").description("Deploy a directory to a site").argument("<name>", "Site name").argument("[directory]", "Directory to deploy", ".").action(async (name, directory) => {
|
|
213
|
-
const spinner = ora("").start();
|
|
214
|
-
try {
|
|
215
|
-
const result = await deploy(name, directory, (msg) => {
|
|
216
|
-
spinner.text = msg;
|
|
217
|
-
});
|
|
218
|
-
spinner.succeed("Deployed!");
|
|
219
|
-
console.log(` URL: ${chalk.cyan(result.url)}`);
|
|
220
|
-
console.log(` Uploaded: ${result.filesUploaded} files`);
|
|
221
|
-
if (result.filesDeleted > 0) console.log(` Deleted: ${result.filesDeleted} files`);
|
|
222
|
-
if (result.filesUnchanged > 0) console.log(` Unchanged: ${result.filesUnchanged} files`);
|
|
223
|
-
console.log(` Size: ${formatBytes(result.totalSize)}`);
|
|
485
|
+
spinner.succeed(`Created ${chalk2.bold(site.name)}`);
|
|
486
|
+
console.log(` URL: ${chalk2.cyan(site.url)}`);
|
|
487
|
+
if (site.password_protected)
|
|
488
|
+
console.log(` Password: ${chalk2.yellow("enabled")}`);
|
|
489
|
+
if (site.expires_at)
|
|
490
|
+
console.log(
|
|
491
|
+
` Expires: ${new Date(site.expires_at * 1e3).toISOString()}`
|
|
492
|
+
);
|
|
224
493
|
} catch (e) {
|
|
225
494
|
spinner.fail(e.message);
|
|
226
495
|
process.exit(1);
|
|
@@ -230,51 +499,63 @@ program.command("list").alias("ls").description("List your sites").action(async
|
|
|
230
499
|
try {
|
|
231
500
|
const { sites } = await api.listSites();
|
|
232
501
|
if (sites.length === 0) {
|
|
233
|
-
console.log(
|
|
502
|
+
console.log(
|
|
503
|
+
chalk2.dim("No sites yet. Run: jetr <directory> to deploy")
|
|
504
|
+
);
|
|
234
505
|
return;
|
|
235
506
|
}
|
|
236
507
|
for (const s of sites) {
|
|
237
|
-
const lock = s.password_protected ?
|
|
238
|
-
const expire = s.expires_at ?
|
|
508
|
+
const lock = s.password_protected ? chalk2.yellow(" \u{1F512}") : "";
|
|
509
|
+
const expire = s.expires_at ? chalk2.dim(
|
|
510
|
+
` expires ${new Date(s.expires_at * 1e3).toLocaleDateString()}`
|
|
511
|
+
) : "";
|
|
239
512
|
console.log(
|
|
240
|
-
` ${
|
|
513
|
+
` ${chalk2.bold(s.name)}${lock}${expire} ${chalk2.dim(formatBytes(s.total_size))} ${chalk2.cyan(s.url)}`
|
|
241
514
|
);
|
|
242
515
|
}
|
|
243
|
-
console.log(
|
|
516
|
+
console.log(chalk2.dim(`
|
|
244
517
|
${sites.length} site(s)`));
|
|
245
518
|
} catch (e) {
|
|
246
|
-
console.error(
|
|
519
|
+
console.error(chalk2.red(e.message));
|
|
247
520
|
process.exit(1);
|
|
248
521
|
}
|
|
249
522
|
});
|
|
250
523
|
program.command("info").description("Show site details").argument("<name>", "Site name").action(async (name) => {
|
|
251
524
|
try {
|
|
252
525
|
const site = await api.getSite(name);
|
|
253
|
-
console.log(`${
|
|
254
|
-
console.log(` URL: ${
|
|
255
|
-
console.log(
|
|
256
|
-
|
|
526
|
+
console.log(`${chalk2.bold(site.name)}`);
|
|
527
|
+
console.log(` URL: ${chalk2.cyan(site.url)}`);
|
|
528
|
+
console.log(
|
|
529
|
+
` Password: ${site.password_protected ? chalk2.yellow("yes") : "no"}`
|
|
530
|
+
);
|
|
531
|
+
console.log(
|
|
532
|
+
` Expires: ${site.expires_at ? new Date(site.expires_at * 1e3).toISOString() : "never"}`
|
|
533
|
+
);
|
|
257
534
|
console.log(` Files: ${site.files.length}`);
|
|
258
|
-
console.log(
|
|
259
|
-
|
|
535
|
+
console.log(
|
|
536
|
+
` Created: ${new Date(site.created_at * 1e3).toISOString()}`
|
|
537
|
+
);
|
|
538
|
+
console.log(
|
|
539
|
+
` Updated: ${new Date(site.updated_at * 1e3).toISOString()}`
|
|
540
|
+
);
|
|
260
541
|
if (site.files.length > 0) {
|
|
261
542
|
console.log(`
|
|
262
|
-
${
|
|
543
|
+
${chalk2.dim("Files:")}`);
|
|
263
544
|
for (const f of site.files) {
|
|
264
|
-
console.log(` ${f.path} ${
|
|
545
|
+
console.log(` ${f.path} ${chalk2.dim(formatBytes(f.size))}`);
|
|
265
546
|
}
|
|
266
547
|
}
|
|
267
548
|
if (site.tokens.length > 0) {
|
|
268
549
|
console.log(`
|
|
269
|
-
${
|
|
550
|
+
${chalk2.dim("Tokens:")}`);
|
|
270
551
|
for (const t of site.tokens) {
|
|
271
552
|
const note = t.note ? ` (${t.note})` : "";
|
|
272
553
|
const exp = t.expires_at ? ` expires ${new Date(t.expires_at * 1e3).toLocaleDateString()}` : "";
|
|
273
|
-
console.log(` ${t.id}${note}${
|
|
554
|
+
console.log(` ${t.id}${note}${chalk2.dim(exp)}`);
|
|
274
555
|
}
|
|
275
556
|
}
|
|
276
557
|
} catch (e) {
|
|
277
|
-
console.error(
|
|
558
|
+
console.error(chalk2.red(e.message));
|
|
278
559
|
process.exit(1);
|
|
279
560
|
}
|
|
280
561
|
});
|
|
@@ -282,7 +563,7 @@ program.command("delete").alias("rm").description("Delete a site and all its fil
|
|
|
282
563
|
const spinner = ora(`Deleting ${name}...`).start();
|
|
283
564
|
try {
|
|
284
565
|
await api.deleteSite(name);
|
|
285
|
-
spinner.succeed(`Deleted ${
|
|
566
|
+
spinner.succeed(`Deleted ${chalk2.bold(name)}`);
|
|
286
567
|
} catch (e) {
|
|
287
568
|
spinner.fail(e.message);
|
|
288
569
|
process.exit(1);
|
|
@@ -292,38 +573,43 @@ program.command("password").description("Set or remove site password").argument(
|
|
|
292
573
|
try {
|
|
293
574
|
const site = await api.updateSite(name, { password: password ?? null });
|
|
294
575
|
if (site.password_protected) {
|
|
295
|
-
console.log(
|
|
576
|
+
console.log(chalk2.green(`\u2713 Password set for ${chalk2.bold(name)}`));
|
|
296
577
|
} else {
|
|
297
|
-
console.log(
|
|
578
|
+
console.log(chalk2.green(`\u2713 Password removed from ${chalk2.bold(name)}`));
|
|
298
579
|
}
|
|
299
580
|
} catch (e) {
|
|
300
|
-
console.error(
|
|
581
|
+
console.error(chalk2.red(e.message));
|
|
301
582
|
process.exit(1);
|
|
302
583
|
}
|
|
303
584
|
});
|
|
304
585
|
var tokenCmd = program.command("token").description("Manage share tokens");
|
|
305
|
-
tokenCmd.command("create").description("Create a share token").argument("<site>", "Site name").option("-n, --note <note>", "Token note").option("-e, --expires <seconds>", "Expire after N seconds", parseInt).action(
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
586
|
+
tokenCmd.command("create").description("Create a share token").argument("<site>", "Site name").option("-n, --note <note>", "Token note").option("-e, --expires <seconds>", "Expire after N seconds", parseInt).action(
|
|
587
|
+
async (site, opts) => {
|
|
588
|
+
try {
|
|
589
|
+
const token = await api.createToken(site, {
|
|
590
|
+
note: opts.note,
|
|
591
|
+
expires_in: opts.expires
|
|
592
|
+
});
|
|
593
|
+
console.log(chalk2.green("\u2713 Token created"));
|
|
594
|
+
console.log(` ID: ${token.id}`);
|
|
595
|
+
console.log(` URL: ${chalk2.cyan(token.url)}`);
|
|
596
|
+
if (token.note) console.log(` Note: ${token.note}`);
|
|
597
|
+
if (token.expires_at)
|
|
598
|
+
console.log(
|
|
599
|
+
` Expires: ${new Date(token.expires_at * 1e3).toISOString()}`
|
|
600
|
+
);
|
|
601
|
+
} catch (e) {
|
|
602
|
+
console.error(chalk2.red(e.message));
|
|
603
|
+
process.exit(1);
|
|
604
|
+
}
|
|
319
605
|
}
|
|
320
|
-
|
|
606
|
+
);
|
|
321
607
|
tokenCmd.command("revoke").description("Revoke a share token").argument("<site>", "Site name").argument("<id>", "Token ID").action(async (site, id) => {
|
|
322
608
|
try {
|
|
323
609
|
await api.revokeToken(site, id);
|
|
324
|
-
console.log(
|
|
610
|
+
console.log(chalk2.green(`\u2713 Token ${id} revoked`));
|
|
325
611
|
} catch (e) {
|
|
326
|
-
console.error(
|
|
612
|
+
console.error(chalk2.red(e.message));
|
|
327
613
|
process.exit(1);
|
|
328
614
|
}
|
|
329
615
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@within-7/jetr",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "CLI for Jetr static site hosting",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"commander": "^13",
|
|
17
17
|
"chalk": "^5",
|
|
18
18
|
"ora": "^8",
|
|
19
|
-
"glob": "^11"
|
|
19
|
+
"glob": "^11",
|
|
20
|
+
"ignore": "^6"
|
|
20
21
|
},
|
|
21
22
|
"devDependencies": {
|
|
22
23
|
"tsup": "^8",
|