Compare commits

..
9 Commits
Author SHA1 Message Date
DeepFal c71668e82d build(release): 客户端与服务端分开打包,macOS 提供 dmg 安装镜像
- 服务端:portrelay-server-<版本>-linux-{amd64,arm64}.tar.gz(含 deploy/ 安装脚本)
- 客户端:PortRelay-<版本>.dmg(通用版本),以及 Linux / Windows 的 portrelay-agent 压缩包
- make dmg 打包安装镜像;Release 说明按客户端 / 服务端列出下载项
2026-10-14 10:41:38 +08:00
DeepFal dceb59c0ca feat: 授权延长——同事一键申请,管理员批准时决定时长;时长支持 30 分钟与自定义
同事端
- 用过邀请链接的设备再次打开链接时显示自己的授权状态:有效时可申请延长,
  访问结束(到期或通道关闭)后 2 小时内也可以;超过 2 小时或被撤销则提示重新获取邀请
- 「申请延长」只有一个按钮,提交后复用审批状态页,批准后显示新的到期时间
- 用过链接的设备不再自动重新放通,统一走申请延长

网关
- 授权结束后保留 2 小时的记录(通道关闭后仍保留,随状态文件持久化);撤销的不能延长,
  删除通道 / 解散组时一并清除(unregister 带 deleted 标记)
- 新增 extend_lease:有效授权从原到期时间顺延,已结束的从现在起算,不受通道最长开启时长截断,
  并批准该设备的延长申请;指标里带上全部可延长设备与申请(包括已关闭的通道)
- 授权时长支持分钟数("30m"、"90m",5 分钟到 30 天)

Agent / App / 网页面板
- POST .../leases/extend:通道已关闭时先自动开启(组则全组开启),并让通道至少开到授权结束
- 授权管理新增「申请延长」「最近结束 · 2 小时内可延长」,已授权设备可直接「延长」;
  卡片、菜单栏和系统通知提示延长申请,菜单栏可直接选时长批准
- 邀请的可访问时长、通道最长开启时长增加 30 分钟与自定义;网页面板弹窗层级高于侧边抽屉
- 邀请分享文案提示「授权结束后 2 小时内可重新打开此链接申请延长」
2026-10-14 10:11:06 +08:00
DeepFal 9a675ba96e feat: 新建时不再选择通道组,「添加地址」固定加入所在组;占位提示统一措辞
- 「添加通道」去掉通道组下拉:一个地址为独立通道,多个地址自动放进以名称命名的新组,
  只在目标列表下方说明结果
- 组卡片「添加地址」标题为「添加地址到「组名」」,固定加入该组,名称可选(留空按「组名 (主机)」命名),
  按钮为「添加 / 添加 N 个地址」,不再自动弹出邀请
- 通道组选择只保留在编辑已有通道时(不分组 / 新建通道组 / 已有组)
- 占位提示统一为「如:…」并说明格式:IP 或域名、IP 或 CIDR 网段、负责人或团队等
- App 与网页面板一致
2026-10-13 21:31:22 +08:00
DeepFal dfb3a9fd8e build: make install-app 构建并替换 /Applications 中的 App
正在运行的 App 会先正常退出(同时停止后台引擎),装好后按原状态重新打开。
2026-10-13 21:24:48 +08:00
DeepFal 659e69f8f7 feat: 目标改为动态列表、访问端口全自动;菜单栏合并通道组;接入记录可按地址查看
添加通道
- 目标服务改成列表:每行一台主机,多个端口用逗号隔开(也支持 8080-8082、主机范围);
  「添加主机」沿用上一行的端口,粘贴一串 IP / 主机:端口会自动拆成多行
- 新建时不再手动指定对外端口,统一自动分配;预览里列出每个地址分到的端口,
  连通性测试并发进行,结果标在每个地址后面
- 超过一个地址自动放进以名称命名的新通道组(也可选加入已有组),只有一个地址时为不分组;
  编辑通道仍保留端口栏和「新建通道组」
- 网页面板同步:同样的目标列表、自动端口与通道组规则

菜单栏
- 同一个通道组合并为一行「组名(N 个地址)」,子菜单里整组开关、逐个地址开关

连接与授权(通道组)
- 当前在线、历史接入台账增加「按设备 / 按地址」切换:按地址列出组内每个地址
  有几台设备在用、谁访问过;按设备的汇总行显示访问了组内哪些地址,并带上授权姓名
