@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,111 @@
1
+ /**
2
+ * Config file CRUD: load, get, set, delete, save for ~/.deploy/config.yaml.
3
+ * Keys: aliyun_access_key_id, aliyun_access_key_secret, api.backend_port,
4
+ * api.domains, admin.domains, tenant.domains, client.domains
5
+ */
6
+ import fs from "node:fs";
7
+ import path from "node:path";
8
+ import { parseDeployConfig, serializeDeployConfig } from "./domains.js";
9
+ const SECTION_KEYS = [
10
+ "api.backend_port",
11
+ "api.domains",
12
+ "admin.domains",
13
+ "tenant.domains",
14
+ "client.domains",
15
+ ];
16
+ export function isValidConfigKey(key) {
17
+ return (key === "aliyun_access_key_id" ||
18
+ key === "aliyun_access_key_secret" ||
19
+ SECTION_KEYS.includes(key));
20
+ }
21
+ export function loadConfig(configPath) {
22
+ const content = fs.readFileSync(configPath, "utf8");
23
+ return parseDeployConfig(content);
24
+ }
25
+ export function saveConfig(configPath, data) {
26
+ const dir = path.dirname(configPath);
27
+ if (dir && dir !== ".") {
28
+ fs.mkdirSync(dir, { recursive: true });
29
+ }
30
+ fs.writeFileSync(configPath, serializeDeployConfig(data), "utf8");
31
+ }
32
+ export function getConfigValue(data, key) {
33
+ switch (key) {
34
+ case "aliyun_access_key_id":
35
+ return data.aliyunAccessKeyId;
36
+ case "aliyun_access_key_secret":
37
+ return data.aliyunAccessKeySecret;
38
+ case "api.backend_port":
39
+ return data.domainsConfig.api.backend_port;
40
+ case "api.domains":
41
+ return data.domainsConfig.api.domains;
42
+ case "admin.domains":
43
+ return data.domainsConfig.admin.domains;
44
+ case "tenant.domains":
45
+ return data.domainsConfig.tenant.domains;
46
+ case "client.domains":
47
+ return data.domainsConfig.client.domains;
48
+ default:
49
+ return undefined;
50
+ }
51
+ }
52
+ export function setConfigValue(data, key, value) {
53
+ switch (key) {
54
+ case "aliyun_access_key_id":
55
+ data.aliyunAccessKeyId = String(value);
56
+ break;
57
+ case "aliyun_access_key_secret":
58
+ data.aliyunAccessKeySecret = String(value);
59
+ break;
60
+ case "api.backend_port":
61
+ data.domainsConfig.api.backend_port =
62
+ typeof value === "number" ? value : parseInt(String(value), 10);
63
+ break;
64
+ case "api.domains":
65
+ data.domainsConfig.api.domains = Array.isArray(value)
66
+ ? value
67
+ : [String(value)];
68
+ break;
69
+ case "admin.domains":
70
+ data.domainsConfig.admin.domains = Array.isArray(value)
71
+ ? value
72
+ : [String(value)];
73
+ break;
74
+ case "tenant.domains":
75
+ data.domainsConfig.tenant.domains = Array.isArray(value)
76
+ ? value
77
+ : [String(value)];
78
+ break;
79
+ case "client.domains":
80
+ data.domainsConfig.client.domains = Array.isArray(value)
81
+ ? value
82
+ : [String(value)];
83
+ break;
84
+ }
85
+ }
86
+ export function deleteConfigValue(data, key) {
87
+ switch (key) {
88
+ case "aliyun_access_key_id":
89
+ data.aliyunAccessKeyId = undefined;
90
+ break;
91
+ case "aliyun_access_key_secret":
92
+ data.aliyunAccessKeySecret = undefined;
93
+ break;
94
+ case "api.backend_port":
95
+ data.domainsConfig.api.backend_port = 3000;
96
+ break;
97
+ case "api.domains":
98
+ data.domainsConfig.api.domains = [];
99
+ break;
100
+ case "admin.domains":
101
+ data.domainsConfig.admin.domains = [];
102
+ break;
103
+ case "tenant.domains":
104
+ data.domainsConfig.tenant.domains = [];
105
+ break;
106
+ case "client.domains":
107
+ data.domainsConfig.client.domains = [];
108
+ break;
109
+ }
110
+ }
111
+ //# sourceMappingURL=config-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-store.js","sourceRoot":"","sources":["../../src/lib/config-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=config-store.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-store.test.d.ts","sourceRoot":"","sources":["../../src/lib/config-store.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,173 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import os from "node:os";
5
+ import { isValidConfigKey, loadConfig, saveConfig, getConfigValue, setConfigValue, deleteConfigValue, } from "./config-store.js";
6
+ describe("config-store", () => {
7
+ let tmpDir;
8
+ beforeEach(() => {
9
+ tmpDir = path.join(os.tmpdir(), `n2-config-store-${Date.now()}`);
10
+ fs.mkdirSync(tmpDir, { recursive: true });
11
+ });
12
+ afterEach(() => {
13
+ fs.rmSync(tmpDir, { recursive: true, force: true });
14
+ });
15
+ describe("isValidConfigKey", () => {
16
+ it("returns true for valid keys", () => {
17
+ expect(isValidConfigKey("aliyun_access_key_id")).toBe(true);
18
+ expect(isValidConfigKey("aliyun_access_key_secret")).toBe(true);
19
+ expect(isValidConfigKey("api.backend_port")).toBe(true);
20
+ expect(isValidConfigKey("api.domains")).toBe(true);
21
+ expect(isValidConfigKey("admin.domains")).toBe(true);
22
+ expect(isValidConfigKey("tenant.domains")).toBe(true);
23
+ expect(isValidConfigKey("client.domains")).toBe(true);
24
+ });
25
+ it("returns false for invalid keys", () => {
26
+ expect(isValidConfigKey("foo")).toBe(false);
27
+ expect(isValidConfigKey("api.port")).toBe(false);
28
+ expect(isValidConfigKey("")).toBe(false);
29
+ });
30
+ });
31
+ describe("loadConfig / saveConfig", () => {
32
+ it("loads and parses deploy config", () => {
33
+ const configPath = path.join(tmpDir, "config.yaml");
34
+ const yaml = `aliyun_access_key_id: "my-key"
35
+ aliyun_access_key_secret: "my-secret"
36
+
37
+ api:
38
+ backend_port: 4000
39
+ domains:
40
+ - api.example.com
41
+
42
+ admin:
43
+ domains:
44
+ - admin.example.com
45
+
46
+ tenant:
47
+ domains: []
48
+
49
+ client:
50
+ domains: []
51
+ `;
52
+ fs.writeFileSync(configPath, yaml, "utf8");
53
+ const data = loadConfig(configPath);
54
+ expect(data.aliyunAccessKeyId).toBe("my-key");
55
+ expect(data.aliyunAccessKeySecret).toBe("my-secret");
56
+ expect(data.domainsConfig.api.backend_port).toBe(4000);
57
+ expect(data.domainsConfig.api.domains).toEqual(["api.example.com"]);
58
+ expect(data.domainsConfig.admin.domains).toEqual(["admin.example.com"]);
59
+ expect(data.domainsConfig.tenant.domains).toEqual([]);
60
+ expect(data.domainsConfig.client.domains).toEqual([]);
61
+ });
62
+ it("saveConfig writes back and loadConfig round-trips", () => {
63
+ const configPath = path.join(tmpDir, "config.yaml");
64
+ const yaml = `api:
65
+ backend_port: 3000
66
+ domains: []
67
+
68
+ admin:
69
+ domains: []
70
+
71
+ tenant:
72
+ domains: []
73
+
74
+ client:
75
+ domains: []
76
+ `;
77
+ fs.writeFileSync(configPath, yaml, "utf8");
78
+ const data = loadConfig(configPath);
79
+ data.aliyunAccessKeyId = "new-key";
80
+ data.domainsConfig.admin.domains = ["a.com", "b.com"];
81
+ saveConfig(configPath, data);
82
+ const loaded = loadConfig(configPath);
83
+ expect(loaded.aliyunAccessKeyId).toBe("new-key");
84
+ expect(loaded.domainsConfig.admin.domains).toEqual(["a.com", "b.com"]);
85
+ });
86
+ });
87
+ describe("getConfigValue", () => {
88
+ it("returns value for each key", () => {
89
+ const configPath = path.join(tmpDir, "config.yaml");
90
+ const yaml = `aliyun_access_key_id: "key"
91
+ aliyun_access_key_secret: "secret"
92
+
93
+ api:
94
+ backend_port: 5000
95
+ domains:
96
+ - x.com
97
+
98
+ admin:
99
+ domains: []
100
+
101
+ tenant:
102
+ domains: []
103
+
104
+ client:
105
+ domains: []
106
+ `;
107
+ fs.writeFileSync(configPath, yaml, "utf8");
108
+ const data = loadConfig(configPath);
109
+ expect(getConfigValue(data, "aliyun_access_key_id")).toBe("key");
110
+ expect(getConfigValue(data, "aliyun_access_key_secret")).toBe("secret");
111
+ expect(getConfigValue(data, "api.backend_port")).toBe(5000);
112
+ expect(getConfigValue(data, "api.domains")).toEqual(["x.com"]);
113
+ expect(getConfigValue(data, "admin.domains")).toEqual([]);
114
+ });
115
+ });
116
+ describe("setConfigValue", () => {
117
+ it("sets string keys", () => {
118
+ const configPath = path.join(tmpDir, "config.yaml");
119
+ fs.writeFileSync(configPath, "api:\n backend_port: 3000\n domains: []\n\nadmin:\n domains: []\n\ntenant:\n domains: []\n\nclient:\n domains: []\n", "utf8");
120
+ const data = loadConfig(configPath);
121
+ setConfigValue(data, "aliyun_access_key_id", "id");
122
+ setConfigValue(data, "aliyun_access_key_secret", "sec");
123
+ expect(data.aliyunAccessKeyId).toBe("id");
124
+ expect(data.aliyunAccessKeySecret).toBe("sec");
125
+ });
126
+ it("sets api.backend_port as number", () => {
127
+ const configPath = path.join(tmpDir, "config.yaml");
128
+ fs.writeFileSync(configPath, "api:\n backend_port: 3000\n domains: []\n\nadmin:\n domains: []\n\ntenant:\n domains: []\n\nclient:\n domains: []\n", "utf8");
129
+ const data = loadConfig(configPath);
130
+ setConfigValue(data, "api.backend_port", 8080);
131
+ expect(data.domainsConfig.api.backend_port).toBe(8080);
132
+ });
133
+ it("sets domains arrays", () => {
134
+ const configPath = path.join(tmpDir, "config.yaml");
135
+ fs.writeFileSync(configPath, "api:\n backend_port: 3000\n domains: []\n\nadmin:\n domains: []\n\ntenant:\n domains: []\n\nclient:\n domains: []\n", "utf8");
136
+ const data = loadConfig(configPath);
137
+ setConfigValue(data, "admin.domains", ["a.com", "b.com"]);
138
+ expect(data.domainsConfig.admin.domains).toEqual(["a.com", "b.com"]);
139
+ });
140
+ });
141
+ describe("deleteConfigValue", () => {
142
+ it("clears aliyun keys and resets domains/port", () => {
143
+ const configPath = path.join(tmpDir, "config.yaml");
144
+ const yaml = `aliyun_access_key_id: "k"
145
+ aliyun_access_key_secret: "s"
146
+
147
+ api:
148
+ backend_port: 9999
149
+ domains:
150
+ - x.com
151
+
152
+ admin:
153
+ domains:
154
+ - a.com
155
+
156
+ tenant:
157
+ domains: []
158
+
159
+ client:
160
+ domains: []
161
+ `;
162
+ fs.writeFileSync(configPath, yaml, "utf8");
163
+ const data = loadConfig(configPath);
164
+ deleteConfigValue(data, "aliyun_access_key_id");
165
+ expect(data.aliyunAccessKeyId).toBeUndefined();
166
+ deleteConfigValue(data, "api.backend_port");
167
+ expect(data.domainsConfig.api.backend_port).toBe(3000);
168
+ deleteConfigValue(data, "admin.domains");
169
+ expect(data.domainsConfig.admin.domains).toEqual([]);
170
+ });
171
+ });
172
+ });
173
+ //# sourceMappingURL=config-store.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-store.test.js","sourceRoot":"","sources":["../../src/lib/config-store.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Parse domains.yaml / deploy config format.
3
+ * Deploy config (~/.deploy/config.yaml) may include ALIYUN_ACCESS_KEY_ID, ALIYUN_ACCESS_KEY_SECRET and domain sections.
4
+ */
5
+ export interface DomainsConfig {
6
+ api: {
7
+ backend_port: number;
8
+ domains: string[];
9
+ };
10
+ admin: {
11
+ domains: string[];
12
+ };
13
+ tenant: {
14
+ domains: string[];
15
+ };
16
+ client: {
17
+ domains: string[];
18
+ };
19
+ }
20
+ /** Result of parsing a deploy config file (aliyun credentials + domains). */
21
+ export interface DeployConfigResult {
22
+ domainsConfig: DomainsConfig;
23
+ aliyunAccessKeyId?: string;
24
+ aliyunAccessKeySecret?: string;
25
+ }
26
+ /**
27
+ * Parse deploy config YAML: optional aliyun_access_key_id / aliyun_access_key_secret at top level,
28
+ * plus api/admin/tenant/client sections (same as domains.yaml).
29
+ */
30
+ export declare function parseDeployConfig(content: string): DeployConfigResult;
31
+ /** Parse domains-only YAML (backward compatible). */
32
+ export declare function parseDomainsYaml(content: string): DomainsConfig;
33
+ /** Return all domains that need SSL (admin + tenant + client). API 与前端同域,不再单独申请证书。 */
34
+ export declare function getAllDomains(config: DomainsConfig): string[];
35
+ /** Serialize DeployConfigResult to YAML-like deploy config string. */
36
+ export declare function serializeDeployConfig(data: DeployConfigResult): string;
37
+ //# sourceMappingURL=domains.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domains.d.ts","sourceRoot":"","sources":["../../src/lib/domains.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACjD,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC7B,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC9B,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAC/B;AAED,6EAA6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,aAAa,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAoBD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAiErE;AAED,qDAAqD;AACrD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAE/D;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,EAAE,CAM7D;AAED,sEAAsE;AACtE,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CA2BtE"}
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Parse domains.yaml / deploy config format.
3
+ * Deploy config (~/.deploy/config.yaml) may include ALIYUN_ACCESS_KEY_ID, ALIYUN_ACCESS_KEY_SECRET and domain sections.
4
+ */
5
+ const DEFAULT_CONFIG = {
6
+ api: { backend_port: 3000, domains: [] },
7
+ admin: { domains: [] },
8
+ tenant: { domains: [] },
9
+ client: { domains: [] },
10
+ };
11
+ function unquote(s) {
12
+ const t = s.trim();
13
+ if ((t.startsWith('"') && t.endsWith('"')) ||
14
+ (t.startsWith("'") && t.endsWith("'"))) {
15
+ return t.slice(1, -1);
16
+ }
17
+ return t;
18
+ }
19
+ /**
20
+ * Parse deploy config YAML: optional aliyun_access_key_id / aliyun_access_key_secret at top level,
21
+ * plus api/admin/tenant/client sections (same as domains.yaml).
22
+ */
23
+ export function parseDeployConfig(content) {
24
+ const result = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
25
+ let aliyunAccessKeyId;
26
+ let aliyunAccessKeySecret;
27
+ const lines = content.split(/\r?\n/);
28
+ let current = null;
29
+ let inDomains = false;
30
+ for (const line of lines) {
31
+ const trimmed = line.trim();
32
+ if (trimmed.startsWith("#") || !trimmed)
33
+ continue;
34
+ const aliIdMatch = trimmed.match(/^aliyun_access_key_id:\s*(.*)$/i);
35
+ if (aliIdMatch) {
36
+ aliyunAccessKeyId = unquote(aliIdMatch[1]).trim() || undefined;
37
+ continue;
38
+ }
39
+ const aliSecretMatch = trimmed.match(/^aliyun_access_key_secret:\s*(.*)$/i);
40
+ if (aliSecretMatch) {
41
+ aliyunAccessKeySecret = unquote(aliSecretMatch[1]).trim() || undefined;
42
+ continue;
43
+ }
44
+ if (/^api:\s*$/.test(trimmed)) {
45
+ current = "api";
46
+ inDomains = false;
47
+ continue;
48
+ }
49
+ if (/^admin:\s*$/.test(trimmed)) {
50
+ current = "admin";
51
+ inDomains = false;
52
+ continue;
53
+ }
54
+ if (/^tenant:\s*$/.test(trimmed)) {
55
+ current = "tenant";
56
+ inDomains = false;
57
+ continue;
58
+ }
59
+ if (/^client:\s*$/.test(trimmed)) {
60
+ current = "client";
61
+ inDomains = false;
62
+ continue;
63
+ }
64
+ const backendPortMatch = trimmed.match(/^backend_port:\s*(\d+)\s*$/);
65
+ if (current === "api" && backendPortMatch) {
66
+ result.api.backend_port = parseInt(backendPortMatch[1], 10);
67
+ continue;
68
+ }
69
+ if (current && /^domains:\s*$/.test(trimmed)) {
70
+ inDomains = true;
71
+ continue;
72
+ }
73
+ const domainMatch = trimmed.match(/^-\s*(.+)$/);
74
+ if (current && inDomains && domainMatch) {
75
+ const domain = domainMatch[1].trim();
76
+ if (domain)
77
+ result[current].domains.push(domain);
78
+ }
79
+ }
80
+ return {
81
+ domainsConfig: result,
82
+ aliyunAccessKeyId,
83
+ aliyunAccessKeySecret,
84
+ };
85
+ }
86
+ /** Parse domains-only YAML (backward compatible). */
87
+ export function parseDomainsYaml(content) {
88
+ return parseDeployConfig(content).domainsConfig;
89
+ }
90
+ /** Return all domains that need SSL (admin + tenant + client). API 与前端同域,不再单独申请证书。 */
91
+ export function getAllDomains(config) {
92
+ const list = [];
93
+ for (const d of config.admin.domains)
94
+ list.push(d);
95
+ for (const d of config.tenant.domains)
96
+ list.push(d);
97
+ for (const d of config.client.domains)
98
+ list.push(d);
99
+ return list;
100
+ }
101
+ /** Serialize DeployConfigResult to YAML-like deploy config string. */
102
+ export function serializeDeployConfig(data) {
103
+ const lines = [];
104
+ if (data.aliyunAccessKeyId != null && data.aliyunAccessKeyId !== "") {
105
+ lines.push(`aliyun_access_key_id: "${data.aliyunAccessKeyId}"`);
106
+ }
107
+ if (data.aliyunAccessKeySecret != null && data.aliyunAccessKeySecret !== "") {
108
+ lines.push(`aliyun_access_key_secret: "${data.aliyunAccessKeySecret}"`);
109
+ }
110
+ const c = data.domainsConfig;
111
+ lines.push("");
112
+ lines.push("api:");
113
+ lines.push(` backend_port: ${c.api.backend_port}`);
114
+ lines.push(" domains:");
115
+ for (const d of c.api.domains)
116
+ lines.push(` - ${d}`);
117
+ lines.push("");
118
+ lines.push("admin:");
119
+ lines.push(" domains:");
120
+ for (const d of c.admin.domains)
121
+ lines.push(` - ${d}`);
122
+ lines.push("");
123
+ lines.push("tenant:");
124
+ lines.push(" domains:");
125
+ for (const d of c.tenant.domains)
126
+ lines.push(` - ${d}`);
127
+ lines.push("");
128
+ lines.push("client:");
129
+ lines.push(" domains:");
130
+ for (const d of c.client.domains)
131
+ lines.push(` - ${d}`);
132
+ return lines.join("\n") + "\n";
133
+ }
134
+ //# sourceMappingURL=domains.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domains.js","sourceRoot":"","sources":["../../src/lib/domains.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=domains.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domains.test.d.ts","sourceRoot":"","sources":["../../src/lib/domains.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,141 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { parseDeployConfig, parseDomainsYaml, getAllDomains, serializeDeployConfig, } from "./domains.js";
3
+ describe("domains", () => {
4
+ describe("parseDeployConfig", () => {
5
+ it("parses minimal config with defaults", () => {
6
+ const yaml = `
7
+ api:
8
+ backend_port: 3000
9
+ domains: []
10
+
11
+ admin:
12
+ domains: []
13
+
14
+ tenant:
15
+ domains: []
16
+
17
+ client:
18
+ domains: []
19
+ `;
20
+ const result = parseDeployConfig(yaml);
21
+ expect(result.domainsConfig.api.backend_port).toBe(3000);
22
+ expect(result.domainsConfig.api.domains).toEqual([]);
23
+ expect(result.aliyunAccessKeyId).toBeUndefined();
24
+ expect(result.aliyunAccessKeySecret).toBeUndefined();
25
+ });
26
+ it("parses aliyun keys and domains", () => {
27
+ const yaml = `aliyun_access_key_id: "my-key"
28
+ aliyun_access_key_secret: "my-secret"
29
+
30
+ api:
31
+ backend_port: 4000
32
+ domains:
33
+ - api.example.com
34
+
35
+ admin:
36
+ domains:
37
+ - admin.example.com
38
+
39
+ tenant:
40
+ domains:
41
+ - t1.com
42
+ - t2.com
43
+
44
+ client:
45
+ domains: []
46
+ `;
47
+ const result = parseDeployConfig(yaml);
48
+ expect(result.aliyunAccessKeyId).toBe("my-key");
49
+ expect(result.aliyunAccessKeySecret).toBe("my-secret");
50
+ expect(result.domainsConfig.api.backend_port).toBe(4000);
51
+ expect(result.domainsConfig.api.domains).toEqual(["api.example.com"]);
52
+ expect(result.domainsConfig.admin.domains).toEqual(["admin.example.com"]);
53
+ expect(result.domainsConfig.tenant.domains).toEqual(["t1.com", "t2.com"]);
54
+ });
55
+ it("ignores comments and empty lines", () => {
56
+ const yaml = `# comment
57
+ api:
58
+ backend_port: 3000
59
+ domains: []
60
+ # another
61
+ admin:
62
+ domains: []
63
+
64
+ tenant:
65
+ domains: []
66
+
67
+ client:
68
+ domains: []
69
+ `;
70
+ const result = parseDeployConfig(yaml);
71
+ expect(result.domainsConfig.api.backend_port).toBe(3000);
72
+ });
73
+ });
74
+ describe("parseDomainsYaml", () => {
75
+ it("returns domainsConfig only", () => {
76
+ const yaml = `api:
77
+ backend_port: 3000
78
+ domains: []
79
+
80
+ admin:
81
+ domains: []
82
+
83
+ tenant:
84
+ domains: []
85
+
86
+ client:
87
+ domains: []
88
+ `;
89
+ const config = parseDomainsYaml(yaml);
90
+ expect(config.api.backend_port).toBe(3000);
91
+ expect(config.admin.domains).toEqual([]);
92
+ });
93
+ });
94
+ describe("getAllDomains", () => {
95
+ it("returns admin + tenant + client domains (no api)", () => {
96
+ const config = {
97
+ api: { backend_port: 3000, domains: ["api.com"] },
98
+ admin: { domains: ["admin.com"] },
99
+ tenant: { domains: ["t1.com", "t2.com"] },
100
+ client: { domains: ["c.com"] },
101
+ };
102
+ expect(getAllDomains(config)).toEqual([
103
+ "admin.com",
104
+ "t1.com",
105
+ "t2.com",
106
+ "c.com",
107
+ ]);
108
+ });
109
+ it("returns empty when no domains", () => {
110
+ const config = {
111
+ api: { backend_port: 3000, domains: [] },
112
+ admin: { domains: [] },
113
+ tenant: { domains: [] },
114
+ client: { domains: [] },
115
+ };
116
+ expect(getAllDomains(config)).toEqual([]);
117
+ });
118
+ });
119
+ describe("serializeDeployConfig", () => {
120
+ it("round-trips with parseDeployConfig", () => {
121
+ const data = {
122
+ domainsConfig: {
123
+ api: { backend_port: 3000, domains: ["api.example.com"] },
124
+ admin: { domains: ["admin.example.com"] },
125
+ tenant: { domains: [] },
126
+ client: { domains: ["c.com"] },
127
+ },
128
+ aliyunAccessKeyId: "key",
129
+ aliyunAccessKeySecret: "secret",
130
+ };
131
+ const serialized = serializeDeployConfig(data);
132
+ const parsed = parseDeployConfig(serialized);
133
+ expect(parsed.aliyunAccessKeyId).toBe("key");
134
+ expect(parsed.aliyunAccessKeySecret).toBe("secret");
135
+ expect(parsed.domainsConfig.api.backend_port).toBe(3000);
136
+ expect(parsed.domainsConfig.api.domains).toEqual(["api.example.com"]);
137
+ expect(parsed.domainsConfig.client.domains).toEqual(["c.com"]);
138
+ });
139
+ });
140
+ });
141
+ //# sourceMappingURL=domains.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domains.test.js","sourceRoot":"","sources":["../../src/lib/domains.test.ts"],"names":[],"mappings":"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"}