@aaricchen1991/n2-cli 1.0.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 (92) hide show
  1. package/README.md +92 -0
  2. package/assets/deploy/deploy.sh +172 -0
  3. package/assets/deploy/domains.yaml +18 -0
  4. package/assets/deploy/lib/common.sh +62 -0
  5. package/assets/deploy/nginx/n2.conf +162 -0
  6. package/assets/deploy/server-setup.sh +285 -0
  7. package/assets/deploy/ssl/README.md +320 -0
  8. package/assets/deploy/ssl/check-and-setup-ssl.sh +222 -0
  9. package/assets/deploy/ssl/domains.txt +3 -0
  10. package/assets/deploy/ssl/renew-ssl.sh +236 -0
  11. package/assets/deploy/ssl/setup-ssl.sh +474 -0
  12. package/dist/cli.d.ts +7 -0
  13. package/dist/cli.d.ts.map +1 -0
  14. package/dist/cli.js +186 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/commands/config.d.ts +29 -0
  17. package/dist/commands/config.d.ts.map +1 -0
  18. package/dist/commands/config.js +134 -0
  19. package/dist/commands/config.js.map +1 -0
  20. package/dist/commands/config.test.d.ts +2 -0
  21. package/dist/commands/config.test.d.ts.map +1 -0
  22. package/dist/commands/config.test.js +215 -0
  23. package/dist/commands/config.test.js.map +1 -0
  24. package/dist/commands/init.d.ts +10 -0
  25. package/dist/commands/init.d.ts.map +1 -0
  26. package/dist/commands/init.js +106 -0
  27. package/dist/commands/init.js.map +1 -0
  28. package/dist/commands/init.test.d.ts +2 -0
  29. package/dist/commands/init.test.d.ts.map +1 -0
  30. package/dist/commands/init.test.js +70 -0
  31. package/dist/commands/init.test.js.map +1 -0
  32. package/dist/commands/nginx.d.ts +10 -0
  33. package/dist/commands/nginx.d.ts.map +1 -0
  34. package/dist/commands/nginx.js +72 -0
  35. package/dist/commands/nginx.js.map +1 -0
  36. package/dist/commands/nginx.test.d.ts +2 -0
  37. package/dist/commands/nginx.test.d.ts.map +1 -0
  38. package/dist/commands/nginx.test.js +75 -0
  39. package/dist/commands/nginx.test.js.map +1 -0
  40. package/dist/commands/ssl-logs.d.ts +17 -0
  41. package/dist/commands/ssl-logs.d.ts.map +1 -0
  42. package/dist/commands/ssl-logs.js +55 -0
  43. package/dist/commands/ssl-logs.js.map +1 -0
  44. package/dist/commands/ssl-logs.test.d.ts +2 -0
  45. package/dist/commands/ssl-logs.test.d.ts.map +1 -0
  46. package/dist/commands/ssl-logs.test.js +54 -0
  47. package/dist/commands/ssl-logs.test.js.map +1 -0
  48. package/dist/commands/ssl.d.ts +16 -0
  49. package/dist/commands/ssl.d.ts.map +1 -0
  50. package/dist/commands/ssl.js +105 -0
  51. package/dist/commands/ssl.js.map +1 -0
  52. package/dist/commands/ssl.test.d.ts +2 -0
  53. package/dist/commands/ssl.test.d.ts.map +1 -0
  54. package/dist/commands/ssl.test.js +95 -0
  55. package/dist/commands/ssl.test.js.map +1 -0
  56. package/dist/lib/config-store.d.ts +14 -0
  57. package/dist/lib/config-store.d.ts.map +1 -0
  58. package/dist/lib/config-store.js +111 -0
  59. package/dist/lib/config-store.js.map +1 -0
  60. package/dist/lib/config-store.test.d.ts +2 -0
  61. package/dist/lib/config-store.test.d.ts.map +1 -0
  62. package/dist/lib/config-store.test.js +173 -0
  63. package/dist/lib/config-store.test.js.map +1 -0
  64. package/dist/lib/domains.d.ts +37 -0
  65. package/dist/lib/domains.d.ts.map +1 -0
  66. package/dist/lib/domains.js +134 -0
  67. package/dist/lib/domains.js.map +1 -0
  68. package/dist/lib/domains.test.d.ts +2 -0
  69. package/dist/lib/domains.test.d.ts.map +1 -0
  70. package/dist/lib/domains.test.js +141 -0
  71. package/dist/lib/domains.test.js.map +1 -0
  72. package/dist/lib/logger.d.ts +19 -0
  73. package/dist/lib/logger.d.ts.map +1 -0
  74. package/dist/lib/logger.js +58 -0
  75. package/dist/lib/logger.js.map +1 -0
  76. package/dist/lib/nginx.d.ts +7 -0
  77. package/dist/lib/nginx.d.ts.map +1 -0
  78. package/dist/lib/nginx.js +86 -0
  79. package/dist/lib/nginx.js.map +1 -0
  80. package/dist/lib/nginx.test.d.ts +2 -0
  81. package/dist/lib/nginx.test.d.ts.map +1 -0
  82. package/dist/lib/nginx.test.js +46 -0
  83. package/dist/lib/nginx.test.js.map +1 -0
  84. package/dist/lib/paths.d.ts +13 -0
  85. package/dist/lib/paths.d.ts.map +1 -0
  86. package/dist/lib/paths.js +36 -0
  87. package/dist/lib/paths.js.map +1 -0
  88. package/dist/lib/paths.test.d.ts +2 -0
  89. package/dist/lib/paths.test.d.ts.map +1 -0
  90. package/dist/lib/paths.test.js +52 -0
  91. package/dist/lib/paths.test.js.map +1 -0
  92. package/package.json +34 -0
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Unified logger: consola for console, optional file output for SSL log.
3
+ */
4
+ export type LogLevel = "info" | "warn" | "error";
5
+ export declare function setLogFile(filePath: string): void;
6
+ export declare function getLogFilePath(): string | null;
7
+ /** Info message (consola + optional file). */
8
+ export declare function info(msg: string): void;
9
+ /** Success message. */
10
+ export declare function success(msg: string): void;
11
+ /** Warning message. */
12
+ export declare function warn(msg: string): void;
13
+ /** Error message. */
14
+ export declare function error(msg: string): void;
15
+ /** Append a raw line to the log file (no timestamp/level prefix). */
16
+ export declare function appendRaw(line: string): void;
17
+ /** Append a structured SSL log line (time, action, domain, result, message). */
18
+ export declare function appendSslLog(action: "create" | "renew" | "success" | "failure" | "run", domain: string, result: "ok" | "fail", message?: string): void;
19
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAIjD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAE9C;AAiBD,8CAA8C;AAC9C,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAGtC;AAED,uBAAuB;AACvB,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAGzC;AAED,uBAAuB;AACvB,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAGtC;AAED,qBAAqB;AACrB,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAGvC;AAED,qEAAqE;AACrE,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAC1B,MAAM,EAAE,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,KAAK,EAC1D,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,IAAI,GAAG,MAAM,EACrB,OAAO,CAAC,EAAE,MAAM,GACf,IAAI,CAKN"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Unified logger: consola for console, optional file output for SSL log.
3
+ */
4
+ import consola from "consola";
5
+ import fs from "node:fs";
6
+ import path from "node:path";
7
+ let logFile = null;
8
+ export function setLogFile(filePath) {
9
+ logFile = filePath;
10
+ }
11
+ export function getLogFilePath() {
12
+ return logFile;
13
+ }
14
+ function timestamp() {
15
+ return new Date().toISOString().replace("T", " ").slice(0, 19);
16
+ }
17
+ function writeToLogFileSync(line) {
18
+ if (!logFile)
19
+ return;
20
+ try {
21
+ const dir = path.dirname(logFile);
22
+ fs.mkdirSync(dir, { recursive: true });
23
+ fs.appendFileSync(logFile, line + "\n");
24
+ }
25
+ catch {
26
+ // ignore
27
+ }
28
+ }
29
+ /** Info message (consola + optional file). */
30
+ export function info(msg) {
31
+ consola.info(msg);
32
+ writeToLogFileSync(`${timestamp()} [INFO] ${msg}`);
33
+ }
34
+ /** Success message. */
35
+ export function success(msg) {
36
+ consola.success(msg);
37
+ writeToLogFileSync(`${timestamp()} [INFO] ${msg}`);
38
+ }
39
+ /** Warning message. */
40
+ export function warn(msg) {
41
+ consola.warn(msg);
42
+ writeToLogFileSync(`${timestamp()} [WARN] ${msg}`);
43
+ }
44
+ /** Error message. */
45
+ export function error(msg) {
46
+ consola.error(msg);
47
+ writeToLogFileSync(`${timestamp()} [ERROR] ${msg}`);
48
+ }
49
+ /** Append a raw line to the log file (no timestamp/level prefix). */
50
+ export function appendRaw(line) {
51
+ writeToLogFileSync(line);
52
+ }
53
+ /** Append a structured SSL log line (time, action, domain, result, message). */
54
+ export function appendSslLog(action, domain, result, message) {
55
+ const line = [timestamp(), "ssl", action, domain, result, message ?? ""].join("\t");
56
+ writeToLogFileSync(line);
57
+ }
58
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAI7B,IAAI,OAAO,GAAkB,IAAI,CAAC;AAElC,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,OAAO,GAAG,QAAQ,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAY;IACtC,IAAI,CAAC,OAAO;QAAE,OAAO;IACrB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAClC,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;AACH,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,IAAI,CAAC,GAAW;IAC9B,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClB,kBAAkB,CAAC,GAAG,SAAS,EAAE,WAAW,GAAG,EAAE,CAAC,CAAC;AACrD,CAAC;AAED,uBAAuB;AACvB,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACrB,kBAAkB,CAAC,GAAG,SAAS,EAAE,WAAW,GAAG,EAAE,CAAC,CAAC;AACrD,CAAC;AAED,uBAAuB;AACvB,MAAM,UAAU,IAAI,CAAC,GAAW;IAC9B,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClB,kBAAkB,CAAC,GAAG,SAAS,EAAE,WAAW,GAAG,EAAE,CAAC,CAAC;AACrD,CAAC;AAED,qBAAqB;AACrB,MAAM,UAAU,KAAK,CAAC,GAAW;IAC/B,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,kBAAkB,CAAC,GAAG,SAAS,EAAE,YAAY,GAAG,EAAE,CAAC,CAAC;AACtD,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,kBAAkB,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,YAAY,CAC1B,MAA0D,EAC1D,MAAc,EACd,MAAqB,EACrB,OAAgB;IAEhB,MAAM,IAAI,GAAG,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAC3E,IAAI,CACL,CAAC;IACF,kBAAkB,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Generate nginx n2.conf from domains config.
3
+ * 后端与前端同域:每个应用域名下 /api 反向代理到后端,不再为 API 单独配置 server。
4
+ */
5
+ import type { DomainsConfig } from "./domains.js";
6
+ export declare function generateNginxConf(config: DomainsConfig): string;
7
+ //# sourceMappingURL=nginx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nginx.d.ts","sourceRoot":"","sources":["../../src/lib/nginx.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AA8DlD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CA4B/D"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Generate nginx n2.conf from domains config.
3
+ * 后端与前端同域:每个应用域名下 /api 反向代理到后端,不再为 API 单独配置 server。
4
+ */
5
+ function appServerBlock(domain, appName, backendPort) {
6
+ const root = `/var/www/${appName}`;
7
+ return `# HTTPS - ${appName} (${domain}),/api 代理到后端
8
+ server {
9
+ listen 443 ssl http2;
10
+ server_name ${domain};
11
+
12
+ root ${root};
13
+ index index.html;
14
+
15
+ ssl_certificate /etc/nginx/ssl/${domain}.crt;
16
+ ssl_certificate_key /etc/nginx/ssl/${domain}.key;
17
+
18
+ ssl_protocols TLSv1.2 TLSv1.3;
19
+ ssl_ciphers HIGH:!aNULL:!MD5;
20
+ ssl_prefer_server_ciphers on;
21
+ ssl_session_cache shared:SSL:10m;
22
+ ssl_session_timeout 10m;
23
+
24
+ access_log /var/log/nginx/${appName}-access.log;
25
+ error_log /var/log/nginx/${appName}-error.log;
26
+
27
+ # 后端接口:优先匹配 /api,与前端同域
28
+ location /api {
29
+ proxy_pass http://127.0.0.1:${backendPort};
30
+ proxy_http_version 1.1;
31
+ proxy_set_header Host $host;
32
+ proxy_set_header X-Real-IP $remote_addr;
33
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
34
+ proxy_set_header X-Forwarded-Proto $scheme;
35
+ }
36
+
37
+ gzip on;
38
+ gzip_vary on;
39
+ gzip_min_length 1024;
40
+ gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
41
+
42
+ location ~* \\.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot|mp3)$ {
43
+ expires 1y;
44
+ add_header Cache-Control "public, immutable";
45
+ access_log off;
46
+ }
47
+
48
+ location / {
49
+ try_files $uri $uri/ /index.html;
50
+ }
51
+
52
+ add_header X-Frame-Options "SAMEORIGIN" always;
53
+ add_header X-Content-Type-Options "nosniff" always;
54
+ add_header X-XSS-Protection "1; mode=block" always;
55
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
56
+ }
57
+
58
+ `;
59
+ }
60
+ export function generateNginxConf(config) {
61
+ const blocks = [];
62
+ const apiPort = config.api.backend_port ?? 3000;
63
+ for (const d of config.admin.domains) {
64
+ blocks.push(appServerBlock(d, "admin", apiPort));
65
+ }
66
+ for (const d of config.tenant.domains) {
67
+ blocks.push(appServerBlock(d, "tenant", apiPort));
68
+ }
69
+ for (const d of config.client.domains) {
70
+ blocks.push(appServerBlock(d, "client", apiPort));
71
+ }
72
+ const allDomains = [
73
+ ...config.admin.domains,
74
+ ...config.tenant.domains,
75
+ ...config.client.domains,
76
+ ];
77
+ const redirectBlock = `# HTTP to HTTPS redirect
78
+ server {
79
+ listen 80;
80
+ server_name ${allDomains.join(" ")};
81
+ return 301 https://$host$request_uri;
82
+ }
83
+ `;
84
+ return blocks.join("\n") + "\n" + redirectBlock;
85
+ }
86
+ //# sourceMappingURL=nginx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nginx.js","sourceRoot":"","sources":["../../src/lib/nginx.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,SAAS,cAAc,CACrB,MAAc,EACd,OAAe,EACf,WAAmB;IAEnB,MAAM,IAAI,GAAG,YAAY,OAAO,EAAE,CAAC;IACnC,OAAO,aAAa,OAAO,KAAK,MAAM;;;kBAGtB,MAAM;;WAEb,IAAI;;;qCAGsB,MAAM;yCACF,MAAM;;;;;;;;gCAQf,OAAO;+BACR,OAAO;;;;sCAIA,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BhD,CAAC;AACF,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAqB;IACrD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC;IAEhD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IACpD,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,UAAU,GAAG;QACjB,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO;QACvB,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO;QACxB,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO;KACzB,CAAC;IACF,MAAM,aAAa,GAAG;;;kBAGN,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC;;;CAGrC,CAAC;IAEA,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,aAAa,CAAC;AAClD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=nginx.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nginx.test.d.ts","sourceRoot":"","sources":["../../src/lib/nginx.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,46 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { generateNginxConf } from "./nginx.js";
3
+ describe("nginx", () => {
4
+ it("generates empty redirect block when no domains", () => {
5
+ const config = {
6
+ api: { backend_port: 3000, domains: [] },
7
+ admin: { domains: [] },
8
+ tenant: { domains: [] },
9
+ client: { domains: [] },
10
+ };
11
+ const out = generateNginxConf(config);
12
+ expect(out).toContain("listen 80");
13
+ expect(out).toContain("return 301 https://");
14
+ expect(out).not.toContain("server_name admin");
15
+ });
16
+ it("generates HTTPS server blocks for admin/tenant/client with backend_port", () => {
17
+ const config = {
18
+ api: { backend_port: 4000, domains: [] },
19
+ admin: { domains: ["admin.example.com"] },
20
+ tenant: { domains: ["tenant.example.com"] },
21
+ client: { domains: ["client.example.com"] },
22
+ };
23
+ const out = generateNginxConf(config);
24
+ expect(out).toContain("server_name admin.example.com");
25
+ expect(out).toContain("server_name tenant.example.com");
26
+ expect(out).toContain("server_name client.example.com");
27
+ expect(out).toContain("proxy_pass http://127.0.0.1:4000");
28
+ expect(out).toContain("/var/www/admin");
29
+ expect(out).toContain("/var/www/tenant");
30
+ expect(out).toContain("/var/www/client");
31
+ expect(out).toContain("location /api");
32
+ expect(out).toContain("ssl_certificate ");
33
+ expect(out).toContain("server_name admin.example.com tenant.example.com client.example.com");
34
+ });
35
+ it("uses default port 3000 when backend_port not set", () => {
36
+ const config = {
37
+ api: { backend_port: 3000, domains: [] },
38
+ admin: { domains: ["a.com"] },
39
+ tenant: { domains: [] },
40
+ client: { domains: [] },
41
+ };
42
+ const out = generateNginxConf(config);
43
+ expect(out).toContain("proxy_pass http://127.0.0.1:3000");
44
+ });
45
+ });
46
+ //# sourceMappingURL=nginx.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nginx.test.js","sourceRoot":"","sources":["../../src/lib/nginx.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAG/C,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;IACrB,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,MAAM,GAAkB;YAC5B,GAAG,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;YACxC,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;YACtB,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;YACvB,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;SACxB,CAAC;QACF,MAAM,GAAG,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACnC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;QAC7C,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QACjF,MAAM,MAAM,GAAkB;YAC5B,GAAG,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;YACxC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,mBAAmB,CAAC,EAAE;YACzC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,oBAAoB,CAAC,EAAE;YAC3C,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,oBAAoB,CAAC,EAAE;SAC5C,CAAC;QACF,MAAM,GAAG,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,+BAA+B,CAAC,CAAC;QACvD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,gCAAgC,CAAC,CAAC;QACxD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,gCAAgC,CAAC,CAAC;QACxD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,kCAAkC,CAAC,CAAC;QAC1D,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACxC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QACzC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QACzC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QACvC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QAC1C,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,qEAAqE,CAAC,CAAC;IAC/F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,MAAM,GAAkB;YAC5B,GAAG,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;YACxC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE;YAC7B,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;YACvB,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;SACxB,CAAC;QACF,MAAM,GAAG,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,kCAAkC,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Resolve deploy scripts directory: for init/ssl we need deploy.sh, server-setup.sh, ssl/*, lib/*.
3
+ * 1) N2_DEPLOY_SCRIPTS_DIR env
4
+ * 2) package assets (assets/deploy in this package)
5
+ *
6
+ * Deploy config: default path ~/.deploy/config.yaml (ALIYUN keys + domains).
7
+ */
8
+ /** Default deploy config path: ~/.deploy/config.yaml */
9
+ export declare function getDefaultConfigPath(): string;
10
+ /** Resolve config path: expand ~ to homedir, otherwise resolve against cwd. */
11
+ export declare function resolveConfigPath(configPath: string, cwd?: string): string;
12
+ export declare function getScriptsDir(_cwd?: string): string | null;
13
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/lib/paths.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,wDAAwD;AACxD,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,GAAG,GAAE,MAAsB,GAC1B,MAAM,CAKR;AAID,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW1D"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Resolve deploy scripts directory: for init/ssl we need deploy.sh, server-setup.sh, ssl/*, lib/*.
3
+ * 1) N2_DEPLOY_SCRIPTS_DIR env
4
+ * 2) package assets (assets/deploy in this package)
5
+ *
6
+ * Deploy config: default path ~/.deploy/config.yaml (ALIYUN keys + domains).
7
+ */
8
+ import fs from "node:fs";
9
+ import os from "node:os";
10
+ import path from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+ /** Default deploy config path: ~/.deploy/config.yaml */
13
+ export function getDefaultConfigPath() {
14
+ return path.join(os.homedir(), ".deploy", "config.yaml");
15
+ }
16
+ /** Resolve config path: expand ~ to homedir, otherwise resolve against cwd. */
17
+ export function resolveConfigPath(configPath, cwd = process.cwd()) {
18
+ if (configPath.startsWith("~/") || configPath === "~") {
19
+ return path.join(os.homedir(), configPath.slice(1) || "");
20
+ }
21
+ return path.resolve(cwd, configPath);
22
+ }
23
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
24
+ export function getScriptsDir(_cwd) {
25
+ const envDir = process.env.N2_DEPLOY_SCRIPTS_DIR;
26
+ if (envDir && fs.existsSync(path.join(envDir, "deploy.sh"))) {
27
+ return envDir;
28
+ }
29
+ const packageRoot = path.join(__dirname, "..", "..");
30
+ const assetsDeploy = path.join(packageRoot, "assets", "deploy");
31
+ if (fs.existsSync(path.join(assetsDeploy, "deploy.sh"))) {
32
+ return assetsDeploy;
33
+ }
34
+ return null;
35
+ }
36
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/lib/paths.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,wDAAwD;AACxD,MAAM,UAAU,oBAAoB;IAClC,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;AAC3D,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,iBAAiB,CAC/B,UAAkB,EAClB,MAAc,OAAO,CAAC,GAAG,EAAE;IAE3B,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,UAAU,KAAK,GAAG,EAAE,CAAC;QACtD,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE/D,MAAM,UAAU,aAAa,CAAC,IAAa;IACzC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;IACjD,IAAI,MAAM,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC;QAC5D,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACrD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAChE,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC;QACxD,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=paths.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.test.d.ts","sourceRoot":"","sources":["../../src/lib/paths.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,52 @@
1
+ import { describe, it, expect, vi, afterEach } from "vitest";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ import fs from "node:fs";
5
+ import { getDefaultConfigPath, resolveConfigPath, getScriptsDir, } from "./paths.js";
6
+ describe("paths", () => {
7
+ describe("getDefaultConfigPath", () => {
8
+ it("returns homedir/.deploy/config.yaml", () => {
9
+ const p = getDefaultConfigPath();
10
+ expect(p).toBe(path.join(os.homedir(), ".deploy", "config.yaml"));
11
+ });
12
+ });
13
+ describe("resolveConfigPath", () => {
14
+ it("expands ~ to homedir", () => {
15
+ const p = resolveConfigPath("~/.deploy/config.yaml");
16
+ expect(p).toBe(path.join(os.homedir(), ".deploy", "config.yaml"));
17
+ });
18
+ it("resolves relative path against cwd", () => {
19
+ const cwd = "/tmp/foo";
20
+ const p = resolveConfigPath("config.yaml", cwd);
21
+ expect(p).toBe(path.resolve(cwd, "config.yaml"));
22
+ });
23
+ it("handles absolute path", () => {
24
+ const abs = "/etc/deploy/config.yaml";
25
+ const p = resolveConfigPath(abs, "/other");
26
+ expect(p).toBe(path.resolve("/other", abs));
27
+ });
28
+ });
29
+ describe("getScriptsDir", () => {
30
+ const origEnv = process.env.N2_DEPLOY_SCRIPTS_DIR;
31
+ afterEach(() => {
32
+ process.env.N2_DEPLOY_SCRIPTS_DIR = origEnv;
33
+ vi.unstubAllEnvs();
34
+ });
35
+ it("returns N2_DEPLOY_SCRIPTS_DIR when set and deploy.sh exists", () => {
36
+ const tmpDir = path.join(os.tmpdir(), `n2-scripts-${Date.now()}`);
37
+ fs.mkdirSync(tmpDir, { recursive: true });
38
+ fs.writeFileSync(path.join(tmpDir, "deploy.sh"), "#!/bin/bash", "utf8");
39
+ vi.stubEnv("N2_DEPLOY_SCRIPTS_DIR", tmpDir);
40
+ const dir = getScriptsDir();
41
+ expect(dir).toBe(tmpDir);
42
+ fs.rmSync(tmpDir, { recursive: true, force: true });
43
+ });
44
+ it("falls back to package assets when N2_DEPLOY_SCRIPTS_DIR has no deploy.sh", () => {
45
+ vi.stubEnv("N2_DEPLOY_SCRIPTS_DIR", "/nonexistent");
46
+ const dir = getScriptsDir();
47
+ // Package has assets/deploy, so fallback is used (not null)
48
+ expect(dir === null || dir.endsWith("assets/deploy")).toBe(true);
49
+ });
50
+ });
51
+ });
52
+ //# sourceMappingURL=paths.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.test.js","sourceRoot":"","sources":["../../src/lib/paths.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;IACrB,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;QACpC,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;YAC7C,MAAM,CAAC,GAAG,oBAAoB,EAAE,CAAC;YACjC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;QACjC,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;YAC9B,MAAM,CAAC,GAAG,iBAAiB,CAAC,uBAAuB,CAAC,CAAC;YACrD,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC5C,MAAM,GAAG,GAAG,UAAU,CAAC;YACvB,MAAM,CAAC,GAAG,iBAAiB,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;YAChD,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;YAC/B,MAAM,GAAG,GAAG,yBAAyB,CAAC;YACtC,MAAM,CAAC,GAAG,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YAC3C,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;QAElD,SAAS,CAAC,GAAG,EAAE;YACb,OAAO,CAAC,GAAG,CAAC,qBAAqB,GAAG,OAAO,CAAC;YAC5C,EAAE,CAAC,aAAa,EAAE,CAAC;QACrB,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;YACrE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,cAAc,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAClE,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1C,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;YACxE,EAAE,CAAC,OAAO,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;YAC5C,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;YAC5B,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACzB,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;YAClF,EAAE,CAAC,OAAO,CAAC,uBAAuB,EAAE,cAAc,CAAC,CAAC;YACpD,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;YAC5B,4DAA4D;YAC5D,MAAM,CAAC,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@aaricchen1991/n2-cli",
3
+ "version": "1.0.0",
4
+ "description": "CLI for server init, nginx config, and SSL certificate management",
5
+ "type": "module",
6
+ "bin": {
7
+ "n2": "./dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "assets",
12
+ "README.md"
13
+ ],
14
+ "dependencies": {
15
+ "commander": "^12.1.0",
16
+ "consola": "^3.4.2"
17
+ },
18
+ "devDependencies": {
19
+ "@types/node": "^25.0.3",
20
+ "typescript": "~5.9.3",
21
+ "vitest": "^4.0.18"
22
+ },
23
+ "engines": {
24
+ "node": ">=20.0.0"
25
+ },
26
+ "scripts": {
27
+ "build": "tsc",
28
+ "cli": "node dist/cli.js",
29
+ "dev": "tsc && node dist/cli.js",
30
+ "test": "vitest run",
31
+ "test:watch": "vitest",
32
+ "release": "node scripts/release.js"
33
+ }
34
+ }