- 网页面板的在线连接同样可以按地址查看
2026-10-13 21:23:25 +08:00
DeepFal 5b355fb759 feat: 通道组并入「添加通道」表单,不再单独新建
- 去掉单独的「新建通道组」入口:添加通道表单的目标可以一次填多个
  (10.0.1.11-13:22、10.0.1.11:8080-8082、逗号分隔),预览每个地址分到的连续端口
- 表单新增「通道组」选项:不分组 / 新建通道组 / 已有组;多个目标自动放进新建的组,
  组名默认与通道名称相同;加入组时名称可不填,按「组名 (主机)」命名
- 闲置关闭、最长时长、限速、访问控制、责任人等设置对每个地址都生效,与单个通道一致
- 编辑通道时可以新建通道组并移入;组卡片上「添加地址」打开同一个表单并选好该组
- 编辑通道组只负责改名、责任人和加入已有通道
- 多个目标的连通性测试并发进行并汇总结果;网页表单补上责任人与限速
- 接口:POST /api/tunnels/batch(dry_run 预览、group_id / new_group),
  PUT /api/tunnels/{id} 支持 new_group;移除 /api/groups/preview 及组接口里的批量建通道
2026-10-13 20:55:57 +08:00
DeepFal e02c4c1184 fix(mac): 拆分长字符串表达式,避免旧版编译器类型推断超时
GitHub Actions 的 macOS runner(较旧的 Xcode)编译菜单签名拼接时报
"unable to type-check this expression in reasonable time"。
2026-10-13 20:22:54 +08:00
DeepFal 3797bef88a feat: 通道组——一次开放多台主机 / 多个端口,共用邀请与授权
- 批量创建:主机支持 IP、主机名、末段范围(10.0.1.11-13)和 主机:端口,
  与端口列表展开为 主机 × 端口,自动分配连续的访问端口,创建前可预览
- 授权以组为单位:一条邀请链接、一次审批即可访问组内全部地址;
  计时从首次连上任一地址开始,撤销一次全组断开;后加入组的通道自动获得组授权
- 网关授权状态按作用范围(通道 / 通道组)存储,通道的动态白名单统一由状态库重新计算,
  通道授权与组授权互不干扰;组内最后一个通道关闭或组被解散时,组的邀请与授权失效
- 闲置联动:组内任一地址在用时整组都不算闲置;支持整组开关
- 已有通道可并入组或移出组;组可改名、追加目标、解散(保留或删除成员)
- App 与网页面板:组卡片、新建 / 编辑通道组、组邀请与“连接与授权”,授权页列出全部地址
- 测试:网关 9 个、Agent 5 个通道组用例
2026-10-13 20:16:43 +08:00
DeepFal 86e159b3f0 PortRelay:把内网服务临时、可控地开放给同事
边缘网关 + Agent 的端口中继:Agent 主动连出到网关,把内网目标挂到网关端口上;
通过邀请链接按设备授权,可选审批,首次连接起计时,到期、闲置自动收回。

- portrelay-server:边缘网关(端口池、白名单与授权租约、邀请与授权网页)
- portrelay-agent:Agent(yamux 隧道、本地 API 与网页面板、流量统计)
- mac-native:SwiftUI 菜单栏 App
2026-10-13 16:33:17 +08:00
5 changed files with 88 additions and 29 deletions

No files matched your search

