You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

515 lines
12 KiB

package proxy
import (
"encoding/json"
"fmt"
"godo/libs"
"godo/model"
"godo/progress"
"log"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
)
// FrpConfig 结构体用于存储 FRP 配置
type FrpConfig struct {
IsOn bool `json:"isOn"`
ServerAddr string `json:"serverAddr"`
ServerPort int `json:"serverPort"`
AuthMethod string `json:"authMethod"`
AuthToken string `json:"authToken"`
User string `json:"user"`
MetaToken string `json:"metaToken"`
}
var FrpcRequest struct {
Config FrpConfig `json:"config"`
Proxies []model.FrpcProxy `json:"proxies"`
}
func InitFrpcServer() {
frpcConf, err := GetFrpcConfig()
if err != nil {
return
}
if frpcConf.IsOn {
if err := progress.StartCmd("frpc"); err != nil {
log.Printf("Failed to start frpc: %v", err)
return
}
}
}
// GenFrpcIniConfig 生成 FRP 配置文件内容
func GenFrpcIniConfig(config FrpConfig, proxys []model.FrpcProxy) string {
var builder strings.Builder
builder.WriteString(fmt.Sprintf(`serverAddr = "%s"
serverPort = %d
`,
config.ServerAddr,
config.ServerPort,
))
if config.AuthMethod == "token" {
builder.WriteString(fmt.Sprintf(`auth.token = "%s"
`,
config.AuthToken,
))
}
for _, m := range proxys {
builder.WriteString(fmt.Sprintf(`[[proxies]]
name="%s"
type = "%s"
`,
m.Name,
m.Type,
))
switch m.Type {
case "tcp", "udp":
if m.LocalIp != "" {
builder.WriteString(fmt.Sprintf(`localIP = "%s"
`,
m.LocalIp,
))
}
if m.LocalPort > 0 {
builder.WriteString(fmt.Sprintf(`localPort = %d
`,
m.LocalPort,
))
}
if m.RemotePort > 0 {
builder.WriteString(fmt.Sprintf(`remotePort = %d
`,
m.RemotePort,
))
}
if m.Type == "tcp" {
if m.StaticFile && m.LocalPath != "" {
builder.WriteString(fmt.Sprintf(`[proxies.plugin]
type = "static_file"
localPath = "%s"
`, m.LocalPath))
if m.StripPrefix != "" {
builder.WriteString(fmt.Sprintf(`stripPrefix = "%s"
`,
m.StripPrefix,
))
}
}
}
case "http", "https":
if m.LocalIp != "" {
builder.WriteString(fmt.Sprintf(`localIP = "%s"
`,
m.LocalIp,
))
}
builder.WriteString(fmt.Sprintf(`localPort = %d
`,
m.LocalPort,
))
if len(m.CustomDomains) > 0 {
domains := strings.Split(m.CustomDomains, ",")
domainArr := []string{}
for _, domain := range domains {
domainArr = append(domainArr, fmt.Sprintf(`"%s"`, domain))
}
builder.WriteString(fmt.Sprintf(`customDomains = [%s]
`,
strings.Join(domainArr, ","),
))
}
if m.Subdomain != "" {
builder.WriteString(fmt.Sprintf(`subdomain = "%s"
`,
m.Subdomain,
))
}
if m.BasicAuth {
builder.WriteString(fmt.Sprintf(`httpUser = "%s"
httpPassword = "%s"
`,
m.HttpUser,
m.HttpPassword,
))
}
if m.Type == "https" {
builder.WriteString(fmt.Sprintf(`[proxies.plugin]
type = "https2http"
localAddr = "%s:%d"
`,
m.LocalIp,
m.LocalPort,
))
if m.Https2httpCaFile != "" && m.Https2httpKeyFile != "" {
builder.WriteString(fmt.Sprintf(`crtPath = "%s"
keyPath = "%s"
`,
m.Https2httpCaFile,
m.Https2httpKeyFile,
))
}
}
case "stcp", "xtcp", "sudp":
if m.StcpModel == "visitors" {
// 访问者
builder.WriteString(fmt.Sprintf(`[[visitors]]
serverName = "%s"
bindAddr = "%s"
bindPort = %d
`,
m.ServerName,
m.BindAddr,
m.BindPort,
))
if m.FallbackTo != "" {
builder.WriteString(fmt.Sprintf(`fallbackTo = %s
fallbackTimeoutMs = %d
`,
m.FallbackTo,
m.FallbackTimeoutMs,
))
}
} else if m.StcpModel == "visited" {
// 被访问者
builder.WriteString(fmt.Sprintf(`localIP = "%s"
localPort = %d
`,
m.LocalIp,
m.LocalPort,
))
}
builder.WriteString(fmt.Sprintf(`sk="%s"
`,
m.SecretKey,
))
default:
// 默认情况不做处理
}
}
ini := builder.String()
// 去除多余的空格和换行符
ini = strings.TrimSpace(ini)
ini = strings.ReplaceAll(ini, "\n\n", "\n")
return ini
}
func CheckConfig() error {
runDir := libs.GetAppRunDir()
configPath := filepath.Join(runDir, "frpc", "config.json")
if !libs.PathExists(configPath) {
return fmt.Errorf("请先配置服务端")
}
return nil
}
func GetFrpcConfig() (FrpConfig, error) {
runDir := libs.GetAppRunDir()
configPath := filepath.Join(runDir, "frpc", "config.json")
if !libs.PathExists(configPath) {
return FrpConfig{}, fmt.Errorf("请先配置服务端")
}
configFile, err := os.Open(configPath)
if err != nil {
return FrpConfig{}, err
}
defer configFile.Close()
var config FrpConfig
decoder := json.NewDecoder(configFile)
if err := decoder.Decode(&config); err != nil {
return FrpConfig{}, err
}
return config, nil
}
func SetFrpcConfig(config FrpConfig) error {
runDir := libs.GetAppRunDir()
configPath := filepath.Join(runDir, "frpc", "config.json")
// 检查配置目录是否存在
if !libs.PathExists(filepath.Join(runDir, "frpc")) {
log.Printf("Config file not found at %s, creating new file", configPath)
}
// 没有就创建
if !libs.PathExists(filepath.Join(runDir, "frpc")) {
if err := os.MkdirAll(filepath.Join(runDir, "frpc"), 0755); err != nil {
log.Printf("Failed to create frpc directory: %v", err)
return err
}
}
// 打开或创建配置文件
configFile, err := os.Create(configPath)
if err != nil {
log.Printf("Failed to create or open config file: %v", err)
return err
}
defer configFile.Close()
// 将配置写入文件
encoder := json.NewEncoder(configFile)
encoder.SetIndent("", " ") // 设置缩进以便于阅读
if err := encoder.Encode(config); err != nil {
log.Printf("Failed to encode config to file: %v", err)
return err
}
return nil
}
func UpdateFrpcIni() error {
frpcConf, err := GetFrpcConfig()
if err != nil {
return err
}
var proxyList []model.FrpcProxy
if err := model.Db.Model(&proxyList).Find(&proxyList).Error; err != nil {
// 发生了查询错误,返回内部服务器错误
return fmt.Errorf("查询失败")
}
runDir := libs.GetAppRunDir()
filePath := filepath.Join(runDir, "frpc", "frpc.ini")
// 生成新的配置内容
iniContent := GenFrpcIniConfig(frpcConf, proxyList)
err = os.WriteFile(filePath, []byte(iniContent), 0644)
if err != nil {
return fmt.Errorf("写入失败")
}
return nil
}
// CreateFrpcHandler 创建frpc配置
func CreateFrpcHandler(w http.ResponseWriter, r *http.Request) {
var req model.FrpcProxy
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
libs.ErrorMsg(w, "解析结构体错误:"+err.Error())
return
}
err = CheckConfig()
if err != nil {
libs.ErrorMsg(w, err.Error())
return
}
var existingProxy model.FrpcProxy
err = model.Db.Model(&existingProxy).Where("name = ?", req.Name).First(&existingProxy).Error
if err == nil {
// 记录存在,返回错误
libs.ErrorMsg(w, "已存在相同名称的配置")
return
}
if err := model.Db.Create(&req).Error; err != nil {
// 发生了插入错误,返回内部服务器错误
libs.ErrorMsg(w, "数据库插入错误")
return
}
if err := UpdateFrpcIni(); err != nil {
libs.ErrorMsg(w, "更新配置错误:"+err.Error())
return
}
if err := RestartFrpc(); err != nil {
log.Printf("Failed to restarted frpc service: %v", err)
}
libs.SuccessMsg(w, nil, "配置已创建")
}
// GetFrpcConfigHandler 处理获取单个 FRPC 配置的 HTTP 请求
func GetFrpcHandler(w http.ResponseWriter, r *http.Request) {
// 获取请求参数中的 id
idStr := r.URL.Query().Get("id")
if idStr == "" {
http.Error(w, "Name parameter is required", http.StatusBadRequest)
return
}
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "Invalid ID", http.StatusBadRequest)
return
}
var proxy model.FrpcProxy
if err := model.Db.First(&proxy, uint(id)).Error; err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
libs.SuccessMsg(w, proxy, "")
}
// GetFrpcProxiesHandler 获取 FRPC 代理列表
func GetFrpcListHandler(w http.ResponseWriter, r *http.Request) {
// 获取查询参数 page 和 limit
pageStr := r.URL.Query().Get("page")
limitStr := r.URL.Query().Get("limit")
page, err := strconv.Atoi(pageStr)
if err != nil || page < 1 {
page = 1
}
limit, err := strconv.Atoi(limitStr)
if err != nil || limit < 1 {
limit = 10
}
// 定义响应结构体
type ProxyResponse struct {
List []model.FrpcProxy `json:"list"`
Total int64 `json:"total"`
}
// 修改处理函数
proxies, total, err := model.GetFrpcList(page, limit)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
response := ProxyResponse{
List: proxies,
Total: total,
}
libs.SuccessMsg(w, response, "")
}
func DeleteFrpcHandler(w http.ResponseWriter, r *http.Request) {
idStr := r.URL.Query().Get("id")
if idStr == "" {
http.Error(w, "Name parameter is required", http.StatusBadRequest)
return
}
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "Invalid ID", http.StatusBadRequest)
return
}
if err := model.Db.Delete(&model.FrpcProxy{}, uint(id)).Error; err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := UpdateFrpcIni(); err != nil {
libs.ErrorMsg(w, err.Error())
return
}
if err := RestartFrpc(); err != nil {
log.Printf("Failed to restarted frpc service: %v", err)
}
libs.SuccessMsg(w, nil, "配置已删除")
}
func UpdateFrpcHandler(w http.ResponseWriter, r *http.Request) {
// 解析请求体中的更新数据
var updateData model.FrpcProxy
err := json.NewDecoder(r.Body).Decode(&updateData)
if err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if err := model.Db.Model(&model.FrpcProxy{}).Where("id = ?", updateData.ID).Updates(updateData).Error; err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := UpdateFrpcIni(); err != nil {
libs.ErrorMsg(w, err.Error())
return
}
if err := RestartFrpc(); err != nil {
log.Printf("Failed to restarted frpc service: %v", err)
}
libs.SuccessMsg(w, nil, "配置已更新")
}
func GetFrpcConfigHandler(w http.ResponseWriter, r *http.Request) {
frpcConf, err := GetFrpcConfig()
if err != nil {
libs.ErrorMsg(w, err.Error())
return
}
libs.SuccessMsg(w, frpcConf, "配置已更新")
}
func UpdateFrpcConfigHandler(w http.ResponseWriter, r *http.Request) {
// 解析请求体中的更新数据
var updateData FrpConfig
err := json.NewDecoder(r.Body).Decode(&updateData)
if err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if err := SetFrpcConfig(updateData); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := UpdateFrpcIni(); err != nil {
libs.ErrorMsg(w, err.Error())
return
}
if err := RestartFrpc(); err != nil {
log.Printf("Failed to restarted frpc service: %v", err)
}
libs.SuccessMsg(w, nil, "配置已更新")
}
func RestartFrpc() error {
status := progress.GetCmd("frpc").Running
if status {
return progress.RestartCmd("frpc")
}
return nil
}
func StatusFrpcHandler(w http.ResponseWriter, r *http.Request) {
libs.SuccessMsg(w, progress.GetCmd("frpc").Running, "")
}
func StartFrpcHandler(w http.ResponseWriter, r *http.Request) {
err := CheckConfig()
if err != nil {
libs.ErrorMsg(w, err.Error())
return
}
if err := progress.StartCmd("frpc"); err != nil {
libs.ErrorMsg(w, err.Error())
return
}
libs.SuccessMsg(w, nil, "frpc service started")
}
func StopFrpcHandler(w http.ResponseWriter, r *http.Request) {
err := CheckConfig()
if err != nil {
libs.ErrorMsg(w, err.Error())
return
}
if err := progress.StopCmd("frpc"); err != nil {
libs.ErrorMsg(w, err.Error())
return
}
libs.SuccessMsg(w, nil, "frpc service stoped")
}
func RestartFrpcHandler(w http.ResponseWriter, r *http.Request) {
err := CheckConfig()
if err != nil {
libs.ErrorMsg(w, err.Error())
return
}
if err := progress.RestartCmd("frpc"); err != nil {
libs.ErrorMsg(w, err.Error())
return
}
libs.SuccessMsg(w, nil, "frpc service restarted")
}