FRP 管理桌面客户端公版Mole部署教程

本文将带你从零开始,快速完成基于 Mole 的内网穿透服务部署,包含服务端 (frps) 与桌面客户端 (Mole/frpc) 的配置与排查要点。 📖 核心概念 什么是 FRP? FRP (Fast Reverse Proxy) 是一款高性能的反向代理/内网穿透工具,它通过在公网服务器和内网客户端之间建立隧道,使外网可以访问内网服务(如 NAS、树莓派、开发环境等)。 什么是 Mole? Mole 是一款基于 wails3 开发的 FRP 桌面客户端,提供图形化管理界面,简化 frpc 的配置与使用: 告别繁琐命令行 支持系统托盘常驻,防止误关窗口导致中断 支持 HTTP/HTTPS、TCP、UDP 等多种协议 🛠️ 部署前准备 一台拥有公网 IP 的低配云服务器(例如1核1G内存的阿里云、腾讯云等) FRP 服务端(frps)与客户端(frpc)二进制文件,建议使用 FRP Releases 的 v0.65.0 或更高版本 Mole 桌面客户端安装包(对应你操作系统的版本) 基本网络与防火墙管理权限 1. 服务端配置(frps) 在拥有公网IP的云服务器上部署frps服务端: 下载并解压 FRP 服务端(以 Linux 为例) 访问 FRP 的 Releases 页面下载对应版本并解压(示例为 v0.65.0 或更高)。 编写 frps.toml 配置文件(示例) 将以下内容写入 frps.toml,并根据实际需求调整端口与 token: bindPort = 7000 # 客户端连接端口(frpc 连接到此端口) vhostHTTPPort = 8080 # HTTP 映射的公网访问端口(可选) # 建议设置 auth.token 为复杂字符串,防止未授权连接 auth.token = "你的复杂密匙" 启动 frps(示例) 在服务端目录运行: ...

2026-01-07 · 2 min · Eagle

Wails 3 初体验及在FRP管理客户端中的应用

为什么选择了Wails 3 ? Wails 3 最大的改变在于它不再强绑定于某个特定的前端框架,且引入了多窗口支持和更轻量级的 Runtime。它允许你在不启动主窗口的情况下运行后端服务,这正是我们实现“系统托盘”和“后台演示服务”的基础。 在 v2 中,我们习惯于自动生成的 wailsjs 文件夹。但在 v3 中,这一逻辑被进一步标准化。 当你运行开发指令时,Wails 会扫描你的 Go 结构体方法,并将其映射为前端可以调用的 JavaScript 函数。这个过程在 v3 中被称为 Generate 过程。 Wails 3 通信灵魂 我们在前端调用在后端定义的 HandleConnect 返回自定义结构体,这在 Wails 3 中是前后端通信的灵魂。 在 Wails 3 开发中,最核心的动作就是:后端做功,前端表现。 当你调用 MoleService.HandleConnect() 时,Go 后端会产生一个结果。在本项目中,我们需要同时返回一个 Code(状态码)和一个 Content(数据内容)。 为了实现这一点,我们定义了一个结构体: type Response struct { Code int; Content string } 虽然 Go 内部使用的是结构体,但前端 JavaScript 只能读懂 JSON 对象。Wails 3 内部会自动帮你完成这个“翻译”过程。 但是,如果你想让前端看到的字段名是小写的(例如 res.code 而不是 res.Code),你必须在 Go 结构体定义时加上“注解”。 type Response struct { Code int `json:"code"` Content string `json:"content"` } 记住,所有通过 bindings 调用的 Go 方法,在前端返回的都是一个 Promise 对象。这意味着你必须使用 await 或者 .then() 来接收数据,否则你拿到的将是一个永不开启的“盲盒”。 ...

2025-11-01 · 3 min · Eagle

自助语音验证码介绍

