@within-7/jetr 0.3.0 → 0.4.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.
Files changed (3) hide show
  1. package/README.md +26 -19
  2. package/dist/cli.js +74 -17
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -17,29 +17,34 @@ npx @within-7/jetr deploy my-site ./dist
17
17
  ## Quick Start
18
18
 
19
19
  ```bash
20
- # 1. Login with your JWT token (from auth-gateway)
21
- jetr login <token>
22
-
23
- # 2. Create a site
24
- jetr create my-blog
20
+ # 1. Login
21
+ jetr login
22
+ # Username: lib
23
+ # Password: ****
24
+ # Logged in as lib
25
+
26
+ # 2. Deploy current directory
27
+ jetr
28
+ # Site name [my-blog]:
29
+ # ✔ Deployed!
30
+ # URL: https://my-blog.jetr.within-7.com
25
31
 
26
- # 3. Deploy
27
- jetr deploy my-blog ./dist
32
+ # 3. Re-deploy (remembers site name)
33
+ jetr
34
+ # Using saved site: my-blog
28
35
  # ✔ Deployed!
29
- # URL: https://my-blog.jetr.within-7.com
30
- # Uploaded: 12 files
31
- # Size: 1.2 MB
32
36
  ```
33
37
 
34
38
  ## Commands
35
39
 
36
- ### `jetr login <token>`
40
+ ### `jetr login [user] [password]`
37
41
 
38
- Save JWT token for authentication. Token is stored in `~/.jetr/config.json`.
42
+ Login with username and password. Credentials are verified against auth-gateway, and the JWT token is saved locally.
39
43
 
40
44
  ```bash
41
- jetr login eyJhbGciOiJIUzI1NiJ9...
42
- jetr login <token> --api-url https://custom-api.example.com
45
+ jetr login # interactive prompts
46
+ jetr login lib mypassword # non-interactive
47
+ jetr login --token <jwt> # direct token (for CI)
43
48
  ```
44
49
 
45
50
  ### `jetr create <name>`
@@ -52,16 +57,18 @@ jetr create preview-site --password secret123
52
57
  jetr create temp-demo --expires 86400 # expires in 24h
53
58
  ```
54
59
 
55
- ### `jetr deploy <name> [directory]`
60
+ ### `jetr [directory] [name]`
56
61
 
57
- Deploy a directory to a site. Uses incremental upload only changed files are uploaded.
62
+ Deploy a directory. This is the default commandjust run `jetr` in your project.
58
63
 
59
64
  ```bash
