mirror of
https://github.com/JimLiu/baoyu-skills.git
synced 2026-08-07 09:23:04 +08:00
feat(baoyu-post-to-wechat): add remote-api publishing via SSH SOCKS5 tunnel
Re-implements PR #156 (remote WeChat API publishing for IP-allowlist constraints) with a fully local code path: spawn a background `ssh -N -D` SOCKS5 dynamic forward, wrap a `SocksProxyAgent`, and thread it through the existing token/upload/draft flow. No Python helper, no SCP, no remote files, no `AppSecret` ever leaving the local process. Reusing `uploadImagesInHtml` (which replaces the matched `<img>` fullTag rather than substring-replacing `src`) naturally avoids the original PR's `html.replace(src, url)` HTML-replacement bug. Changes: - New `wechat-remote-publish.ts`: typed-whitelist SSH args, free-port allocation, SOCKS readiness polling, signal-handler-backed cleanup. - New `wechat-http.ts`: minimal `https.request`-backed HTTP helper so a `SocksProxyAgent` can be passed through (undici `fetch` ignores agent). - New `wechat-image-loader.ts`: extracts `loadUploadAsset` for reuse. - `wechat-api.ts`: thread optional `agent?` through token/upload/draft; add `--remote*` CLI flags; dispatch via `withSshTunnel` when remote mode is selected by flag or `default_publish_method: remote-api`. - `wechat-extend-config.ts`: add typed `remote_publish_*` keys with account-over-global fallback and value validation. - Docs: SKILL.md, references/multi-account.md, references/config/ first-time-setup.md, README.md, README.zh.md. - Tests: 17 new node:test cases covering config parsing, SSH arg whitelisting, free-port allocation, multipart assembly, and HTTP agent threading. Co-authored-by: Dame5211 <1079825614@qq.com>
This commit is contained in:
@@ -86,8 +86,12 @@ options:
|
||||
description: "Fast, requires API credentials (AppID + AppSecret)"
|
||||
- label: "browser"
|
||||
description: "Slow, requires Chrome and login session"
|
||||
- label: "remote-api"
|
||||
description: "Fast, tunnels WeChat API calls through SSH to a server whose IP is on the WeChat allowlist"
|
||||
```
|
||||
|
||||
If the user selects `remote-api`, prompt for `remote_publish_host` and (optionally) `remote_publish_user`, `remote_publish_identity_file`. These can also be filled in later by editing EXTEND.md.
|
||||
|
||||
### Question 4: Default Author
|
||||
|
||||
```yaml
|
||||
@@ -157,13 +161,26 @@ options:
|
||||
```md
|
||||
default_theme: [default/grace/simple/modern]
|
||||
default_color: [preset name, hex, or empty for theme default]
|
||||
default_publish_method: [api/browser]
|
||||
default_publish_method: [api/browser/remote-api]
|
||||
default_author: [author name or empty]
|
||||
need_open_comment: [1/0]
|
||||
only_fans_can_comment: [1/0]
|
||||
chrome_profile_path:
|
||||
|
||||
# Remote API publishing — only fill in if default_publish_method is remote-api
|
||||
# or you plan to pass --remote on the CLI.
|
||||
remote_publish_host:
|
||||
remote_publish_user:
|
||||
remote_publish_port:
|
||||
remote_publish_identity_file:
|
||||
remote_publish_known_hosts_file:
|
||||
remote_publish_strict_host_key_checking:
|
||||
remote_publish_connect_timeout:
|
||||
remote_publish_proxy_jump:
|
||||
```
|
||||
|
||||
Raw `ssh` / `scp` options are intentionally not supported; only the typed keys above are honored. Authentication is SSH key only.
|
||||
|
||||
### Multi-Account
|
||||
|
||||
```md
|
||||
@@ -174,15 +191,19 @@ accounts:
|
||||
- name: [display name]
|
||||
alias: [short key, e.g. "baoyu"]
|
||||
default: true
|
||||
default_publish_method: [api/browser]
|
||||
default_publish_method: [api/browser/remote-api]
|
||||
default_author: [author name]
|
||||
need_open_comment: [1/0]
|
||||
only_fans_can_comment: [1/0]
|
||||
app_id: [WeChat App ID, optional]
|
||||
app_secret: [WeChat App Secret, optional]
|
||||
# Remote API publishing (optional, per-account override of globals)
|
||||
remote_publish_host:
|
||||
remote_publish_user:
|
||||
remote_publish_identity_file:
|
||||
- name: [second account name]
|
||||
alias: [short key, e.g. "ai-tools"]
|
||||
default_publish_method: [api/browser]
|
||||
default_publish_method: [api/browser/remote-api]
|
||||
default_author: [author name]
|
||||
need_open_comment: [1/0]
|
||||
only_fans_can_comment: [1/0]
|
||||
|
||||
@@ -37,7 +37,7 @@ accounts:
|
||||
|
||||
## Per-Account vs Global Keys
|
||||
|
||||
**Per-account** (also accepted globally as fallback): `default_publish_method`, `default_author`, `need_open_comment`, `only_fans_can_comment`, `app_id`, `app_secret`, `chrome_profile_path`.
|
||||
**Per-account** (also accepted globally as fallback): `default_publish_method`, `default_author`, `need_open_comment`, `only_fans_can_comment`, `app_id`, `app_secret`, `chrome_profile_path`, `remote_publish_host`, `remote_publish_user`, `remote_publish_port`, `remote_publish_identity_file`, `remote_publish_known_hosts_file`, `remote_publish_strict_host_key_checking`, `remote_publish_connect_timeout`, `remote_publish_proxy_jump`.
|
||||
|
||||
**Global-only** (always shared): `default_theme`, `default_color`.
|
||||
|
||||
@@ -99,3 +99,54 @@ ${BUN_X} {baseDir}/scripts/wechat-api.ts <file> --theme default --account ai-too
|
||||
${BUN_X} {baseDir}/scripts/wechat-article.ts --markdown <file> --theme default --account baoyu
|
||||
${BUN_X} {baseDir}/scripts/wechat-browser.ts --markdown <file> --images ./photos/ --account baoyu
|
||||
```
|
||||
|
||||
## Remote API Publishing
|
||||
|
||||
`wechat-api.ts` supports a `remote-api` mode that tunnels WeChat API calls through an SSH SOCKS5 dynamic port forward to a server whose IP is on WeChat's allowlist. Markdown rendering, image processing, draft assembly, and HTML rewriting still happen locally; only outbound HTTPS calls to `api.weixin.qq.com` traverse the tunnel. No files are written to the remote host and `AppSecret` never leaves the local process. The remote host needs only `sshd` and outbound network access.
|
||||
|
||||
### Per-Account Configuration
|
||||
|
||||
```md
|
||||
default_theme: default
|
||||
default_color: blue
|
||||
default_publish_method: browser # browser remains the default
|
||||
|
||||
accounts:
|
||||
- name: 宝玉的技术分享
|
||||
alias: baoyu
|
||||
default: true
|
||||
default_publish_method: api
|
||||
default_author: 宝玉
|
||||
app_id: your_wechat_app_id
|
||||
app_secret: your_wechat_app_secret
|
||||
- name: AI工具集
|
||||
alias: ai-tools
|
||||
default_publish_method: remote-api
|
||||
default_author: AI工具集
|
||||
app_id: your_ai_tools_app_id
|
||||
app_secret: your_ai_tools_app_secret
|
||||
remote_publish_host: ai-tools-server.example.com
|
||||
remote_publish_user: deploy
|
||||
remote_publish_port: 22
|
||||
remote_publish_identity_file: /home/me/.ssh/id_ed25519
|
||||
remote_publish_known_hosts_file: /home/me/.ssh/known_hosts
|
||||
remote_publish_strict_host_key_checking: accept-new
|
||||
```
|
||||
|
||||
Account-level `remote_publish_*` values override top-level globals. CLI `--remote-*` flags override both.
|
||||
|
||||
### CLI Usage
|
||||
|
||||
```bash
|
||||
# Use the account's default_publish_method (remote-api here):
|
||||
${BUN_X} {baseDir}/scripts/wechat-api.ts <file> --theme default --account ai-tools
|
||||
|
||||
# Force remote mode regardless of default_publish_method:
|
||||
${BUN_X} {baseDir}/scripts/wechat-api.ts <file> --theme default --account baoyu --remote --remote-host other-server.example.com
|
||||
```
|
||||
|
||||
### Security Notes
|
||||
|
||||
- Authentication is SSH key only. Passwords and `ssh-askpass` are not used.
|
||||
- Only the typed `remote_publish_*` keys are read; raw `ssh` / `scp` options are intentionally not supported.
|
||||
- The tunnel forwards raw TCP; TLS verification for `api.weixin.qq.com` is still performed end-to-end by the local process.
|
||||
|
||||
Reference in New Issue
Block a user