版本:v1.0 创作时间:2024-08-19 21:40:00 自助语音验证码是通过 拨打 VOIP 电话自动获取验证码,验证码可用来豆子笔记网站认证。掌握了此项技术,你也可以应用于其它地方。 Asterisk 是一个流行的开源通信平台,它提供了构建各种通信应用的灵活而强大的解决方案。它被广泛应用于企业内部电话系统、呼叫中心、语音邮件等场景。 Asterisk 支持多种通信协议,包括 SIP、H.323、MGCP 和 SCCP,并且能够与传统的 PSTN 线路深度兼容。 此外,Asterisk 还支持通过 Inter-Asterisk eXchange (IAX)协议进行语音过 IP 传输,允许数据和语音同时在网络上传输。 我们使用 Asterisk 实现了自助语音验证码功能。 常用 VOIP 软件 手机端推荐:zoiper,linphone PC 端推荐:microsip,eyebeam,zoiper,linphone 自助语音验证码使用说明 你需要准备: 软电话,或者支持 VOIP 的电话机 AST 账户,AST 账户从豆子工具小程序中获取,功能菜单为获取 AST 账户。 注意:AST 账户获取后,需在次日才可以使用,因为申请账号的库和 Asterisk 不是同一个库。它会在凌晨进行自动同步。 首先,使用获取到的账户配置你的电话。 然后,拨打 8000,即可自动获取验证码。 如果您有什么问题,欢迎关注公众号【技术源泉】私信我。

2022-08-19 · 1 min · Eagle

使用 Wails v3 打造 frp 桌面客户端:mole-go 原理与跨端开发

一、 缘起:为什么需要 mole-go? 在开发微信公众号、调试支付接口、以及演示本地开发网站时,或由于服务器资源限制需要在本地部署服务时,frp 是不可或缺的内网穿透神器。然而,原生的 frpc 存在几个显著的痛点: 运行隐形性差:必须开启命令行窗口,一旦误关服务即中断。 配置门槛高:新手难以记忆复杂的 .toml 参数。 为了解决这些问题,我开发了 mole-go。它是一个轻量级、跨平台的桌面客户端,旨在实现 frp 的配置、启动与监控一体化。我选择 Wails v3 则是看中了其原生渲染、系统托盘支持、Go 强力后端以及极小的打包体积。 二、 核心架构:Go + Wails v3 的化学反应 mole-go 采用了经典的“UI-Backend-Service”三层架构: Wails UI:负责前端展示,通过事件驱动(Event-Driven)与后端交互。 Go Backend:核心大脑,负责业务逻辑、进程管理与系统级 API 调用。 frpc 二进制:底层服务,通过 Go 的 embed 特性内嵌到二进制文件中。 三、 关键实现细节:从命令行到图形化的进化 前端:从“面条代码”到模块化数据驱动 早期版本中,我直接采用 window.startFrp,window.stopFrp这样的写法,导致代码碎片化严重,以及管理app运行状态不方便。在 mole-go 的正式版中,我将其重构为数据驱动模式,类似Vue,由数据驱动界面: 模块化封装:定义全局 window.App 对象,将数据状态与行为(Methods)统一封装,使代码结构清晰。 动态 UI 组件:针对 HTTP、TCP、UDP 等不同代理模型,不再机械地堆砌 HTML 片段,而是通过逻辑判断实现“按需渲染”,大大精简了 DOM 结构。 后端:全局实例与事件机制 为了保证服务层(Service)能随时与 UI 通信,我设计了一个全局 App 实例,这样可以方便得调用和管理。 状态约定:前后端约定一套状态码,通过 Wails v3 的 Events 机制,后端可以主动向前端推送 frpc 的运行状态、日志等信息。 独立服务层:将 frp 相关逻辑抽离到专门的文件中,通过 Wails 的 Binding 暴露给前端,保持代码的解耦。 系统深度集成 系统托盘(System Tray):利用 Wails v3 原生的托盘支持,实现了“关闭即隐藏”逻辑。 外部链接调用:使用 wails3自带的 Browser.OpenURL 方法,确保点击文档链接时能正确唤起系统浏览器。 可参考项目源码。 ...

2026-01-10 · 1 min · Eagle

系列开篇:告别命令行的束缚,我为何选择 Wails v3 打造 frp 管理客户端?

