nginx-proxy-manager-cli 0.2.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/AGENTS.md ADDED
@@ -0,0 +1,34 @@
1
+ # AGENTS.md
2
+
3
+ ## 开发约定
4
+
5
+ - 遵守KISS。
6
+ - 写操作必须默认确认,自动化场景使用 `--yes`。
7
+ - 所有可运行命令都要有 `--json` 参数保证机器可读。
8
+
9
+ ## 开发流程
10
+
11
+ 使用TDD开发,开发完创建至少一个 Subagent 做 Review ,Review 要做压力测试和对抗测试。
12
+
13
+ 流程:
14
+ 1. 写失败测试。
15
+ 2. 写最小模型。
16
+ 3. Subagent Review and fix loop。
17
+ 4. 完成需求功能。
18
+ 5. Subagent Review and fix loop。
19
+
20
+ ## 操作边界
21
+
22
+ - 不要执行真实环境写操作。
23
+ - 连接真实环境时优先运行只读命令。
24
+ - 不要读取或修改用户真实 HOME 配置,除非用户明确要求。
25
+
26
+ ## 安全
27
+
28
+ - 不打印 JWT、密码或 token。
29
+ - 不把真实账号、密码或 token 写入仓库。
30
+ - 文档和测试只能使用占位值。
31
+
32
+ ## 发布边界
33
+
34
+ - 不要执行真实 commit、tag、push 或 publish,除非用户明确要求。
package/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0 (2026-07-02)
4
+
5
+ ### Features
6
+
7
+ - **cli:** 搭建管理命令
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aturan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,134 @@
1
+ # nginx-proxy-manager-cli
2
+
3
+ 用于在命令行管理 Nginx Proxy Manager。
4
+
5
+ 命令入口:
6
+
7
+ ```bash
8
+ nginx-proxy-manager
9
+ ```
10
+
11
+ ## 安装
12
+
13
+ ```bash
14
+ pnpm add -g nginx-proxy-manager-cli
15
+ ```
16
+
17
+ ## 快速开始
18
+
19
+ ```bash
20
+ nginx-proxy-manager profile add prod \
21
+ --base_url https://proxy.example.com \
22
+ --username admin@example.com \
23
+ --password "$PASSWORD" \
24
+ --default
25
+
26
+ nginx-proxy-manager health
27
+ nginx-proxy-manager proxy-hosts list
28
+ ```
29
+
30
+ 写操作默认需要确认。脚本或自动化环境中使用 `--yes`:
31
+
32
+ ```bash
33
+ nginx-proxy-manager --yes proxy-hosts create \
34
+ --domain_names app.example.com \
35
+ --forward_scheme http \
36
+ --forward_host app \
37
+ --forward_port 8080
38
+ ```
39
+
40
+ 复杂请求体使用 `--body-json` 或 `--from-file`:
41
+
42
+ ```bash
43
+ nginx-proxy-manager --yes proxy-hosts update 1 \
44
+ --body-json '{"enabled":true}'
45
+ ```
46
+
47
+ ## 配置
48
+
49
+ 默认配置路径:
50
+
51
+ ```text
52
+ $HOME/.nginx-xproxy-manager.json
53
+ ```
54
+
55
+ 配置路径优先级:
56
+
57
+ 1. `--config PATH`
58
+ 2. `NGINX_PROXY_MANAGER_CONFIG`
59
+ 3. 默认配置路径
60
+
61
+ 示例:
62
+
63
+ ```bash
64
+ nginx-proxy-manager --config ./npm-config.json proxy-hosts list
65
+ ```
66
+
67
+ ## Profile
68
+
69
+ ```bash
70
+ nginx-proxy-manager profile add
71
+ nginx-proxy-manager profile add prod --base_url https://proxy.example.com --username admin --password "$PASSWORD" --default
72
+ nginx-proxy-manager profile list
73
+ nginx-proxy-manager profile show prod
74
+ nginx-proxy-manager profile use prod
75
+ nginx-proxy-manager --yes profile remove prod
76
+ ```
77
+
78
+ ## 命令
79
+
80
+ 认证与健康检查:
81
+
82
+ ```bash
83
+ nginx-proxy-manager auth token
84
+ nginx-proxy-manager health
85
+ nginx-proxy-manager schema
86
+ ```
87
+
88
+ Certificates:
89
+
90
+ ```bash
91
+ nginx-proxy-manager certificates list
92
+ nginx-proxy-manager certificates get 1
93
+ nginx-proxy-manager --yes certificates create --body-json '{"provider":"letsencrypt","domain_names":["example.com"]}'
94
+ nginx-proxy-manager --yes certificates delete 1
95
+ nginx-proxy-manager --yes certificates renew 1
96
+ nginx-proxy-manager certificates download 1 --output cert.zip
97
+ nginx-proxy-manager --yes certificates upload --from-file certificate-upload.json
98
+ ```
99
+
100
+ Proxy hosts:
101
+
102
+ ```bash
103
+ nginx-proxy-manager proxy-hosts list
104
+ nginx-proxy-manager proxy-hosts get 1
105
+ nginx-proxy-manager --yes proxy-hosts create --domain_names app.example.com --forward_scheme http --forward_host app --forward_port 8080
106
+ nginx-proxy-manager --yes proxy-hosts update 1 --body-json '{"enabled":true}'
107
+ nginx-proxy-manager --yes proxy-hosts delete 1
108
+ nginx-proxy-manager --yes proxy-hosts enable 1
109
+ nginx-proxy-manager --yes proxy-hosts disable 1
110
+ ```
111
+
112
+ Redirection hosts:
113
+
114
+ ```bash
115
+ nginx-proxy-manager redirection-hosts list
116
+ nginx-proxy-manager redirection-hosts get 1
117
+ nginx-proxy-manager --yes redirection-hosts create --body-json '{"domain_names":["old.example.com"],"forward_domain_name":"new.example.com"}'
118
+ nginx-proxy-manager --yes redirection-hosts update 1 --from-file redirection-host.json
119
+ nginx-proxy-manager --yes redirection-hosts delete 1
120
+ nginx-proxy-manager --yes redirection-hosts enable 1
121
+ nginx-proxy-manager --yes redirection-hosts disable 1
122
+ ```
123
+
124
+ Streams:
125
+
126
+ ```bash
127
+ nginx-proxy-manager streams list
128
+ nginx-proxy-manager streams get 1
129
+ nginx-proxy-manager --yes streams create --body-json '{"incoming_port":2222,"forwarding_host":"ssh","forwarding_port":22}'
130
+ nginx-proxy-manager --yes streams update 1 --from-file stream.json
131
+ nginx-proxy-manager --yes streams delete 1
132
+ nginx-proxy-manager --yes streams enable 1
133
+ nginx-proxy-manager --yes streams disable 1
134
+ ```
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function runCli(argv?: string[]): Promise<void>;
package/dist/cli.js ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import { createProgram } from "./commands.js";
3
+ export async function runCli(argv = process.argv) {
4
+ const program = createProgram({
5
+ env: process.env,
6
+ homeDir: process.env.HOME ?? "",
7
+ fetch,
8
+ stdin: process.stdin,
9
+ stdout: process.stdout,
10
+ stderr: process.stderr
11
+ });
12
+ await program.parseAsync(argv);
13
+ }
14
+ runCli().catch((error) => {
15
+ const message = error instanceof Error ? error.message : String(error);
16
+ process.stderr.write(`${message}\n`);
17
+ process.exitCode = 1;
18
+ });
19
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,OAAiB,OAAO,CAAC,IAAI;IACxD,MAAM,OAAO,GAAG,aAAa,CAAC;QAC5B,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE;QAC/B,KAAK;QACL,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;IACH,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAChC,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;IACrC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { JsonObject, JsonValue, Profile, RequestRecord } from "./types.js";
2
+ export type TokenCache = {
3
+ token: string;
4
+ expires: string;
5
+ };
6
+ export declare class ApiClient {
7
+ private readonly profile;
8
+ private readonly fetchImpl;
9
+ private readonly onToken?;
10
+ constructor(profile: Profile, fetchImpl?: typeof fetch, onToken?: ((token: TokenCache) => Promise<void> | void) | undefined);
11
+ token(): Promise<string>;
12
+ request(record: RequestRecord): Promise<JsonValue>;
13
+ private url;
14
+ }
15
+ export declare function isTokenFresh(token: string | undefined, expiresAt: string | undefined): token is string;
16
+ export declare function assertOk(response: Response): Promise<void>;
17
+ export declare function resourcePath(resource: string, id?: string | number, action?: string): string;
18
+ export declare function coerceId(id: string): number | string;
19
+ export declare function dataToJsonObject(value: JsonValue): JsonObject;
package/dist/client.js ADDED
@@ -0,0 +1,111 @@
1
+ import { URL } from "node:url";
2
+ import { z } from "zod";
3
+ const tokenResponseSchema = z.union([
4
+ z.object({
5
+ token: z.string().min(1),
6
+ expires: z.string().min(1)
7
+ }),
8
+ z.object({
9
+ requires_2fa: z.literal(true),
10
+ challenge_token: z.string().min(1)
11
+ })
12
+ ]);
13
+ export class ApiClient {
14
+ profile;
15
+ fetchImpl;
16
+ onToken;
17
+ constructor(profile, fetchImpl = fetch, onToken) {
18
+ this.profile = profile;
19
+ this.fetchImpl = fetchImpl;
20
+ this.onToken = onToken;
21
+ }
22
+ async token() {
23
+ if (isTokenFresh(this.profile.token, this.profile.token_expires_at)) {
24
+ return this.profile.token;
25
+ }
26
+ const response = await this.fetchImpl(this.url("/api/tokens"), {
27
+ method: "POST",
28
+ headers: { "content-type": "application/json" },
29
+ body: JSON.stringify({
30
+ identity: this.profile.username,
31
+ secret: this.profile.password
32
+ })
33
+ });
34
+ await assertOk(response);
35
+ const data = tokenResponseSchema.parse(await response.json());
36
+ if ("requires_2fa" in data) {
37
+ throw new Error("当前 CLI 不支持 2FA,请使用未启用 2FA 的账号或专用账号");
38
+ }
39
+ this.profile.token = data.token;
40
+ this.profile.token_expires_at = data.expires;
41
+ await this.onToken?.({ token: data.token, expires: data.expires });
42
+ return data.token;
43
+ }
44
+ async request(record) {
45
+ const token = await this.token();
46
+ const headers = {
47
+ authorization: `Bearer ${token}`
48
+ };
49
+ let body;
50
+ if (record.body instanceof FormData) {
51
+ body = record.body;
52
+ }
53
+ else if (record.body !== undefined) {
54
+ headers["content-type"] = "application/json";
55
+ body = JSON.stringify(record.body);
56
+ }
57
+ const init = {
58
+ method: record.method,
59
+ headers
60
+ };
61
+ if (body !== undefined) {
62
+ init.body = body;
63
+ }
64
+ const response = await this.fetchImpl(this.url(record.path, record.query), init);
65
+ await assertOk(response);
66
+ if (response.status === 204)
67
+ return { ok: true };
68
+ const contentType = response.headers.get("content-type") ?? "";
69
+ if (contentType.includes("application/octet-stream")) {
70
+ return { data: await response.arrayBuffer() };
71
+ }
72
+ return (await response.json());
73
+ }
74
+ url(path, query) {
75
+ const base = this.profile.base_url.replace(/\/+$/, "");
76
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
77
+ const url = new URL(`${base}${normalizedPath}`);
78
+ for (const [key, value] of Object.entries(query ?? {})) {
79
+ url.searchParams.set(key, String(value));
80
+ }
81
+ return url.toString();
82
+ }
83
+ }
84
+ export function isTokenFresh(token, expiresAt) {
85
+ if (!token || !expiresAt)
86
+ return false;
87
+ const expiresTime = Date.parse(expiresAt);
88
+ if (!Number.isFinite(expiresTime))
89
+ return false;
90
+ return expiresTime > Date.now() + 30_000;
91
+ }
92
+ export async function assertOk(response) {
93
+ if (response.ok)
94
+ return;
95
+ const body = await response.text().catch(() => "");
96
+ throw new Error(`API 请求失败:${response.status} ${response.statusText}${body ? ` ${body}` : ""}`);
97
+ }
98
+ export function resourcePath(resource, id, action) {
99
+ const base = resource === "certificates" ? "/api/nginx/certificates" : `/api/nginx/${resource}`;
100
+ return [base, id, action].filter((part) => part !== undefined && part !== "").join("/");
101
+ }
102
+ export function coerceId(id) {
103
+ const numeric = Number(id);
104
+ return Number.isSafeInteger(numeric) && String(numeric) === id ? numeric : id;
105
+ }
106
+ export function dataToJsonObject(value) {
107
+ if (value && typeof value === "object" && !Array.isArray(value))
108
+ return value;
109
+ throw new Error("API 返回值不是 JSON object");
110
+ }
111
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Command } from "commander";
2
+ import type { JsonObject, RuntimeContext } from "./types.js";
3
+ export declare function createProgram(context: RuntimeContext): Command;
4
+ export declare function parseUnknownFieldOptions(args: string[]): JsonObject;
@@ -0,0 +1,406 @@
1
+ import { createWriteStream } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { Command } from "commander";
4
+ import { confirm, input, password } from "@inquirer/prompts";
5
+ import { addProfile, getProfileName, readConfig, removeProfile, resolveConfigPath, selectProfile, useProfile, writeConfig } from "./config.js";
6
+ import { ApiClient, coerceId, dataToJsonObject, resourcePath } from "./client.js";
7
+ import { formatHuman, formatJson, formatProfileList } from "./output.js";
8
+ import { mergePayloads, parseJsonObject, readJsonObjectFile } from "./json.js";
9
+ const resourceNames = ["certificates", "proxy-hosts", "redirection-hosts", "streams"];
10
+ const writeActions = new Set(["create", "update", "delete", "enable", "disable", "renew", "upload"]);
11
+ const profileNamePattern = /^[A-Za-z0-9._-]+$/;
12
+ const requirePackage = createRequire(import.meta.url);
13
+ const packageInfo = requirePackage("../package.json");
14
+ export function createProgram(context) {
15
+ const program = new Command();
16
+ program
17
+ .name("nginx-proxy-manager")
18
+ .description("管理 Nginx Proxy Manager API 的命令行工具")
19
+ .version(packageInfo.version)
20
+ .option("--config <path>", "配置文件路径")
21
+ .option("--profile <name>", "要使用的 profile 名称")
22
+ .option("--json", "输出 JSON")
23
+ .option("--yes", "跳过写操作确认")
24
+ .configureOutput({
25
+ writeOut: (text) => write(context.stdout, text),
26
+ writeErr: (text) => write(context.stderr, text)
27
+ });
28
+ addProfileCommands(program, context);
29
+ addAuthCommands(program, context);
30
+ addHealthCommand(program, context);
31
+ for (const resource of resourceNames) {
32
+ addResourceCommands(program, context, resource);
33
+ }
34
+ return program;
35
+ }
36
+ function addProfileCommands(program, context) {
37
+ const profile = program.command("profile").description("管理本地 profile");
38
+ profile
39
+ .command("add [name]")
40
+ .description("新增或覆盖 profile")
41
+ .option("--base_url <url>", "base_url")
42
+ .option("--username <username>", "username")
43
+ .option("--password <password>", "password")
44
+ .option("--default", "设为默认 profile")
45
+ .action(async (name, options, command) => {
46
+ const global = getGlobal(command);
47
+ const path = getConfigPath(global, context);
48
+ const profileInput = await resolveProfileAddInput(name, options, context);
49
+ const newProfile = {
50
+ base_url: profileInput.base_url,
51
+ username: profileInput.username,
52
+ password: profileInput.password
53
+ };
54
+ await new ApiClient(newProfile, context.fetch).token();
55
+ const config = await addProfile(path, profileInput.name, newProfile, Boolean(options.default));
56
+ writeResult(context, global, {
57
+ ok: true,
58
+ config_path: path,
59
+ default_profile: config.default_profile ?? null,
60
+ profile: profileInput.name
61
+ });
62
+ });
63
+ profile
64
+ .command("list")
65
+ .description("列出 profile")
66
+ .action(async (_options, command) => {
67
+ const global = getGlobal(command);
68
+ const config = await readConfig(getConfigPath(global, context));
69
+ write(context.stdout, formatProfileList(config, Boolean(global.json)));
70
+ });
71
+ profile
72
+ .command("show [name]")
73
+ .description("显示 profile,password 会脱敏")
74
+ .action(async (name, _options, command) => {
75
+ const global = getGlobal(command);
76
+ const config = await readConfig(getConfigPath(global, context));
77
+ const profileName = getProfileName(config, name ?? stringOption(global.profile));
78
+ const profile = config.profiles[profileName];
79
+ writeResult(context, global, {
80
+ name: profileName,
81
+ default: config.default_profile === profileName,
82
+ base_url: profile?.base_url ?? "",
83
+ username: profile?.username ?? "",
84
+ credential: "[redacted]"
85
+ });
86
+ });
87
+ profile
88
+ .command("remove <name>")
89
+ .description("删除 profile")
90
+ .action(async (name, _options, command) => {
91
+ const global = getGlobal(command);
92
+ await confirmWrite(command, context);
93
+ const config = await removeProfile(getConfigPath(global, context), name);
94
+ writeResult(context, global, { ok: true, removed: name, default_profile: config.default_profile ?? null });
95
+ });
96
+ profile
97
+ .command("use <name>")
98
+ .description("切换默认 profile")
99
+ .action(async (name, _options, command) => {
100
+ const global = getGlobal(command);
101
+ const config = await useProfile(getConfigPath(global, context), name);
102
+ writeResult(context, global, { ok: true, default_profile: config.default_profile ?? null });
103
+ });
104
+ }
105
+ async function resolveProfileAddInput(name, options, context) {
106
+ const currentName = stringOption(name);
107
+ const currentBaseUrl = stringOption(options.base_url);
108
+ const currentUsername = stringOption(options.username);
109
+ const currentPassword = stringOption(options.password);
110
+ if (currentName && currentBaseUrl && currentUsername && currentPassword) {
111
+ return validateProfileAddInput({
112
+ name: currentName,
113
+ base_url: currentBaseUrl,
114
+ username: currentUsername,
115
+ password: currentPassword
116
+ });
117
+ }
118
+ const promptInput = context.stdin;
119
+ if (!promptInput?.isTTY) {
120
+ throw new Error("profile add requires name, --base_url, --username, and --password in non-interactive mode");
121
+ }
122
+ const promptContext = { input: promptInput, output: context.stderr };
123
+ const resolved = {
124
+ name: currentName ?? (await input({ message: "name", required: true, validate: validateProfileName }, promptContext)),
125
+ base_url: currentBaseUrl ??
126
+ (await input({ message: "base_url", required: true, validate: validateBaseUrl }, promptContext)),
127
+ username: currentUsername ?? (await input({ message: "username", required: true, validate: validateRequired("username") }, promptContext)),
128
+ password: currentPassword ??
129
+ (await password({
130
+ message: "password",
131
+ mask: "*",
132
+ validate: validateRequired("password")
133
+ }, promptContext))
134
+ };
135
+ return validateProfileAddInput(resolved);
136
+ }
137
+ function validateProfileAddInput(input) {
138
+ const checks = [
139
+ validateProfileName(input.name),
140
+ validateBaseUrl(input.base_url),
141
+ validateRequired("username")(input.username),
142
+ validateRequired("password")(input.password)
143
+ ];
144
+ for (const result of checks) {
145
+ if (result !== true) {
146
+ throw new Error(result);
147
+ }
148
+ }
149
+ return input;
150
+ }
151
+ function validateProfileName(value) {
152
+ if (!value.trim())
153
+ return "profile name is required";
154
+ if (!profileNamePattern.test(value))
155
+ return "profile name may only contain letters, numbers, dots, underscores, and dashes";
156
+ return true;
157
+ }
158
+ function validateBaseUrl(value) {
159
+ try {
160
+ const url = new URL(value);
161
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
162
+ return "base_url must use http or https";
163
+ }
164
+ return true;
165
+ }
166
+ catch {
167
+ return "base_url must be a valid URL";
168
+ }
169
+ }
170
+ function validateRequired(field) {
171
+ return (value) => (value.trim().length > 0 ? true : `${field} is required`);
172
+ }
173
+ function addAuthCommands(program, context) {
174
+ const auth = program.command("auth").description("认证相关命令");
175
+ auth
176
+ .command("token")
177
+ .description("验证凭据并获取 token,但不会打印 JWT")
178
+ .action(async (_options, command) => {
179
+ const global = getGlobal(command);
180
+ const client = await getClient(command, context);
181
+ await client.token();
182
+ writeResult(context, global, { ok: true, token: "[redacted]" });
183
+ });
184
+ }
185
+ function addHealthCommand(program, context) {
186
+ program
187
+ .command("health")
188
+ .description("读取 API schema 检查连通性")
189
+ .action(async (_options, command) => {
190
+ const global = getGlobal(command);
191
+ const client = await getClient(command, context);
192
+ const result = await client.request({ method: "GET", path: "/api/schema" });
193
+ writeResult(context, global, result);
194
+ });
195
+ program
196
+ .command("schema")
197
+ .description("读取 API schema")
198
+ .action(async (_options, command) => {
199
+ const global = getGlobal(command);
200
+ const client = await getClient(command, context);
201
+ const result = await client.request({ method: "GET", path: "/api/schema" });
202
+ writeResult(context, global, result);
203
+ });
204
+ }
205
+ function addResourceCommands(program, context, resource) {
206
+ const group = program.command(resource).description(`${resource} 资源命令`);
207
+ group
208
+ .command("list")
209
+ .description(`列出 ${resource}`)
210
+ .option("--query-json <json>", "查询参数 JSON object")
211
+ .action(async (options, command) => {
212
+ const global = getGlobal(command);
213
+ const client = await getClient(command, context);
214
+ const queryJson = stringOption(options.queryJson);
215
+ const query = queryJson ? parseJsonObject(queryJson, "--query-json") : undefined;
216
+ const result = await client.request({
217
+ method: "GET",
218
+ path: resourcePath(resource),
219
+ query: queryToRecord(query)
220
+ });
221
+ writeResult(context, global, result);
222
+ });
223
+ group
224
+ .command("get <id>")
225
+ .description(`读取 ${resource}`)
226
+ .action(async (id, _options, command) => {
227
+ const global = getGlobal(command);
228
+ const client = await getClient(command, context);
229
+ const result = await client.request({ method: "GET", path: resourcePath(resource, coerceId(id)) });
230
+ writeResult(context, global, result);
231
+ });
232
+ group
233
+ .command("create")
234
+ .description(`创建 ${resource}`)
235
+ .option("--body-json <json>", "请求体 JSON object")
236
+ .option("--from-file <path>", "从文件读取请求体 JSON object")
237
+ .allowUnknownOption(true)
238
+ .allowExcessArguments(true)
239
+ .action(async (options, command) => {
240
+ await writeResource(command, context, resource, "create", undefined, options, "POST");
241
+ });
242
+ group
243
+ .command("update <id>")
244
+ .description(`更新 ${resource}`)
245
+ .option("--body-json <json>", "请求体 JSON object")
246
+ .option("--from-file <path>", "从文件读取请求体 JSON object")
247
+ .allowUnknownOption(true)
248
+ .allowExcessArguments(true)
249
+ .action(async (id, options, command) => {
250
+ await writeResource(command, context, resource, "update", coerceId(id), options, "PUT");
251
+ });
252
+ group
253
+ .command("delete <id>")
254
+ .description(`删除 ${resource}`)
255
+ .action(async (id, _options, command) => {
256
+ await writeResource(command, context, resource, "delete", coerceId(id), undefined, "DELETE");
257
+ });
258
+ for (const action of ["enable", "disable"]) {
259
+ group
260
+ .command(`${action} <id>`)
261
+ .description(`${action} ${resource}`)
262
+ .action(async (id, _options, command) => {
263
+ await writeResource(command, context, resource, action, coerceId(id), undefined, "PUT", action);
264
+ });
265
+ }
266
+ if (resource === "certificates") {
267
+ group
268
+ .command("renew <id>")
269
+ .description("续期 certificate")
270
+ .action(async (id, _options, command) => {
271
+ await writeResource(command, context, resource, "renew", coerceId(id), undefined, "POST", "renew");
272
+ });
273
+ group
274
+ .command("download <id>")
275
+ .description("下载 certificate")
276
+ .requiredOption("--output <path>", "输出文件路径")
277
+ .action(async (id, options, command) => {
278
+ const global = getGlobal(command);
279
+ const client = await getClient(command, context);
280
+ const result = await client.request({ method: "GET", path: resourcePath(resource, coerceId(id), "download") });
281
+ const object = dataToJsonObject(result);
282
+ const data = object.data;
283
+ if (!(data instanceof ArrayBuffer)) {
284
+ throw new Error("certificate download 未返回二进制内容");
285
+ }
286
+ await writeBinary(String(options.output), data);
287
+ writeResult(context, global, { ok: true, output: String(options.output) });
288
+ });
289
+ group
290
+ .command("upload")
291
+ .description("上传 certificate 元数据")
292
+ .option("--body-json <json>", "请求体 JSON object")
293
+ .option("--from-file <path>", "从文件读取请求体 JSON object")
294
+ .allowUnknownOption(true)
295
+ .allowExcessArguments(true)
296
+ .action(async (options, command) => {
297
+ await writeResource(command, context, resource, "upload", undefined, options, "POST", "upload");
298
+ });
299
+ }
300
+ }
301
+ async function writeResource(command, context, resource, action, id, options, method, pathAction) {
302
+ if (writeActions.has(action))
303
+ await confirmWrite(command, context);
304
+ const global = getGlobal(command);
305
+ const client = await getClient(command, context);
306
+ const body = options ? await payloadFromOptions(options, command) : undefined;
307
+ const result = await client.request({
308
+ method,
309
+ path: resourcePath(resource, id, pathAction),
310
+ body
311
+ });
312
+ writeResult(context, global, result);
313
+ }
314
+ async function payloadFromOptions(options, command) {
315
+ const fromFilePath = stringOption(options.fromFile);
316
+ const bodyJsonRaw = stringOption(options.bodyJson);
317
+ const fromFile = fromFilePath ? await readJsonObjectFile(fromFilePath) : undefined;
318
+ const inlineJson = bodyJsonRaw ? parseJsonObject(bodyJsonRaw, "--body-json") : undefined;
319
+ const unknown = parseUnknownFieldOptions(command.args);
320
+ const payload = mergePayloads(fromFile, inlineJson, unknown);
321
+ if (Object.keys(payload).length === 0) {
322
+ throw new Error("写操作需要 --body-json、--from-file 或字段参数");
323
+ }
324
+ return payload;
325
+ }
326
+ export function parseUnknownFieldOptions(args) {
327
+ const payload = {};
328
+ for (let index = 0; index < args.length; index += 1) {
329
+ const arg = args[index];
330
+ if (!arg?.startsWith("--"))
331
+ continue;
332
+ const key = arg.slice(2);
333
+ const values = [];
334
+ while (args[index + 1] && !args[index + 1]?.startsWith("--")) {
335
+ values.push(String(args[index + 1]));
336
+ index += 1;
337
+ }
338
+ payload[key] = values.length > 1 ? values : values[0] ?? true;
339
+ }
340
+ return payload;
341
+ }
342
+ async function getClient(command, context) {
343
+ const global = getGlobal(command);
344
+ const configPath = getConfigPath(global, context);
345
+ const config = await readConfig(configPath);
346
+ const profileName = getProfileName(config, stringOption(global.profile));
347
+ const profile = selectProfile(config, profileName);
348
+ return new ApiClient(profile, context.fetch, async ({ token, expires }) => {
349
+ profile.token = token;
350
+ profile.token_expires_at = expires;
351
+ await writeConfig(configPath, config);
352
+ });
353
+ }
354
+ async function confirmWrite(command, context) {
355
+ const global = getGlobal(command);
356
+ if (global.yes)
357
+ return;
358
+ if (!context.stdin?.isTTY) {
359
+ throw new Error("写操作需要确认;在非交互环境请显式传入 --yes");
360
+ }
361
+ const accepted = await confirm({ message: "确认执行写操作?", default: false }, { input: context.stdin, output: context.stderr });
362
+ if (!accepted)
363
+ throw new Error("已取消写操作");
364
+ }
365
+ function getGlobal(command) {
366
+ let root = command;
367
+ while (root.parent)
368
+ root = root.parent;
369
+ return root.opts();
370
+ }
371
+ function getConfigPath(global, context) {
372
+ return resolveConfigPath({
373
+ explicitPath: stringOption(global.config),
374
+ env: context.env,
375
+ homeDir: context.homeDir
376
+ });
377
+ }
378
+ function writeResult(context, global, value) {
379
+ write(context.stdout, global.json ? formatJson(value) : formatHuman(value));
380
+ }
381
+ function write(stream, text) {
382
+ stream.write(text);
383
+ }
384
+ function stringOption(value) {
385
+ return typeof value === "string" && value.length > 0 ? value : undefined;
386
+ }
387
+ function queryToRecord(query) {
388
+ if (!query)
389
+ return undefined;
390
+ const output = {};
391
+ for (const [key, value] of Object.entries(query)) {
392
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
393
+ output[key] = value;
394
+ }
395
+ }
396
+ return output;
397
+ }
398
+ async function writeBinary(path, data) {
399
+ await new Promise((resolve, reject) => {
400
+ const stream = createWriteStream(path, { mode: 0o600 });
401
+ stream.on("error", reject);
402
+ stream.on("finish", resolve);
403
+ stream.end(Buffer.from(data));
404
+ });
405
+ }
406
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { AppConfig, Profile } from "./types.js";
2
+ export declare const CONFIG_ENV = "NGINX_PROXY_MANAGER_CONFIG";
3
+ export declare const DEFAULT_CONFIG_BASENAME = ".nginx-xproxy-manager.json";
4
+ export type ConfigPathInput = {
5
+ explicitPath?: string | undefined;
6
+ env?: NodeJS.ProcessEnv;
7
+ homeDir?: string;
8
+ };
9
+ export declare function resolveConfigPath(input?: ConfigPathInput): string;
10
+ export declare function expandHome(path: string, homeDir: string): string;
11
+ export declare function pathExists(path: string): Promise<boolean>;
12
+ export declare function readConfig(path: string): Promise<AppConfig>;
13
+ export declare function writeConfig(path: string, config: AppConfig): Promise<void>;
14
+ export declare function addProfile(path: string, name: string, profile: Profile, makeDefault: boolean): Promise<AppConfig>;
15
+ export declare function removeProfile(path: string, name: string): Promise<AppConfig>;
16
+ export declare function useProfile(path: string, name: string): Promise<AppConfig>;
17
+ export declare function selectProfile(config: AppConfig, name?: string): Profile;
18
+ export declare function getProfileName(config: AppConfig, name?: string): string;
package/dist/config.js ADDED
@@ -0,0 +1,113 @@
1
+ import { constants } from "node:fs";
2
+ import { access, chmod, mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { dirname, resolve } from "node:path";
4
+ import { z } from "zod";
5
+ export const CONFIG_ENV = "NGINX_PROXY_MANAGER_CONFIG";
6
+ export const DEFAULT_CONFIG_BASENAME = ".nginx-xproxy-manager.json";
7
+ const profileSchema = z.object({
8
+ base_url: z.string().url(),
9
+ username: z.string().min(1),
10
+ password: z.string().min(1),
11
+ token: z.string().min(1).optional(),
12
+ token_expires_at: z.string().min(1).optional()
13
+ });
14
+ const configSchema = z.object({
15
+ default_profile: z.string().min(1).optional(),
16
+ profiles: z.record(profileSchema).default({})
17
+ });
18
+ export function resolveConfigPath(input = {}) {
19
+ const env = input.env ?? process.env;
20
+ const homeDir = input.homeDir ?? process.env.HOME ?? "";
21
+ const chosen = input.explicitPath ?? env[CONFIG_ENV] ?? `${homeDir}/${DEFAULT_CONFIG_BASENAME}`;
22
+ return expandHome(chosen, homeDir);
23
+ }
24
+ export function expandHome(path, homeDir) {
25
+ if (path === "~")
26
+ return homeDir;
27
+ if (path.startsWith("~/"))
28
+ return resolve(homeDir, path.slice(2));
29
+ return resolve(path);
30
+ }
31
+ export async function pathExists(path) {
32
+ try {
33
+ await access(path, constants.F_OK);
34
+ return true;
35
+ }
36
+ catch {
37
+ return false;
38
+ }
39
+ }
40
+ export async function readConfig(path) {
41
+ if (!(await pathExists(path))) {
42
+ return { profiles: {} };
43
+ }
44
+ const raw = await readFile(path, "utf8");
45
+ const parsed = JSON.parse(raw);
46
+ return configSchema.parse(parsed);
47
+ }
48
+ export async function writeConfig(path, config) {
49
+ const normalized = configSchema.parse(config);
50
+ await mkdir(dirname(path), { recursive: true });
51
+ await writeFile(path, `${JSON.stringify(normalized, null, 2)}\n`, { mode: 0o600 });
52
+ try {
53
+ await chmod(path, 0o600);
54
+ }
55
+ catch {
56
+ // 某些文件系统不支持 chmod,写入模式已经尽量限制为 0600。
57
+ }
58
+ }
59
+ export async function addProfile(path, name, profile, makeDefault) {
60
+ const config = await readConfig(path);
61
+ config.profiles[name] = profileSchema.parse(profile);
62
+ if (makeDefault || !config.default_profile) {
63
+ config.default_profile = name;
64
+ }
65
+ await writeConfig(path, config);
66
+ return config;
67
+ }
68
+ export async function removeProfile(path, name) {
69
+ const config = await readConfig(path);
70
+ if (!config.profiles[name]) {
71
+ throw new Error(`profile 不存在:${name}`);
72
+ }
73
+ delete config.profiles[name];
74
+ if (config.default_profile === name) {
75
+ const nextDefault = Object.keys(config.profiles)[0];
76
+ if (nextDefault) {
77
+ config.default_profile = nextDefault;
78
+ }
79
+ else {
80
+ delete config.default_profile;
81
+ }
82
+ }
83
+ await writeConfig(path, config);
84
+ return config;
85
+ }
86
+ export async function useProfile(path, name) {
87
+ const config = await readConfig(path);
88
+ if (!config.profiles[name]) {
89
+ throw new Error(`profile 不存在:${name}`);
90
+ }
91
+ config.default_profile = name;
92
+ await writeConfig(path, config);
93
+ return config;
94
+ }
95
+ export function selectProfile(config, name) {
96
+ const profileName = name ?? config.default_profile;
97
+ if (!profileName) {
98
+ throw new Error("未配置默认 profile,请先运行 profile add 或使用 --profile");
99
+ }
100
+ const profile = config.profiles[profileName];
101
+ if (!profile) {
102
+ throw new Error(`profile 不存在:${profileName}`);
103
+ }
104
+ return profile;
105
+ }
106
+ export function getProfileName(config, name) {
107
+ const profileName = name ?? config.default_profile;
108
+ if (!profileName || !config.profiles[profileName]) {
109
+ throw new Error("未找到可用 profile");
110
+ }
111
+ return profileName;
112
+ }
113
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { createProgram, parseUnknownFieldOptions } from "./commands.js";
2
+ export { ApiClient, resourcePath } from "./client.js";
3
+ export { CONFIG_ENV, DEFAULT_CONFIG_BASENAME, readConfig, resolveConfigPath, writeConfig } from "./config.js";
4
+ export { formatHuman, formatJson } from "./output.js";
5
+ export { redactSecrets } from "./safe.js";
6
+ export type { AppConfig, JsonObject, JsonValue, Profile } from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { createProgram, parseUnknownFieldOptions } from "./commands.js";
2
+ export { ApiClient, resourcePath } from "./client.js";
3
+ export { CONFIG_ENV, DEFAULT_CONFIG_BASENAME, readConfig, resolveConfigPath, writeConfig } from "./config.js";
4
+ export { formatHuman, formatJson } from "./output.js";
5
+ export { redactSecrets } from "./safe.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,uBAAuB,EAAE,UAAU,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC9G,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC"}
package/dist/json.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { JsonObject } from "./types.js";
2
+ export declare function parseJsonObject(raw: string, source?: string): JsonObject;
3
+ export declare function readJsonObjectFile(path: string): Promise<JsonObject>;
4
+ export declare function mergePayloads(...payloads: Array<JsonObject | undefined>): JsonObject;
package/dist/json.js ADDED
@@ -0,0 +1,36 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { z } from "zod";
3
+ const jsonValueSchema = z.lazy(() => z.union([
4
+ z.string(),
5
+ z.number(),
6
+ z.boolean(),
7
+ z.null(),
8
+ z.array(jsonValueSchema),
9
+ z.record(jsonValueSchema)
10
+ ]));
11
+ const jsonObjectSchema = z.record(jsonValueSchema);
12
+ export function parseJsonObject(raw, source = "JSON") {
13
+ try {
14
+ const parsed = JSON.parse(raw);
15
+ return jsonObjectSchema.parse(parsed);
16
+ }
17
+ catch (error) {
18
+ const message = error instanceof Error ? error.message : String(error);
19
+ throw new Error(`${source} 不是合法 JSON object:${message}`, { cause: error });
20
+ }
21
+ }
22
+ export async function readJsonObjectFile(path) {
23
+ return parseJsonObject(await readFile(path, "utf8"), path);
24
+ }
25
+ export function mergePayloads(...payloads) {
26
+ const merged = {};
27
+ for (const payload of payloads) {
28
+ if (!payload)
29
+ continue;
30
+ for (const [key, value] of Object.entries(payload)) {
31
+ merged[key] = value;
32
+ }
33
+ }
34
+ return merged;
35
+ }
36
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,eAAe,GAAyB,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CACxD,CAAC,CAAC,KAAK,CAAC;IACN,CAAC,CAAC,MAAM,EAAE;IACV,CAAC,CAAC,MAAM,EAAE;IACV,CAAC,CAAC,OAAO,EAAE;IACX,CAAC,CAAC,IAAI,EAAE;IACR,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC;IACxB,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC;CAC1B,CAAC,CACH,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;AAEnD,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,MAAM,GAAG,MAAM;IAC1D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;QAC1C,OAAO,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,qBAAqB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAY;IACnD,OAAO,eAAe,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAG,QAAuC;IACtE,MAAM,MAAM,GAAe,EAAE,CAAC;IAC9B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACnD,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACtB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { AppConfig, JsonValue } from "./types.js";
2
+ export declare function formatJson(value: JsonValue): string;
3
+ export declare function formatHuman(value: JsonValue): string;
4
+ export declare function formatProfileList(config: AppConfig, json: boolean): string;
package/dist/output.js ADDED
@@ -0,0 +1,28 @@
1
+ import { assertSafeText, redactSecrets } from "./safe.js";
2
+ export function formatJson(value) {
3
+ const text = `${JSON.stringify(redactSecrets(value), null, 2)}\n`;
4
+ assertSafeText(text);
5
+ return text;
6
+ }
7
+ export function formatHuman(value) {
8
+ const safeValue = redactSecrets(value);
9
+ const text = typeof safeValue === "string" ? `${safeValue}\n` : `${JSON.stringify(safeValue, null, 2)}\n`;
10
+ assertSafeText(text);
11
+ return text;
12
+ }
13
+ export function formatProfileList(config, json) {
14
+ const profiles = Object.entries(config.profiles).map(([name, profile]) => ({
15
+ name,
16
+ default: config.default_profile === name,
17
+ base_url: profile.base_url,
18
+ username: profile.username
19
+ }));
20
+ if (json)
21
+ return formatJson(profiles);
22
+ if (profiles.length === 0)
23
+ return "未配置 profile\n";
24
+ return `${profiles
25
+ .map((profile) => `${profile.default ? "*" : " "} ${profile.name}\t${profile.base_url}\t${profile.username}`)
26
+ .join("\n")}\n`;
27
+ }
28
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1D,MAAM,UAAU,UAAU,CAAC,KAAgB;IACzC,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;IAClE,cAAc,CAAC,IAAI,CAAC,CAAC;IACrB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAgB;IAC1C,MAAM,SAAS,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;IAC1G,cAAc,CAAC,IAAI,CAAC,CAAC;IACrB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAiB,EAAE,IAAa;IAChE,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;QACzE,IAAI;QACJ,OAAO,EAAE,MAAM,CAAC,eAAe,KAAK,IAAI;QACxC,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAC,CAAC,CAAC;IACJ,IAAI,IAAI;QAAE,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC;IACtC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,eAAe,CAAC;IAClD,OAAO,GAAG,QAAQ;SACf,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC;SAC5G,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AACpB,CAAC"}
package/dist/safe.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { JsonValue } from "./types.js";
2
+ export declare function redactSecrets<T extends JsonValue>(value: T): T;
3
+ export declare function assertSafeText(text: string): void;
package/dist/safe.js ADDED
@@ -0,0 +1,28 @@
1
+ const secretKeys = new Set([
2
+ "authorization",
3
+ "jwt",
4
+ "password",
5
+ "secret",
6
+ "token",
7
+ "access_token",
8
+ "refresh_token"
9
+ ]);
10
+ export function redactSecrets(value) {
11
+ if (Array.isArray(value)) {
12
+ return value.map((item) => redactSecrets(item));
13
+ }
14
+ if (value && typeof value === "object") {
15
+ const redacted = {};
16
+ for (const [key, nested] of Object.entries(value)) {
17
+ redacted[key] = secretKeys.has(key.toLowerCase()) ? "[redacted]" : redactSecrets(nested);
18
+ }
19
+ return redacted;
20
+ }
21
+ return value;
22
+ }
23
+ export function assertSafeText(text) {
24
+ if (/eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+/.test(text)) {
25
+ throw new Error("输出包含疑似 JWT,已阻止打印");
26
+ }
27
+ }
28
+ //# sourceMappingURL=safe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe.js","sourceRoot":"","sources":["../src/safe.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC;IACzB,eAAe;IACf,KAAK;IACL,UAAU;IACV,QAAQ;IACR,OAAO;IACP,cAAc;IACd,eAAe;CAChB,CAAC,CAAC;AAEH,MAAM,UAAU,aAAa,CAAsB,KAAQ;IACzD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAM,CAAC;IACvD,CAAC;IACD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,QAAQ,GAA8B,EAAE,CAAC;QAC/C,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAClD,QAAQ,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QAC3F,CAAC;QACD,OAAO,QAAa,CAAC;IACvB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,mDAAmD,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACtC,CAAC;AACH,CAAC"}
@@ -0,0 +1,31 @@
1
+ export type JsonValue = string | number | boolean | null | JsonValue[] | {
2
+ [key: string]: JsonValue;
3
+ };
4
+ export type JsonObject = {
5
+ [key: string]: JsonValue;
6
+ };
7
+ export interface Profile {
8
+ base_url: string;
9
+ username: string;
10
+ password: string;
11
+ token?: string | undefined;
12
+ token_expires_at?: string | undefined;
13
+ }
14
+ export interface AppConfig {
15
+ default_profile?: string | undefined;
16
+ profiles: Record<string, Profile>;
17
+ }
18
+ export interface RuntimeContext {
19
+ env: NodeJS.ProcessEnv;
20
+ homeDir: string;
21
+ fetch: typeof fetch;
22
+ stdin?: NodeJS.ReadStream;
23
+ stdout: NodeJS.WriteStream | NodeJS.WritableStream;
24
+ stderr: NodeJS.WriteStream | NodeJS.WritableStream;
25
+ }
26
+ export interface RequestRecord {
27
+ method: string;
28
+ path: string;
29
+ query?: Record<string, string | number | boolean> | undefined;
30
+ body?: JsonValue | FormData | undefined;
31
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "nginx-proxy-manager-cli",
3
+ "version": "0.2.0",
4
+ "description": "用于管理 Nginx Proxy Manager API。",
5
+ "type": "module",
6
+ "packageManager": "pnpm@10.23.0",
7
+ "bin": {
8
+ "nginx-proxy-manager": "dist/cli.js"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "AGENTS.md",
20
+ "CHANGELOG.md"
21
+ ],
22
+ "license": "MIT",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+ssh://git@github.com/Aturan/nginx-proxy-manager-cli.git"
26
+ },
27
+ "bugs": {
28
+ "url": "https://github.com/Aturan/nginx-proxy-manager-cli/issues"
29
+ },
30
+ "homepage": "https://github.com/Aturan/nginx-proxy-manager-cli#readme",
31
+ "keywords": [
32
+ "nginx-proxy-manager",
33
+ "npm-cli",
34
+ "proxy-hosts",
35
+ "certificates",
36
+ "streams"
37
+ ],
38
+ "engines": {
39
+ "node": ">=20.11"
40
+ },
41
+ "scripts": {
42
+ "build": "tsc -p tsconfig.build.json",
43
+ "clean": "rimraf dist coverage",
44
+ "dev": "pnpm build && node dist/cli.js",
45
+ "lint": "eslint .",
46
+ "lint:commit": "commitlint --from=HEAD~1 --to=HEAD",
47
+ "prepare": "husky",
48
+ "typecheck": "tsc --noEmit",
49
+ "test": "vitest run tests/unit tests/integration",
50
+ "release": "node -e \"console.error('Use pnpm release:patch, pnpm release:minor, or pnpm release:major'); process.exit(1)\"",
51
+ "release:patch": "release-it patch --ci",
52
+ "release:minor": "release-it minor --ci",
53
+ "release:major": "release-it major --ci"
54
+ },
55
+ "dependencies": {
56
+ "@inquirer/prompts": "^7.8.0",
57
+ "commander": "^14.0.0",
58
+ "undici": "^7.12.0",
59
+ "zod": "^3.25.76"
60
+ },
61
+ "devDependencies": {
62
+ "@commitlint/cli": "^21.2.0",
63
+ "@commitlint/config-conventional": "^21.2.0",
64
+ "@eslint/js": "^9.39.4",
65
+ "@release-it/conventional-changelog": "^11.0.1",
66
+ "@types/node": "^24.0.10",
67
+ "eslint": "^9.30.1",
68
+ "husky": "^9.1.7",
69
+ "release-it": "^18.1.2",
70
+ "rimraf": "^6.0.1",
71
+ "typescript": "^5.8.3",
72
+ "typescript-eslint": "^8.35.1",
73
+ "vitest": "^3.2.4"
74
+ },
75
+ "publishConfig": {
76
+ "access": "public",
77
+ "provenance": true
78
+ }
79
+ }