60
- jetr deploy my-blog ./dist
61
- jetr deploy my-blog . # deploy current directory
62
- jetr deploy my-blog # defaults to current directory
65
+ jetr # deploy current dir, name from .jetrrc or prompt
66
+ jetr ./dist # deploy ./dist
67
+ jetr ./dist my-blog # deploy ./dist as my-blog
63
68
  ```
64
69
 
70
+ Site is auto-created if it doesn't exist. Name is saved to `.jetrrc` for next time. Uses incremental upload (10x concurrent) — only changed files are uploaded.
71
+
65
72
  ### `jetr list`
66
73
 
67
74
  List your sites.
package/dist/cli.js CHANGED
@@ -14,6 +14,7 @@ import { homedir } from "os";
14
14
  var CONFIG_DIR = join(homedir(), ".jetr");
15
15
  var CONFIG_FILE = join(CONFIG_DIR, "config.json");
16
16
  var DEFAULT_CONFIG = {
17
+ authUrl: "https://api.within-7.com",
17
18
  apiUrl: "https://jetr-api.lixilei.workers.dev"
18
19
  };
19
20
  function loadConfig() {
@@ -39,6 +40,20 @@ function getToken() {
39
40
  }
40
41
  return config.token;
41
42
  }
43
+ async function login(user, password) {
44
+ const config = loadConfig();
45
+ const res = await fetch(`${config.authUrl}/user/login`, {
46
+ method: "POST",
47
+ headers: { "Content-Type": "application/json" },
48
+ body: JSON.stringify({ user, password })
49
+ });
50
+ const body = await res.json();
51
+ if (!res.ok || !body.token) {
52
+ throw new Error(body.error || `Login failed: ${res.status}`);
53
+ }
54
+ saveConfig({ user: body.user || user, token: body.token });
55
+ return { user: body.user || user, token: body.token };
56
+ }
42
57
 
43
58
  // src/api.ts
44
59
  async function request(path, options = {}) {
@@ -54,6 +69,9 @@ async function request(path, options = {}) {
54
69
  async function json(path, options = {}) {
55
70
  const res = await request(path, options);
56
71
  const body = await res.json();
72
+ if (res.status === 401) {
73
+ throw new Error("Token expired or invalid. Run: jetr login");
74
+ }
57
75
  if (!res.ok) {
58
76
  throw new Error(body.error || `API error: ${res.status}`);
59
77
  }
@@ -279,12 +297,19 @@ async function deploy(siteName, directory, onProgress) {
279
297
  `Upload: ${diff.upload.length}, Delete: ${diff.delete.length}, Unchanged: ${diff.unchanged.length}`
280
298
  );
281
299
  if (diff.upload.length > 0) {
282
- for (let i = 0; i < diff.upload.length; i++) {
283
- const fp = diff.upload[i];
300
+ const CONCURRENCY = 10;
301
+ let completed = 0;
302
+ const total = diff.upload.length;
303
+ const uploadOne = async (fp) => {
284
304
  const absPath = join3(absDir, fp);
285
305
  const content = readFileSync3(absPath);
286
- onProgress?.(`Uploading (${i + 1}/${diff.upload.length}) ${fp}`);
287
306
  await api.uploadFile(siteName, fp, content, diff.deploy_id, manifest[fp].hash);
307
+ completed++;
308
+ onProgress?.(`Uploading (${completed}/${total}) ${fp}`);
309
+ };
310
+ for (let i = 0; i < total; i += CONCURRENCY) {
311
+ const batch = diff.upload.slice(i, i + CONCURRENCY);
312
+ await Promise.all(batch.map(uploadOne));
288
313
  }
289
314
  }
290
315
  onProgress?.("Finalizing...");
@@ -397,10 +422,7 @@ program.argument("[directory]", "Directory to deploy (default: current directory
397
422
  const config = loadConfig();
398
423
  if (!config.token) {
399
424
  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
- );
425
+ console.error(`Run: ${chalk2.cyan("jetr login")}`);
404
426
  process.exit(1);
405
427
  }
406
428
  let siteName = await resolveSiteName(dir, name);
@@ -454,18 +476,53 @@ program.argument("[directory]", "Directory to deploy (default: current directory
454
476
  process.exit(1);
455
477
  }
456
478
  });
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) => {
458
- saveConfig({ token, ...opts.apiUrl ? { apiUrl: opts.apiUrl } : {} });
459
- console.log(chalk2.green("\u2713 Token saved to ~/.jetr/config.json"));
479
+ program.command("login").description("Login with username and password").argument("[user]", "Username").argument("[password]", "Password").option("--token <token>", "Login directly with a JWT token").action(async (user, password, opts) => {
480
+ if (opts?.token) {
481
+ saveConfig({ token: opts.token });
482
+ console.log(chalk2.green("\u2713 Token saved"));
483
+ return;
484
+ }
485
+ const { createInterface: createInterface2 } = await import("readline");
486
+ if (!user) {
487
+ const rl = createInterface2({ input: process.stdin, output: process.stdout });
488
+ user = await new Promise((res) => {
489
+ rl.question("Username: ", (a) => {
490
+ rl.close();
491
+ res(a.trim());
492
+ });
493
+ });
494
+ }
495
+ if (!password) {
496
+ const rl = createInterface2({ input: process.stdin, output: process.stdout });
497
+ password = await new Promise((res) => {
498
+ rl.question("Password: ", (a) => {
499
+ rl.close();
500
+ res(a.trim());
501
+ });
502
+ });
503
+ }
504
+ if (!user || !password) {
505
+ console.error(chalk2.red("Username and password required"));
506
+ process.exit(1);
507
+ }
508
+ const spinner = ora("Logging in...").start();
509
+ try {
510
+ const result = await login(user, password);
511
+ spinner.succeed(`Logged in as ${chalk2.bold(result.user)}`);
512
+ } catch (e) {
513
+ spinner.fail(e.message);
514
+ process.exit(1);
515
+ }
460
516
  });
461
- program.command("whoami").description("Show current config").action(() => {
517
+ program.command("whoami").description("Show current login status").action(() => {
462
518
  const config = loadConfig();
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
- );
519
+ if (!config.token) {
520
+ console.log(chalk2.red("Not logged in."));
521
+ console.log(`Run: ${chalk2.cyan("jetr login")}`);
522
+ return;
523
+ }
524
+ console.log(`User: ${chalk2.bold(config.user || "unknown")}`);
525
+ console.log(`Token: ${config.token.slice(0, 20)}...`);
469
526
  });
470
527
  program.command("init").description("Create .jetrignore with default patterns").argument("[directory]", "Target directory", ".").action(async (directory) => {
471
528
  const created = createJetrignore(resolve2(directory));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@within-7/jetr",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "CLI for Jetr static site hosting",
5
5
  "type": "module",
6
6
  "bin": {