一、困境与契机:低配服务器、高性能需求与共享经济的思考 我的云服务器配置很“复古”:1 核 CPU、1G 内存、1M 带宽。它完美地满足了我个人博客的需求,直到我需要演示一套视频会议系统。该系统最低要求 2 核 4G。 我的服务器是包年做活动时购买,升级配置成本高昂,而我的本地电脑性能过剩,为了一次演示,我升级我的云服务器,我感觉不值。为了解决这个问题,frp(Fast Reverse Proxy) 成为了我的救星,它允许我将本地高性能服务安全地映射到公网。 新的痛点出现:命令行的局限性 在成功通过命令行 frpc 实现了内网穿透演示后,我发现了一系列生产力问题: 进程管理难题: 命令行窗口一旦关闭,frpc 进程随之终止,服务中断。这在日常使用中非常不便,因为会经常手误关闭窗口。 潜在的商业价值: 我有闲置资源服务器,公网 IP 以及域名。对于那些临时需要公网映射的用户来说,这是一个刚需。 流量变现与服务化: 我有一个小程序并集成了广告。如果能让用户通过看广告来获取临时的 frps 使用权(子域名分配),可以减少我的服务器支出,这将是一个双赢的模式。 为了解决这些问题并实现我的想法,我意识到可以做一个稳定、可后台运行、拥有友好 UI 的客户端,来替代原始的命令行操作。 二、技术选型坎坷路:Fyne、Tauri 的尝试与碰壁 我的技术栈主要集中在 Go、Rust 和 JavaScript,其中 Go 最擅长。我希望使用这些技术栈实现一个跨平台的客户端。 第一次尝试:Go & Fyne Fyne 是一款使用 Go 语言编写的跨平台 GUI 库。我首先尝试了它。 优点: 纯 Go 编写,上手快。 痛点: 在处理 frpc 实时打印的日志时,Fyne 对中文字符的支持不理想;复杂的列表和表格布局实现起来很痛苦。最终放弃。 第二次尝试:Rust & Tauri Tauri 是当时(也是现在)非常热门的跨平台框架,结合 Rust 的后端性能和 Web 前端技术。 优点: 性能强劲,打包体积小,前端界面开发体验好。 痛点: 我完成了界面设计,但在核心的 frp 控制模块上遇到了巨大的技术难题。Rust 的所有权(Ownership) 和生命周期管理让我头疼不已,始终无法优雅地实现 frp 进程的启动、停止和状态共享。项目卡壳。 三、柳暗花明:Wails v3 成为“天选之子” 在技术选型陷入僵局时,有读者告诉我可以尝试下 wails 3,我关注了 Wails 项目的 v3 版本。虽然现在它还处于 Alpha 阶段,但它宣传的特性完美契合我的所有需求: ...

2025-12-01 · 1 min · Eagle

Go 版本文章上传命令行工具

这是 Golang 实现的上传文章以及管理文章的一个命令行工具。 项目地址 https://gitee.com/littletow/upart-go 实现一个上传文章的命令行工具 这是一个最初的版本,使用 flag 和 ini 来实现文章上传功能。使用 flag 来解析命令行参数,使用 Ini 配置文件,记录识别码,以及 token。记录 token 的原因是因为每次启动命令行,都需要重新获取 token,为了减少 token 获取次数,在获取到 token 后,同时存储到 Ini 配置文件中。每次命令行启动,优先查看配置文件中的 token。 开发这个工具主要有以下几个技术要点: 从命令行获取参数 从配置文件中读取参数 读取文件内容 请求后端接口 我们通过分析后,需要定义以下几个参数: title 标题 keyword 关键字 filename MD 文件名 ispub 是否公开 islock 是否加锁 使用 Go 代码定义参数和结构体 var ( title string keyword string filename string ispub int // 1 pub islock int // 1 lock ) func init() { flag.StringVar(&title, "b", "", "文章题目") flag.StringVar(&keyword, "k", "", "文章关键字") flag.StringVar(&filename, "f", "", "MD文件") flag.IntVar(&islock, "l", 0, "是否加锁") flag.IntVar(&ispub, "p", 0, "是否开放") } func main(){ flag.Parse() } init() 在 main 函数前调用。需要在 main 函数中调用 flag.Parse(),这一步非常关键。之后就可以使用变量了。 ...

