mirror of
https://github.com/qingchencloud/cftunnel.git
synced 2026-08-28 10:03:59 +08:00
feat: 新增 quick 命令 — 免域名一键穿透
cftunnel quick <端口> 无需账户/Token/域名, 自动生成 *.trycloudflare.com 随机公网地址。
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/qingchencloud/cftunnel/internal/daemon"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(quickCmd)
|
||||
}
|
||||
|
||||
var quickCmd = &cobra.Command{
|
||||
Use: "quick <端口>",
|
||||
Short: "快速启动免域名隧道(生成 *.trycloudflare.com 随机域名)",
|
||||
Long: "无需 Cloudflare 账户、API Token 或域名,一条命令生成临时公网地址。\n适合临时分享、快速调试,Ctrl+C 退出后域名自动失效。",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return daemon.StartQuick(args[0])
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// StartQuick 启动免域名模式(前台运行,Ctrl+C 退出)
|
||||
func StartQuick(port string) error {
|
||||
binPath, err := EnsureCloudflared()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if Running() {
|
||||
return fmt.Errorf("cloudflared 已在运行,请先执行 cftunnel down")
|
||||
}
|
||||
|
||||
cmd := exec.Command(binPath, "tunnel", "--url", "http://localhost:"+port)
|
||||
|
||||
// 捕获 stderr 提取随机域名
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Stdout = os.Stdout
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return fmt.Errorf("启动 cloudflared 失败: %w", err)
|
||||
}
|
||||
|
||||
// 后台读取 stderr,提取域名并转发输出
|
||||
go scanForURL(stderr)
|
||||
|
||||
// 捕获 Ctrl+C 优雅退出
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
done := make(chan error, 1)
|
||||
go func() { done <- cmd.Wait() }()
|
||||
|
||||
select {
|
||||
case <-sig:
|
||||
cmd.Process.Signal(syscall.SIGINT)
|
||||
<-done
|
||||
case err := <-done:
|
||||
if err != nil {
|
||||
return fmt.Errorf("cloudflared 异常退出: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func scanForURL(r io.Reader) {
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
// cloudflared 输出格式: ... https://xxx.trycloudflare.com ...
|
||||
if strings.Contains(line, "trycloudflare.com") {
|
||||
url := extractURL(line)
|
||||
if url != "" {
|
||||
fmt.Printf("\n✔ 隧道已启动: %s\n\n", url)
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, line)
|
||||
}
|
||||
}
|
||||
|
||||
func extractURL(line string) string {
|
||||
for _, part := range strings.Fields(line) {
|
||||
if strings.Contains(part, "trycloudflare.com") && strings.HasPrefix(part, "http") {
|
||||
return part
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Reference in New Issue
Block a user