mirror of https://gitee.com/godoos/godoos.git
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
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")
|
|
}
|
|
|