2024-09-21 · 3 min · Eagle

如何实现自助语音验证码?

用户通过 VOIP 电话连接到 Asterisk,拨打固定的数字 8000,Asterisk 将调用 AGI 接口获取验证码,然后将获取到的验证码播报给用户,播报完毕后自动挂断。 我们根据自助语音验证码原理,将自助验证码实现分解为以下几个技术要点,只要我们解决以下技术要点,就可以实现自助语音验证码。 技术要点: 1,如何播放音频文件?音频文件从哪里来?如何播放动态数据? 2,如何存储用户信息?如何使用数据库存储用户信息? 3,如何获取验证码? 下面内容是我对各个技术点的对应解决方案。 1,Asterisk 自带 Playback 应用,可以通过它播放音频文件。音频文件需要我们提前录制好,并且转换为对应的音频格式,最简单的方法就是使用手机上的录音机。当在拨号计划中多次执行 Playback 应用,Asterisk 会将音频流自动连接起来。所以我们可以使用循环多次执行 Playback 应用即可。 2,Asterisk 使用配置文件写入 SIP 用户信息,但当写入新的 SIP 用户信息后,需要重新加载配置文件。为了方便和第三方对接,我们推荐使用数据库。Asterisk 支持数据库,并且可以实时获取用户信息。 3,Asterisk 支持 AGI 接口,我们可以使用 AGI 获取第三方应用的验证码,获取后和提前录制好的文件结合起来进行播放。 当掌握了这些技术点后,我们就可以灵活应用到其它解决方案。

2022-08-20 · 1 min · Eagle

Frp 管理客户端:基于 Wails 3 的 frp 智能管理实战

Mole 是一款基于 Wails 3 开发的跨平台 frp 管理客户端。它不仅解决了 frp 配置繁琐的痛点,更探索出了一种“桌面工具 + 小程序生态”的全新闭环模式。 🌟 核心业务流程 Mole 的核心逻辑在于其自动化配置流与激励机制的融合: 1. 智能激励连接 (One-Click Connect) 当用户点击“一键连接”时,客户端会启动一套自动验证逻辑: 广告触发:系统检查用户当前状态。若未满足条件,则弹出动态窗口显示专属小程序码。 多端协同:用户扫码后进入微信小程序观看激励视频。 自动握手:用户观看完毕后,小程序通过后端指令反馈给 Mole 客户端。 静默取消:客户端接收指令后自动关闭弹窗,进入连接阶段。 2. 云端配置自动下发 Mole 不再需要用户手动编辑复杂的 TOML 文件: 动态获取:从服务器端安全获取分配的随机子域名和 frp 配置 Token。 本地注入:Go 后端自动将配置写入内置的 frpc.toml。 二进制调用:自动调用内嵌的 frpc 核心组件启动服务。 3. 全链路事件反馈 利用 Wails 3 的高效事件机制,实现深度的 UI 反馈: 实时日志:Go 后端捕获 frpc 的标准输出,通过事件流(Events)实时推送到前端页面,用户可以清晰看到连接建立过程。 状态监控:实时反馈隧道运行状态,确保连接稳定性。 🛠️ 板块功能详情 连接控制台:一键启停,集成小程序激励流程。 端口配置页:灵活配置本地服务端口(如 8080, 3000 等),满足开发者调试需求。 实时日志页:直观展示 frp 运行日志,方便故障排查。 帮助中心:详尽的操作指南,降低内网穿透的使用门槛。 技术结缘 (About & Support): 集成优质云服务器推广(为用户提供可靠的 frp 服务端选择)。 扫码直达开发笔记,分享 Wails 3 与 Go 的底层实战。 👀 软件界面概览 1. 软件主界面 ...

2025-12-02 · 1 min · Eagle

上传工具实现上传文章命令