+33
View File
@@ -0,0 +1,33 @@
## 下载 · Downloads
### 客户端 · Client
装在能访问内网服务的电脑上,用来创建通道、邀请同事。
Install it on a machine that can reach your internal services.
| 平台 Platform | 文件 File |
| --- | --- |
| macOS(Apple Silicon / Intel) | [PortRelay-{{VERSION}}.dmg]({{DOWNLOAD}}/PortRelay-{{VERSION}}.dmg) |
| Linux x86_64 | [portrelay-agent-{{VERSION}}-linux-amd64.tar.gz]({{DOWNLOAD}}/portrelay-agent-{{VERSION}}-linux-amd64.tar.gz) |
| Linux ARM64 | [portrelay-agent-{{VERSION}}-linux-arm64.tar.gz]({{DOWNLOAD}}/portrelay-agent-{{VERSION}}-linux-arm64.tar.gz) |
| Windows x86_64 | [portrelay-agent-{{VERSION}}-windows-amd64.zip]({{DOWNLOAD}}/portrelay-agent-{{VERSION}}-windows-amd64.zip) |
macOS 是菜单栏 App:打开 dmg,把 PortRelay 拖进「应用程序」。App 未经 Apple 公证,首次打开请在 Finder 里右键选择「打开」。Linux / Windows 是命令行 Agent,运行后在浏览器里打开管理面板。
On macOS, open the dmg and drag PortRelay to Applications. The app is not notarized, so right-click it and choose Open the first time. On Linux and Windows, run the agent and use the web panel it opens.
### 服务端 · Server (edge gateway)
部署在同事和客户端都能连到的 Linux 机器上。
Deploy it on a Linux host that both teammates and the client can reach.
| 方式 Method | 文件 File |
| --- | --- |
| 二进制 + systemd · x86_64 | [portrelay-server-{{VERSION}}-linux-amd64.tar.gz]({{DOWNLOAD}}/portrelay-server-{{VERSION}}-linux-amd64.tar.gz) |
| 二进制 + systemd · ARM64 | [portrelay-server-{{VERSION}}-linux-arm64.tar.gz]({{DOWNLOAD}}/portrelay-server-{{VERSION}}-linux-arm64.tar.gz) |
| Docker | `{{IMAGE}}:{{VERSION}}` |
解压后运行 `sudo ./deploy/install-server.sh ./portrelay-server`,再次运行即可升级。
Unpack and run `sudo ./deploy/install-server.sh ./portrelay-server`; run it again to upgrade.
校验文件 Checksums:`SHA256SUMS`
+20 -8
View File
@@ -12,7 +12,7 @@ permissions:
jobs:
binaries:
name: Server & agent binaries
name: Server & agent archives
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -31,24 +31,22 @@ jobs:
dist/*.zip
macos-app:
name: macOS app
name: macOS app (dmg)
runs-on: macos-15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Build universal app
- name: Build universal dmg
run: |
VERSION=${GITHUB_REF_NAME#v}
case "$VERSION" in [0-9]*) ;; *) VERSION=0.0.0-${GITHUB_SHA::7} ;; esac
UNIVERSAL=1 VERSION=$VERSION ./build-app.sh
mkdir -p dist
ditto -c -k --sequesterRsrc --keepParent PortRelay.app "dist/PortRelay-$VERSION-macos-universal.zip"
UNIVERSAL=1 make dmg VERSION=$VERSION
- uses: actions/upload-artifact@v4
with:
name: macos-app
path: dist/*.zip
path: dist/*.dmg
docker:
name: Docker image
@@ -88,13 +86,27 @@ jobs:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- name: Checksums
run: cd dist && sha256sum * > SHA256SUMS
# 说明 = 标签注释正文(本次更新)+ 下载说明(区分客户端与服务端)+ 自动生成的变更列表
- name: Release notes
run: |
git fetch --force origin "refs/tags/$GITHUB_REF_NAME:refs/tags/$GITHUB_REF_NAME"
body=$(git tag -l --format='%(contents:body)' "$GITHUB_REF_NAME")
{
if [ -n "$body" ]; then printf "## 本次更新 · What's new\n\n%s\n\n" "$body"; fi
sed -e "s|{{VERSION}}|${GITHUB_REF_NAME#v}|g" \
-e "s|{{DOWNLOAD}}|https://github.com/$GITHUB_REPOSITORY/releases/download/$GITHUB_REF_NAME|g" \
-e "s|{{IMAGE}}|ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/portrelay-server|g" \
.github/release-notes.md
} > "$RUNNER_TEMP/notes.md"
cat "$RUNNER_TEMP/notes.md"
- name: Create release
env:
GH_TOKEN: ${{ github.token }}
run: gh release create "$GITHUB_REF_NAME" dist/* --repo "$GITHUB_REPOSITORY" --title "$GITHUB_REF_NAME" --generate-notes
run: gh release create "$GITHUB_REF_NAME" dist/* --repo "$GITHUB_REPOSITORY" --title "$GITHUB_REF_NAME" --notes-file "$RUNNER_TEMP/notes.md" --generate-notes
+25 -11
View File
@@ -4,16 +4,19 @@
# make agent 编译当前平台的 Agent 到 bin/
# make app 构建 macOS 应用 PortRelay.app(需要 Xcode 命令行工具)
# make install-app 构建并替换 /Applications/PortRelay.app(正在运行的会先退出,装好后重新打开)
# make release 打包所有平台的服务端 / Agent 到 dist/
# make dmg 打包 macOS 安装镜像 dist/PortRelay-<版本>.dmg(UNIVERSAL=1 构建通用版本)
# make release 打包服务端(Linux)与命令行 Agent(Linux / Windows)到 dist/
# make snapshots 离屏渲染 App 界面截图(tools/ui-snapshots/out)
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null | sed 's/^v//' || echo dev)
LDFLAGS := -s -w -X main.version=$(VERSION)
GOBUILD := CGO_ENABLED=0 go build -trimpath -ldflags "$(LDFLAGS)"
PLATFORMS := linux/amd64 linux/arm64 darwin/arm64 darwin/amd64 windows/amd64
# 服务端只发布 Linux 版本;macOS 用户用 App,其余平台用命令行 Agent
SERVER_PLATFORMS := linux/amd64 linux/arm64
AGENT_PLATFORMS := linux/amd64 linux/arm64 windows/amd64
.PHONY: all test fmt server agent app install-app release snapshots clean
.PHONY: all test fmt server agent app install-app dmg release snapshots clean
all: test agent
@@ -47,20 +50,31 @@ install-app: app
echo "✓ 已安装 /Applications/PortRelay.app ($(VERSION))"; \
if [ -n "$$running" ]; then open /Applications/PortRelay.app; fi
# 安装镜像:PortRelay.app 和「应用程序」的快捷方式,打开后拖进去即可
dmg: app
@rm -rf build/dmg && mkdir -p build/dmg dist
ditto PortRelay.app build/dmg/PortRelay.app && rm -rf PortRelay.app
ln -s /Applications build/dmg/Applications
hdiutil create -volname PortRelay -srcfolder build/dmg -fs HFS+ -format UDZO -ov dist/PortRelay-$(VERSION).dmg
@rm -rf build/dmg
# 服务端:portrelay-server-<版本>-linux-<架构>.tar.gz(含 deploy/ 安装脚本)
# 客户端:portrelay-agent-<版本>-<系统>-<架构>.tar.gz / .zip
release:
@rm -rf dist && mkdir -p dist
@for p in $(PLATFORMS); do \
os=$${p%/*}; arch=$${p#*/}; ext=; [ $$os = windows ] && ext=.exe; \
name=portrelay-$(VERSION)-$$os-$$arch; mkdir -p dist/$$name; \
@rm -rf dist/portrelay-* dist/SHA256SUMS && mkdir -p dist
@pack() { \
cmd=$$1; os=$$2; arch=$$3; ext=; [ $$os = windows ] && ext=.exe; \
name=$$cmd-$(VERSION)-$$os-$$arch; mkdir -p dist/$$name; \
echo "==> $$name"; \
GOOS=$$os GOARCH=$$arch $(GOBUILD) -o dist/$$name/portrelay-server$$ext ./cmd/portrelay-server || exit 1; \
GOOS=$$os GOARCH=$$arch $(GOBUILD) -o dist/$$name/portrelay-agent$$ext ./cmd/portrelay-agent || exit 1; \
GOOS=$$os GOARCH=$$arch $(GOBUILD) -o dist/$$name/$$cmd$$ext ./cmd/$$cmd || return 1; \
cp LICENSE README.md README_EN.md dist/$$name/; \
if [ $$os = linux ]; then cp -R deploy dist/$$name/; fi; \
if [ $$cmd = portrelay-server ]; then cp -R deploy dist/$$name/; fi; \
if [ $$os = windows ]; then (cd dist && zip -qr $$name.zip $$name); \
else tar -C dist -czf dist/$$name.tar.gz $$name; fi; \
rm -rf dist/$$name; \
done
}; \
for p in $(SERVER_PLATFORMS); do pack portrelay-server $${p%/*} $${p#*/} || exit 1; done; \
for p in $(AGENT_PLATFORMS); do pack portrelay-agent $${p%/*} $${p#*/} || exit 1; done
@cd dist && shasum -a 256 * > SHA256SUMS && cat SHA256SUMS
snapshots:
+5 -5
View File
@@ -153,9 +153,9 @@ PortRelay 把这件事变成三步:
**方式 A:二进制 + systemd(推荐)**
```bash
VERSION=1.0.0 # 见 Releases 页面
curl -LO https://github.com/DeepFal/PortRelay/releases/download/v$VERSION/portrelay-$VERSION-linux-amd64.tar.gz
tar xzf portrelay-$VERSION-linux-amd64.tar.gz && cd portrelay-$VERSION-linux-amd64
VERSION=1.1.0 # 见 Releases 页面;ARM64 机器把 amd64 换成 arm64
curl -LO https://github.com/DeepFal/PortRelay/releases/download/v$VERSION/portrelay-server-$VERSION-linux-amd64.tar.gz
tar xzf portrelay-server-$VERSION-linux-amd64.tar.gz && cd portrelay-server-$VERSION-linux-amd64
sudo ./deploy/install-server.sh ./portrelay-server
# 安装脚本会生成随机 token,记下来填到 Agent 里
@@ -185,7 +185,7 @@ make server # 产物:dist/portrelay-server-linux-{amd64,arm64}
### 2. 运行 Agent
**macOS App**:从 [Releases](https://github.com/DeepFal/PortRelay/releases) 下载 `PortRelay-*-macos-universal.zip`,解压后拖进“应用程序”。
**macOS App**:从 [Releases](https://github.com/DeepFal/PortRelay/releases) 下载 `PortRelay-*.dmg`(Apple Silicon 与 Intel 通用),打开后把 PortRelay 拖进“应用程序”。
> [!NOTE]
> App 未经 Apple 公证。首次打开时,在 Finder 中右键选择“打开”,或者执行 `xattr -dr com.apple.quarantine /Applications/PortRelay.app`。也可以 `make app` 自行构建。
@@ -200,7 +200,7 @@ make server # 产物:dist/portrelay-server-linux-{amd64,arm64}
> 举例:Agent 通过 Tailscale 地址 `100.64.0.10` 连网关,同事在办公室局域网里用 `192.168.1.10` 访问。这时“服务器控制地址”填 `100.64.0.10:33000`,“对外访问地址”填 `192.168.1.10`,邀请链接和分享文案都会使用后者。
**Linux / Windows**:下载对应平台的压缩包,直接运行 Agent,在浏览器打开的面板里完成同样的设置。
**Linux / Windows**:下载对应平台的 `portrelay-agent-*` 压缩包,直接运行 Agent,在浏览器打开的面板里完成同样的设置。
```bash
./portrelay-agent # 面板:http://127.0.0.1:33080
+5 -5
View File
@@ -158,9 +158,9 @@ The gateway must be reachable by **visitors** and by **the agent**. Open TCP `33
**Option A: binary + systemd (recommended)**
```bash
VERSION=1.0.0 # see the Releases page
curl -LO https://github.com/DeepFal/PortRelay/releases/download/v$VERSION/portrelay-$VERSION-linux-amd64.tar.gz
tar xzf portrelay-$VERSION-linux-amd64.tar.gz && cd portrelay-$VERSION-linux-amd64
VERSION=1.1.0 # see the Releases page; use arm64 instead of amd64 on ARM hosts
curl -LO https://github.com/DeepFal/PortRelay/releases/download/v$VERSION/portrelay-server-$VERSION-linux-amd64.tar.gz
tar xzf portrelay-server-$VERSION-linux-amd64.tar.gz && cd portrelay-server-$VERSION-linux-amd64
sudo ./deploy/install-server.sh ./portrelay-server
# The installer generates a random token; you will enter it in the agent
@@ -194,7 +194,7 @@ make server # dist/portrelay-server-linux-{amd64,arm64}
### 2. Run the agent
**macOS app.** Download `PortRelay-*-macos-universal.zip` from [Releases](https://github.com/DeepFal/PortRelay/releases), unzip it and move it to Applications.
**macOS app.** Download `PortRelay-*.dmg` (universal: Apple Silicon and Intel) from [Releases](https://github.com/DeepFal/PortRelay/releases), open it and drag PortRelay to Applications.
> [!NOTE]
> The app is not notarized. On first launch, right-click it in Finder and choose Open, or run `xattr -dr com.apple.quarantine /Applications/PortRelay.app`. You can also build it yourself with `make app`.
@@ -209,7 +209,7 @@ Open ⚙️ Settings and fill in:
> Example: the agent reaches the gateway over Tailscale at `100.64.0.10`, while teammates in the office use the LAN address `192.168.1.10`. Set the control address to `100.64.0.10:33000` and the public access address to `192.168.1.10`. Invite links and share messages will use the LAN address.
**Linux / Windows.** Download the archive for your platform, run the agent, and use the web panel that opens in your browser.
**Linux / Windows.** Download the `portrelay-agent-*` archive for your platform, run the agent, and use the web panel that opens in your browser.
```bash
./portrelay-agent # panel at http://127.0.0.1:33080