上传文章 定义上传文章的 command,并将 command 添加到 Root 中。 func init() { rootCmd.AddCommand(upCmd) } var upCmd = &cobra.Command{ Use: "upload", Short: "上传文章,可在豆子碎片小程序中查看。", Long: `上传文章,可在豆子碎片小程序中查看。参数依次为题目,关键字,Markdown文件,是否公开,是否加锁。 参数题目,关键字,Markdown文件必填,是否公开,是否加锁选填,默认为否。 例如:gart upload 上传示例1 命令行,工具 ./example.md `, Args: cobra.RangeArgs(3, 5), Run: func(cmd *cobra.Command, args []string) { var ( title string keyword string filename string ispub int islock int ) title = args[0] keyword = args[1] filename = args[2] l := len(args) switch l { case 4: ispub = utils.Str2Int(args[3]) case 5: ispub = utils.Str2Int(args[3]) islock = utils.Str2Int(args[4]) } fmt.Println("上传参数如下:") var isPubStr string = "否" var isLockStr string = "否" if ispub == 1 { isPubStr = "是" } if islock == 1 { isLockStr = "是" } t := table.NewWriter() t.SetOutputMirror(os.Stdout) t.AppendHeader(table.Row{"题目", "关键字", "文件名称", "是否公开", "是否加锁"}) t.AppendRows([]table.Row{ {title, keyword, filename, isPubStr, isLockStr}, }) t.AppendSeparator() t.Render() err := service.UploadArt(token, title, keyword, filename, ispub, islock) if err != nil { fmt.Println("上传发生错误,", err) } else { fmt.Println("上传成功") } }, } 在上面的实现中,使用了 cobra 参数约束函数,Args: cobra.RangeArgs(3, 5),,保证参数最少 3 个,最多不能超过 5 个。这完全满足我的需求。 ...

2024-09-23 · 1 min · Eagle

语音验证码完整实现流程

音频文件处理 我们使用手机上的录音机来录制音频文件。 Android 录音机录制的音频文件格式为 mp3,如果是 amr 格式,请使用豆子工具音频格式转换功能,转成 mp3 格式文件。 IOS 录音机录制的音频文件格式为 m4a,请使用豆子工具音频格式转换功能,转成 mp3 格式文件。 我们还需要使用 ffmpeg 将 mp3 文件转成 g711a 格式文件。这个 mp3 转 g711a 功能后续会集成到豆子工具中。 mp3 转 g711a 命令: ffmpeg -i test.mp3 -acodec pcm_alaw -f alaw -ac 1 -ar 8000 -vn test.alaw 使用 ffplay 播放测试 ffplay -i test.alaw -f alaw -ac 1 -ar 8000 将制作好的音频文件存放在 asterisk sounds 目录,就可以在拨号计划中使用 Playback 应用调用它了。 配置实时数据库 今天讲解 Asterisk 如何实时将 SIP 用户写入 sqlite3 数据库。 先定义数据库表结构,我当前使用的 PJSIP 协议。 CREATE TABLE ps_endpoints ( id VARCHAR(40) NOT NULL, transport VARCHAR(40), aors VARCHAR(200), auth VARCHAR(40), context VARCHAR(40), disallow VARCHAR(200), allow VARCHAR(200), direct_media varchar(5) check(direct_media in ('yes','no')), connected_line_method varchar(10) check(connected_line_method in ('invite','reinvite','update')), direct_media_method varchar(10) check(direct_media_method in ('invite','reinvite','update')), direct_media_glare_mitigation varchar(20) check(direct_media_glare_mitigation in ('none','outgoing','incoming')), disable_direct_media_on_nat varchar(5) check(disable_direct_media_on_nat in ('yes','no')), dtmf_mode varchar(20) check(dtmf_mode in ('rfc4733','inband','info')), external_media_address VARCHAR(40), force_rport varchar(5) check(force_rport in ('yes','no')), ice_support varchar(5) check(ice_support in ('yes','no')), identify_by varchar(10) check(identify_by in ('username')), mailboxes VARCHAR(40), moh_suggest VARCHAR(40), outbound_auth VARCHAR(40), outbound_proxy VARCHAR(40), rewrite_contact varchar(5) check(rewrite_contact in ('yes','no')), rtp_ipv6 varchar(5) check(rtp_ipv6 in ('yes','no')), rtp_symmetric varchar(5) check(rtp_symmetric in ('yes','no')), send_diversion varchar(5) check(send_diversion in ('yes','no')), send_pai varchar(5) check(send_pai in ('yes','no')), send_rpid varchar(5) check(send_rpid in ('yes','no')), timers_min_se INTEGER, timers varchar(20) check(timers in ('forced','no','required','yes')), timers_sess_expires INTEGER, callerid VARCHAR(40), callerid_privacy varchar(40) check(callerid_privacy in ('allowed_not_screened','allowed_passed_screened','allowed_failed_screened','allowed','prohib_not_screened','prohib_passed_screened','prohib_failed_screened','prohib','unavailable')), callerid_tag VARCHAR(40), `100rel` varchar(20) check(`100rel` in ('no','required','yes')), aggregate_mwi varchar(5) check(aggregate_mwi in ('yes','no')), trust_id_inbound varchar(5) check(trust_id_inbound in ('yes','no')), trust_id_outbound varchar(5) check(trust_id_outbound in ('yes','no')), use_ptime varchar(5) check(use_ptime in ('yes','no')), use_avpf varchar(5) check(use_avpf in ('yes','no')), media_encryption varchar(10) check(media_encryption in ('no','sdes','dtls')), inband_progress varchar(5) check(inband_progress in ('yes','no')), call_group VARCHAR(40), pickup_group VARCHAR(40), named_call_group VARCHAR(40), named_pickup_group VARCHAR(40), device_state_busy_at INTEGER, fax_detect varchar(5) check(fax_detect in ('yes','no')), t38_udptl varchar(5) check(t38_udptl in ('yes','no')), t38_udptl_ec varchar(20) check(t38_udptl_ec in ('none','fec','redundancy')), t38_udptl_maxdatagram INTEGER, t38_udptl_nat varchar(5) check(t38_udptl_nat in ('yes','no')), t38_udptl_ipv6 varchar(5) check(t38_udptl_ipv6 in ('yes','no')), tone_zone VARCHAR(40), language VARCHAR(40), one_touch_recording varchar(5) check(one_touch_recording in ('yes','no')), record_on_feature VARCHAR(40), record_off_feature VARCHAR(40), rtp_engine VARCHAR(40), allow_transfer varchar(5) check(allow_transfer in ('yes','no')), allow_subscribe varchar(5) check(allow_subscribe in ('yes','no')), sdp_owner VARCHAR(40), sdp_session VARCHAR(40), tos_audio INTEGER, tos_video INTEGER, cos_audio INTEGER, cos_video INTEGER, sub_min_expiry INTEGER, from_domain VARCHAR(40), from_user VARCHAR(40), mwi_fromuser VARCHAR(40), dtls_verify VARCHAR(40), dtls_rekey VARCHAR(40), dtls_cert_file VARCHAR(200), dtls_private_key VARCHAR(200), dtls_cipher VARCHAR(200), dtls_ca_file VARCHAR(200), dtls_ca_path VARCHAR(200), dtls_setup varchar(20) check(dtls_setup in ('active','passive','actpass')), srtp_tag_32 varchar(5) check(srtp_tag_32 in ('yes','no')), UNIQUE (id) ); CREATE INDEX ps_endpoints_id ON ps_endpoints (id); CREATE TABLE ps_auths ( id VARCHAR(40) NOT NULL, auth_type varchar(10) check(auth_type in ('md5','userpass')), nonce_lifetime INTEGER, md5_cred VARCHAR(40), password VARCHAR(80), realm VARCHAR(40), username VARCHAR(40), UNIQUE (id) ); CREATE INDEX ps_auths_id ON ps_auths (id); CREATE TABLE ps_aors ( id VARCHAR(40) NOT NULL, contact VARCHAR(40), default_expiration INTEGER, mailboxes VARCHAR(80), max_contacts INTEGER, minimum_expiration INTEGER, remove_existing varchar(5) check(remove_existing in ('yes','no')), qualify_frequency INTEGER, authenticate_qualify varchar(5) check(authenticate_qualify in ('yes','no')), UNIQUE (id) ); CREATE INDEX ps_aors_id ON ps_aors (id); PJSIP 测试数据: ...

2022-08-21 · 5 min · Eagle