refactor(frpc): restructure instance management and config handling
- Move instance-related structs and functions to config.go - Remove serverAddr, serverPort, and authMethod from database schema - Implement new config parsing and encoding with nested key support - Update service management to use instanceID instead of username/name - Add GetServiceNameByInstanceID helper function - Update API documentation for auth.method field change
This commit is contained in:
@@ -66,6 +66,7 @@ For detailed API documentation, please see [docs/api.md](docs/api.md)
|
|||||||
- [ ] Add frpc instance running status management API
|
- [ ] Add frpc instance running status management API
|
||||||
- [ ] Add frpc instance log display API
|
- [ ] Add frpc instance log display API
|
||||||
- [ ] Fix random database lock when processing logs
|
- [ ] Fix random database lock when processing logs
|
||||||
|
- [ ] Add frpc createdBy storage and display
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
276
config.go
276
config.go
@@ -5,6 +5,10 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/toml"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -15,6 +19,40 @@ type Config struct {
|
|||||||
Debug bool `json:"debug"`
|
Debug bool `json:"debug"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InstanceInfo struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ServerAddr string `json:"serverAddr"`
|
||||||
|
ServerPort string `json:"serverPort"`
|
||||||
|
AuthMethod string `json:"auth_method"`
|
||||||
|
RunUser string `json:"runUser"`
|
||||||
|
Additional map[string]interface{} `json:"additionalProperties"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FrpcProxyInfo struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
LocalIP string `json:"local_ip"`
|
||||||
|
LocalPort string `json:"local_port"`
|
||||||
|
RemotePort string `json:"remote_port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateInstanceRequest struct {
|
||||||
|
InstanceInfo InstanceInfo `json:"instanceInfo"`
|
||||||
|
BootAtStart bool `json:"bootAtStart"`
|
||||||
|
RunUser string `json:"runUser"`
|
||||||
|
Additional map[string]interface{} `json:"additionalProperties"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateProxyRequest struct {
|
||||||
|
InstanceID string `json:"instanceID"`
|
||||||
|
ProxyInfo FrpcProxyInfo `json:"proxyInfo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FrpcConfig struct {
|
||||||
|
Global map[string]interface{} `toml:"-"`
|
||||||
|
Proxies []map[string]interface{} `toml:"proxies"`
|
||||||
|
}
|
||||||
|
|
||||||
var globalConfig *Config
|
var globalConfig *Config
|
||||||
|
|
||||||
func LoadConfig(configPath string) (*Config, error) {
|
func LoadConfig(configPath string) (*Config, error) {
|
||||||
@@ -72,3 +110,241 @@ func SaveConfig(configPath string, config *Config) error {
|
|||||||
globalConfig = config
|
globalConfig = config
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodeFrpcConfig(configContent string) (FrpcConfig, error) {
|
||||||
|
var config FrpcConfig
|
||||||
|
|
||||||
|
var rawMap map[string]interface{}
|
||||||
|
meta, err := toml.Decode(configContent, &rawMap)
|
||||||
|
if err != nil {
|
||||||
|
return config, fmt.Errorf("failed to parse config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Global = make(map[string]interface{})
|
||||||
|
config.Proxies = make([]map[string]interface{}, 0)
|
||||||
|
|
||||||
|
getNestedValue := func(m map[string]interface{}, key string) (interface{}, bool) {
|
||||||
|
parts := strings.Split(key, ".")
|
||||||
|
current := m
|
||||||
|
for i, part := range parts {
|
||||||
|
if i == len(parts)-1 {
|
||||||
|
val, ok := current[part]
|
||||||
|
return val, ok
|
||||||
|
}
|
||||||
|
next, ok := current[part].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
current = next
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range meta.Keys() {
|
||||||
|
keyStr := key.String()
|
||||||
|
if keyStr == "proxies" {
|
||||||
|
if proxies, ok := rawMap["proxies"].([]map[string]interface{}); ok {
|
||||||
|
config.Proxies = proxies
|
||||||
|
} else if proxies, ok := rawMap["proxies"].([]interface{}); ok {
|
||||||
|
for _, p := range proxies {
|
||||||
|
if pm, ok := p.(map[string]interface{}); ok {
|
||||||
|
config.Proxies = append(config.Proxies, pm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if !strings.HasPrefix(keyStr, "proxies.") {
|
||||||
|
if val, ok := getNestedValue(rawMap, keyStr); ok {
|
||||||
|
config.Global[keyStr] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeFrpcConfig(config FrpcConfig) (string, error) {
|
||||||
|
var buf strings.Builder
|
||||||
|
|
||||||
|
if len(config.Global) > 0 {
|
||||||
|
for key, value := range config.Global {
|
||||||
|
switch v := value.(type) {
|
||||||
|
case string:
|
||||||
|
buf.WriteString(fmt.Sprintf("%s = %q\n", key, v))
|
||||||
|
case int:
|
||||||
|
buf.WriteString(fmt.Sprintf("%s = %d\n", key, v))
|
||||||
|
case int64:
|
||||||
|
buf.WriteString(fmt.Sprintf("%s = %d\n", key, v))
|
||||||
|
case float64:
|
||||||
|
buf.WriteString(fmt.Sprintf("%s = %v\n", key, v))
|
||||||
|
case bool:
|
||||||
|
buf.WriteString(fmt.Sprintf("%s = %v\n", key, v))
|
||||||
|
default:
|
||||||
|
data, err := json.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to marshal value for key %s: %w", key, err)
|
||||||
|
}
|
||||||
|
buf.WriteString(fmt.Sprintf("%s = %s\n", key, string(data)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(config.Proxies) > 0 {
|
||||||
|
for _, proxy := range config.Proxies {
|
||||||
|
buf.WriteString("[[proxies]]\n")
|
||||||
|
for key, value := range proxy {
|
||||||
|
switch v := value.(type) {
|
||||||
|
case string:
|
||||||
|
buf.WriteString(fmt.Sprintf("%s = %q\n", key, v))
|
||||||
|
case int:
|
||||||
|
buf.WriteString(fmt.Sprintf("%s = %d\n", key, v))
|
||||||
|
case int64:
|
||||||
|
buf.WriteString(fmt.Sprintf("%s = %d\n", key, v))
|
||||||
|
case float64:
|
||||||
|
buf.WriteString(fmt.Sprintf("%s = %v\n", key, v))
|
||||||
|
case bool:
|
||||||
|
buf.WriteString(fmt.Sprintf("%s = %v\n", key, v))
|
||||||
|
default:
|
||||||
|
data, err := json.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to marshal value for key %s: %w", key, err)
|
||||||
|
}
|
||||||
|
buf.WriteString(fmt.Sprintf("%s = %s\n", key, string(data)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteString("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSuffix(buf.String(), "\n"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateFrpcConfig(info InstanceInfo) string {
|
||||||
|
config := FrpcConfig{
|
||||||
|
Global: make(map[string]interface{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Global["serverAddr"] = info.ServerAddr
|
||||||
|
config.Global["serverPort"] = info.ServerPort
|
||||||
|
config.Global["auth.method"] = info.AuthMethod
|
||||||
|
|
||||||
|
for key, value := range info.Additional {
|
||||||
|
config.Global[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := encodeFrpcConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFrpcProxy(configContent string, info FrpcProxyInfo) (string, error) {
|
||||||
|
config, err := decodeFrpcConfig(configContent)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to parse config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy := map[string]interface{}{
|
||||||
|
"name": info.Name,
|
||||||
|
"type": info.Type,
|
||||||
|
"localIP": info.LocalIP,
|
||||||
|
"localPort": info.LocalPort,
|
||||||
|
"remotePort": info.RemotePort,
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Proxies = append(config.Proxies, proxy)
|
||||||
|
|
||||||
|
result, err := encodeFrpcConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to write config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeFrpcProxy(configContent string, proxyName string) (string, error) {
|
||||||
|
config, err := decodeFrpcConfig(configContent)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to parse config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var found bool
|
||||||
|
var newProxies []map[string]interface{}
|
||||||
|
for _, proxy := range config.Proxies {
|
||||||
|
if name, ok := proxy["name"].(string); ok && name == proxyName {
|
||||||
|
found = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newProxies = append(newProxies, proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return "", fmt.Errorf("proxy %s not found", proxyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Proxies = newProxies
|
||||||
|
|
||||||
|
result, err := encodeFrpcConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to write config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getKeyText(configPath, key, section string) (value string, err error) {
|
||||||
|
configContent, err := os.ReadFile(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to read config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := decodeFrpcConfig(string(configContent))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to parse config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Global == nil {
|
||||||
|
return "", fmt.Errorf("config file has no global fields")
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := config.Global[key]; ok {
|
||||||
|
value = fmt.Sprintf("%v", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setKeyText(configPath, key, section string, value string) error {
|
||||||
|
configContent, err := os.ReadFile(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := decodeFrpcConfig(string(configContent))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Global == nil {
|
||||||
|
config.Global = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
if intVal, err := strconv.Atoi(value); err == nil {
|
||||||
|
config.Global[key] = intVal
|
||||||
|
} else {
|
||||||
|
config.Global[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := encodeFrpcConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to write config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(configPath, []byte(result), 0644); err != nil {
|
||||||
|
return fmt.Errorf("failed to write config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
43
database.go
43
database.go
@@ -31,9 +31,6 @@ type FrpcInstance struct {
|
|||||||
ID int
|
ID int
|
||||||
UserID int
|
UserID int
|
||||||
Name string
|
Name string
|
||||||
ServerAddr string
|
|
||||||
ServerPort string
|
|
||||||
AuthMethod string
|
|
||||||
BootAtStart bool
|
BootAtStart bool
|
||||||
RunUser string
|
RunUser string
|
||||||
ConfigPath string
|
ConfigPath string
|
||||||
@@ -275,9 +272,6 @@ func InitFrpcDatabase(dbPath string) error {
|
|||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
userID INTEGER NOT NULL,
|
userID INTEGER NOT NULL,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
serverAddr TEXT NOT NULL,
|
|
||||||
serverPort TEXT NOT NULL,
|
|
||||||
auth_method TEXT NOT NULL,
|
|
||||||
bootAtStart INTEGER NOT NULL DEFAULT 0,
|
bootAtStart INTEGER NOT NULL DEFAULT 0,
|
||||||
runUser TEXT NOT NULL DEFAULT 'root',
|
runUser TEXT NOT NULL DEFAULT 'root',
|
||||||
configPath TEXT NOT NULL,
|
configPath TEXT NOT NULL,
|
||||||
@@ -353,8 +347,8 @@ func DBQuerySpecificUser(userID int) (User, error) { // Query user by ID
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DBAddFrpcInstance(instance FrpcInstance) error {
|
func DBAddFrpcInstance(instance FrpcInstance) error {
|
||||||
_, err := frpcDB.Exec("INSERT INTO frpcInstances (userID, name, serverAddr, serverPort, auth_method, bootAtStart, runUser, configPath, createdAt) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
_, err := frpcDB.Exec("INSERT INTO frpcInstances (userID, name, bootAtStart, runUser, configPath, createdAt) VALUES (?, ?, ?, ?, ?, ?)",
|
||||||
instance.UserID, instance.Name, instance.ServerAddr, instance.ServerPort, instance.AuthMethod, instance.BootAtStart, instance.RunUser, instance.ConfigPath, time.Now().Format(time.RFC3339))
|
instance.UserID, instance.Name, instance.BootAtStart, instance.RunUser, instance.ConfigPath, time.Now().Format(time.RFC3339))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to insert frpc instance: %w", err)
|
return fmt.Errorf("failed to insert frpc instance: %w", err)
|
||||||
}
|
}
|
||||||
@@ -364,8 +358,8 @@ func DBAddFrpcInstance(instance FrpcInstance) error {
|
|||||||
func DBQueryFrpcInstanceByID(instanceID int) (FrpcInstance, error) {
|
func DBQueryFrpcInstanceByID(instanceID int) (FrpcInstance, error) {
|
||||||
var instance FrpcInstance
|
var instance FrpcInstance
|
||||||
var createdAtStr string
|
var createdAtStr string
|
||||||
err := frpcDB.QueryRow("SELECT id, userID, name, serverAddr, serverPort, auth_method, bootAtStart, runUser, configPath, createdAt FROM frpcInstances WHERE id = ?", instanceID).Scan(
|
err := frpcDB.QueryRow("SELECT id, userID, name, bootAtStart, runUser, configPath, createdAt FROM frpcInstances WHERE id = ?", instanceID).Scan(
|
||||||
&instance.ID, &instance.UserID, &instance.Name, &instance.ServerAddr, &instance.ServerPort, &instance.AuthMethod, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr)
|
&instance.ID, &instance.UserID, &instance.Name, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return instance, fmt.Errorf("failed to query frpc instance: %w", err)
|
return instance, fmt.Errorf("failed to query frpc instance: %w", err)
|
||||||
}
|
}
|
||||||
@@ -376,8 +370,8 @@ func DBQueryFrpcInstanceByID(instanceID int) (FrpcInstance, error) {
|
|||||||
func DBQueryFrpcInstance(userID int, instanceName string) (FrpcInstance, error) {
|
func DBQueryFrpcInstance(userID int, instanceName string) (FrpcInstance, error) {
|
||||||
var instance FrpcInstance
|
var instance FrpcInstance
|
||||||
var createdAtStr string
|
var createdAtStr string
|
||||||
err := frpcDB.QueryRow("SELECT id, userID, name, serverAddr, serverPort, auth_method, bootAtStart, runUser, configPath, createdAt FROM frpcInstances WHERE userID = ? AND name = ?", userID, instanceName).Scan(
|
err := frpcDB.QueryRow("SELECT id, userID, name, bootAtStart, runUser, configPath, createdAt FROM frpcInstances WHERE userID = ? AND name = ?", userID, instanceName).Scan(
|
||||||
&instance.ID, &instance.UserID, &instance.Name, &instance.ServerAddr, &instance.ServerPort, &instance.AuthMethod, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr)
|
&instance.ID, &instance.UserID, &instance.Name, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return instance, fmt.Errorf("failed to query frpc instance: %w", err)
|
return instance, fmt.Errorf("failed to query frpc instance: %w", err)
|
||||||
}
|
}
|
||||||
@@ -394,8 +388,8 @@ func DBRemoveFrpcInstanceByID(instanceID int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DBUpdateFrpcInstance(instance FrpcInstance) error {
|
func DBUpdateFrpcInstance(instance FrpcInstance) error {
|
||||||
_, err := frpcDB.Exec("UPDATE frpcInstances SET name = ?, serverAddr = ?, serverPort = ?, auth_method = ?, bootAtStart = ?, runUser = ?, configPath = ? WHERE id = ?",
|
_, err := frpcDB.Exec("UPDATE frpcInstances SET bootAtStart = ?, runUser = ?, configPath = ? WHERE id = ?",
|
||||||
instance.Name, instance.ServerAddr, instance.ServerPort, instance.AuthMethod, instance.BootAtStart, instance.RunUser, instance.ConfigPath, instance.ID)
|
instance.BootAtStart, instance.RunUser, instance.ConfigPath, instance.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to update frpc instance: %w", err)
|
return fmt.Errorf("failed to update frpc instance: %w", err)
|
||||||
}
|
}
|
||||||
@@ -404,8 +398,7 @@ func DBUpdateFrpcInstance(instance FrpcInstance) error {
|
|||||||
|
|
||||||
func DBListFrpcInstances() ([]FrpcInstance, error) {
|
func DBListFrpcInstances() ([]FrpcInstance, error) {
|
||||||
rows, err := frpcDB.Query(`
|
rows, err := frpcDB.Query(`
|
||||||
SELECT fi.id, fi.userID, fi.name, fi.serverAddr, fi.serverPort, fi.auth_method,
|
SELECT fi.id, fi.userID, fi.name, fi.bootAtStart, fi.runUser, fi.configPath, fi.createdAt, u.username
|
||||||
fi.bootAtStart, fi.runUser, fi.configPath, fi.createdAt, u.username
|
|
||||||
FROM frpcInstances fi
|
FROM frpcInstances fi
|
||||||
JOIN userLogin u ON fi.userID = u.userID
|
JOIN userLogin u ON fi.userID = u.userID
|
||||||
`)
|
`)
|
||||||
@@ -418,7 +411,7 @@ func DBListFrpcInstances() ([]FrpcInstance, error) {
|
|||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var instance FrpcInstance
|
var instance FrpcInstance
|
||||||
var createdAtStr string
|
var createdAtStr string
|
||||||
if err := rows.Scan(&instance.ID, &instance.UserID, &instance.Name, &instance.ServerAddr, &instance.ServerPort, &instance.AuthMethod, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr, &instance.CreatedBy); err != nil {
|
if err := rows.Scan(&instance.ID, &instance.UserID, &instance.Name, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr, &instance.CreatedBy); err != nil {
|
||||||
return nil, fmt.Errorf("failed to scan frpc instance: %w", err)
|
return nil, fmt.Errorf("failed to scan frpc instance: %w", err)
|
||||||
}
|
}
|
||||||
instance.CreatedAt, _ = time.Parse(time.RFC3339, createdAtStr)
|
instance.CreatedAt, _ = time.Parse(time.RFC3339, createdAtStr)
|
||||||
@@ -431,3 +424,19 @@ func DBListFrpcInstances() ([]FrpcInstance, error) {
|
|||||||
|
|
||||||
return instances, nil
|
return instances, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetServiceNameByInstanceID(instanceID int) (string, error) {
|
||||||
|
instance, err := DBQueryFrpcInstanceByID(instanceID)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to query frpc instance: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := GetUserByID(instance.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get user info: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceName := fmt.Sprintf("superfrpc_%s_%s", user.Username, instance.Name)
|
||||||
|
postLog.Debug(fmt.Sprintf("[GetServiceNameByInstanceID] instanceID: %d, serviceName: %s", instanceID, serviceName))
|
||||||
|
return serviceName, nil
|
||||||
|
}
|
||||||
|
|||||||
10
docs/api.md
10
docs/api.md
@@ -552,8 +552,8 @@ Modify fields in the `[common]` section of the frpc configuration file. Only `[c
|
|||||||
"modifiedData": {
|
"modifiedData": {
|
||||||
"server_addr": "192.168.1.1",
|
"server_addr": "192.168.1.1",
|
||||||
"server_port": "7000",
|
"server_port": "7000",
|
||||||
"auth_method": "token",
|
"auth.method": "token",
|
||||||
"auth_token": "my_secret_token"
|
"auth.token": "my_secret_token"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -1149,7 +1149,7 @@ X-Timestamp: 1704067200000
|
|||||||
"name": "my_frpc",
|
"name": "my_frpc",
|
||||||
"serverAddr": "127.0.0.1",
|
"serverAddr": "127.0.0.1",
|
||||||
"serverPort": "7000",
|
"serverPort": "7000",
|
||||||
"auth_method": "token",
|
"auth.method": "token",
|
||||||
"bootAtStart": true,
|
"bootAtStart": true,
|
||||||
"runUser": "root",
|
"runUser": "root",
|
||||||
"configPath": "./configs/superfrpc_user_my_frpc.toml",
|
"configPath": "./configs/superfrpc_user_my_frpc.toml",
|
||||||
@@ -1185,14 +1185,14 @@ X-Timestamp: 1704067200000
|
|||||||
| name | string | Instance name |
|
| name | string | Instance name |
|
||||||
| serverAddr | string | frps server address (admin/superuser only) |
|
| serverAddr | string | frps server address (admin/superuser only) |
|
||||||
| serverPort | string | frps server port (admin/superuser only) |
|
| serverPort | string | frps server port (admin/superuser only) |
|
||||||
| auth_method | string | Authentication method (admin/superuser only) |
|
| auth.method | string | Authentication method (admin/superuser only) |
|
||||||
| bootAtStart | bool | Auto-start on system boot |
|
| bootAtStart | bool | Auto-start on system boot |
|
||||||
| runUser | string | User to run the frpc instance as |
|
| runUser | string | User to run the frpc instance as |
|
||||||
| configPath | string | Path to the configuration file |
|
| configPath | string | Path to the configuration file |
|
||||||
| createdAt | string | Instance creation time (ISO 8601 format) |
|
| createdAt | string | Instance creation time (ISO 8601 format) |
|
||||||
| createdBy | string | Username of the user who created this instance |
|
| createdBy | string | Username of the user who created this instance |
|
||||||
|
|
||||||
> Note: Visitor users do not see sensitive information (serverAddr, serverPort, auth_method).
|
> Note: Visitor users do not see sensitive information (serverAddr, serverPort, auth.method).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
246
frpAct.go
246
frpAct.go
@@ -9,43 +9,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"super-frpc/postLog"
|
"super-frpc/postLog"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type InstanceInfo struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
ServerAddr string `json:"serverAddr"`
|
|
||||||
ServerPort string `json:"serverPort"`
|
|
||||||
AuthMethod string `json:"auth_method"`
|
|
||||||
RunUser string `json:"runUser"`
|
|
||||||
Additional map[string]interface{} `json:"additionalProperties"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type FrpcProxyInfo struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
LocalIP string `json:"local_ip"`
|
|
||||||
LocalPort string `json:"local_port"`
|
|
||||||
RemotePort string `json:"remote_port"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CreateInstanceRequest struct {
|
|
||||||
InstanceInfo InstanceInfo `json:"instanceInfo"`
|
|
||||||
BootAtStart bool `json:"bootAtStart"`
|
|
||||||
RunUser string `json:"runUser"`
|
|
||||||
Additional map[string]interface{} `json:"additionalProperties"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type FrpcConfig struct {
|
|
||||||
Common map[string]interface{} `toml:"common"`
|
|
||||||
Proxies []map[string]interface{} `toml:"proxies"`
|
|
||||||
Additional map[string]interface{} `toml:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var frpcDB *sql.DB
|
var frpcDB *sql.DB
|
||||||
|
|
||||||
func CloseFrpcDatabase() error {
|
func CloseFrpcDatabase() error {
|
||||||
@@ -165,9 +132,6 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
instance := FrpcInstance{
|
instance := FrpcInstance{
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
Name: req.InstanceInfo.Name,
|
Name: req.InstanceInfo.Name,
|
||||||
ServerAddr: req.InstanceInfo.ServerAddr,
|
|
||||||
ServerPort: req.InstanceInfo.ServerPort,
|
|
||||||
AuthMethod: req.InstanceInfo.AuthMethod,
|
|
||||||
BootAtStart: req.BootAtStart,
|
BootAtStart: req.BootAtStart,
|
||||||
RunUser: runUser,
|
RunUser: runUser,
|
||||||
ConfigPath: configPath,
|
ConfigPath: configPath,
|
||||||
@@ -180,16 +144,24 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := createBootService(user.Username, req.InstanceInfo.Name, configPath, runUser); err != nil {
|
createdInstance, err := DBQueryFrpcInstance(userID, req.InstanceInfo.Name)
|
||||||
frpcDB.Exec("DELETE FROM frpcInstances WHERE userID = ? AND name = ?", userID, req.InstanceInfo.Name)
|
if err != nil {
|
||||||
os.Remove(configPath)
|
os.Remove(configPath)
|
||||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to create boot service for instance %s: %v", req.InstanceInfo.Name, err))
|
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to query created instance: %v", err))
|
||||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to create boot service")
|
SendErrorResponse(w, http.StatusInternalServerError, "Failed to query created instance")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := createBootService(createdInstance.ID); err != nil {
|
||||||
|
frpcDB.Exec("DELETE FROM frpcInstances WHERE userID = ? AND name = ?", userID, req.InstanceInfo.Name)
|
||||||
|
os.Remove(configPath)
|
||||||
|
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to create boot service for instance %s: %v", req.InstanceInfo.Name, err))
|
||||||
|
SendErrorResponse(w, http.StatusInternalServerError, "Failed to create boot service")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if req.BootAtStart {
|
if req.BootAtStart {
|
||||||
if err := setBootAtStart(user.Username, req.InstanceInfo.Name); err != nil {
|
if err := setBootAtStart(createdInstance.ID); err != nil {
|
||||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to set boot at start for instance %s: %v", req.InstanceInfo.Name, err))
|
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to set boot at start for instance %s: %v", req.InstanceInfo.Name, err))
|
||||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to set boot at start")
|
SendErrorResponse(w, http.StatusInternalServerError, "Failed to set boot at start")
|
||||||
return
|
return
|
||||||
@@ -250,15 +222,7 @@ func DeleteInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := GetUserByID(userID)
|
instance, err := DBQueryFrpcInstanceByID(instanceID)
|
||||||
if err != nil {
|
|
||||||
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to get user info: %v", err))
|
|
||||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var instance FrpcInstance
|
|
||||||
instance, err = DBQueryFrpcInstanceByID(instanceID)
|
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
SendErrorResponse(w, http.StatusNotFound, "Instance not found")
|
SendErrorResponse(w, http.StatusNotFound, "Instance not found")
|
||||||
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] User %d tried to delete a not existed instance: %d", userID, instanceID))
|
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] User %d tried to delete a not existed instance: %d", userID, instanceID))
|
||||||
@@ -270,7 +234,7 @@ func DeleteInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := removeBootService(user.Username, instance.Name); err != nil {
|
if err := removeBootService(instanceID); err != nil {
|
||||||
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to remove boot service for instance %s: %v", instance.Name, err))
|
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to remove boot service for instance %s: %v", instance.Name, err))
|
||||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to remove boot service")
|
SendErrorResponse(w, http.StatusInternalServerError, "Failed to remove boot service")
|
||||||
return
|
return
|
||||||
@@ -395,40 +359,40 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
func handleConfigFileModify(w http.ResponseWriter, instance FrpcInstance, modifiedData map[string]interface{}, username string) {
|
func handleConfigFileModify(w http.ResponseWriter, instance FrpcInstance, modifiedData map[string]interface{}, username string) {
|
||||||
configPath := instance.ConfigPath
|
configPath := instance.ConfigPath
|
||||||
|
|
||||||
configContent, err := os.ReadFile(configPath)
|
for key, value := range modifiedData {
|
||||||
if err != nil {
|
configKey := key
|
||||||
postLog.Error(fmt.Sprintf("[handleConfigFileModify] Failed to read config file %s: %v", configPath, err))
|
if key == "auth_method" {
|
||||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to read config file")
|
configKey = "auth.method"
|
||||||
return
|
}
|
||||||
}
|
if key == "auth_token" {
|
||||||
|
configKey = "auth.token"
|
||||||
|
}
|
||||||
|
|
||||||
updatedConfig, err := updateCommonSection(string(configContent), modifiedData)
|
var configValue string
|
||||||
if err != nil {
|
if key == "serverPort" {
|
||||||
postLog.Error(fmt.Sprintf("[handleConfigFileModify] Failed to update common section: %v", err))
|
switch v := value.(type) {
|
||||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to update config file")
|
case float64:
|
||||||
return
|
configValue = strconv.Itoa(int(v))
|
||||||
}
|
case int:
|
||||||
|
configValue = strconv.Itoa(v)
|
||||||
if err := os.WriteFile(configPath, []byte(updatedConfig), 0644); err != nil {
|
case string:
|
||||||
postLog.Error(fmt.Sprintf("[handleConfigFileModify] Failed to write config file %s: %v", configPath, err))
|
var intVal int
|
||||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to write config file")
|
if _, err := fmt.Sscanf(v, "%d", &intVal); err == nil {
|
||||||
return
|
configValue = strconv.Itoa(intVal)
|
||||||
}
|
} else {
|
||||||
|
configValue = v
|
||||||
if v, ok := modifiedData["server_addr"].(string); ok && v != "" {
|
}
|
||||||
instance.ServerAddr = v
|
default:
|
||||||
}
|
configValue = fmt.Sprintf("%v", value)
|
||||||
if v, ok := modifiedData["server_port"].(string); ok && v != "" {
|
}
|
||||||
instance.ServerPort = v
|
} else {
|
||||||
}
|
configValue = fmt.Sprintf("%v", value)
|
||||||
if v, ok := modifiedData["auth_method"].(string); ok && v != "" {
|
}
|
||||||
instance.AuthMethod = v
|
if err := setKeyText(configPath, configKey, "", configValue); err != nil {
|
||||||
}
|
postLog.Error(fmt.Sprintf("[handleConfigFileModify] Failed to set key %s: %v", key, err))
|
||||||
|
SendErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Failed to set key %s", key))
|
||||||
if err := DBUpdateFrpcInstance(instance); err != nil {
|
return
|
||||||
postLog.Error(fmt.Sprintf("[handleConfigFileModify] Failed to update instance in database: %v", err))
|
}
|
||||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to update instance in database")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SendSuccessResponse(w, "Config file modified successfully", map[string]interface{}{
|
SendSuccessResponse(w, "Config file modified successfully", map[string]interface{}{
|
||||||
@@ -439,24 +403,6 @@ func handleConfigFileModify(w http.ResponseWriter, instance FrpcInstance, modifi
|
|||||||
postLog.Info(fmt.Sprintf("[handleConfigFileModify] Config file for instance %s modified successfully", instance.Name))
|
postLog.Info(fmt.Sprintf("[handleConfigFileModify] Config file for instance %s modified successfully", instance.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateCommonSection(configContent string, modifiedData map[string]interface{}) (string, error) {
|
|
||||||
var config FrpcConfig
|
|
||||||
if _, err := toml.Decode(configContent, &config); err != nil {
|
|
||||||
return "", fmt.Errorf("failed to parse config: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, value := range modifiedData {
|
|
||||||
config.Common[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf strings.Builder
|
|
||||||
if err := toml.NewEncoder(&buf).Encode(config); err != nil {
|
|
||||||
return "", fmt.Errorf("failed to write config: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance FrpcInstance, modifiedData map[string]interface{}, user *User) {
|
func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance FrpcInstance, modifiedData map[string]interface{}, user *User) {
|
||||||
newName := instance.Name
|
newName := instance.Name
|
||||||
newRunUser := instance.RunUser
|
newRunUser := instance.RunUser
|
||||||
@@ -512,12 +458,12 @@ func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance F
|
|||||||
}
|
}
|
||||||
|
|
||||||
if newBootAtStart {
|
if newBootAtStart {
|
||||||
if err := removeBootAtStart(user.Username, instance.Name); err != nil {
|
if err := removeBootAtStart(instance.ID); err != nil {
|
||||||
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to remove boot at start: %v", err))
|
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to remove boot at start: %v", err))
|
||||||
bootServiceError = fmt.Sprintf("Failed to remove boot at start: %v", err)
|
bootServiceError = fmt.Sprintf("Failed to remove boot at start: %v", err)
|
||||||
}
|
}
|
||||||
instance.Name = newName
|
instance.Name = newName
|
||||||
if err := setBootAtStart(user.Username, newName); err != nil {
|
if err := setBootAtStart(instance.ID); err != nil {
|
||||||
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to set boot at start: %v", err))
|
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to set boot at start: %v", err))
|
||||||
if bootServiceError != "" {
|
if bootServiceError != "" {
|
||||||
bootServiceError += "; "
|
bootServiceError += "; "
|
||||||
@@ -525,7 +471,7 @@ func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance F
|
|||||||
bootServiceError += fmt.Sprintf("Failed to set boot at start: %v", err)
|
bootServiceError += fmt.Sprintf("Failed to set boot at start: %v", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := removeBootAtStart(user.Username, instance.Name); err != nil {
|
if err := removeBootAtStart(instance.ID); err != nil {
|
||||||
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to remove boot at start: %v", err))
|
postLog.Error(fmt.Sprintf("[handleSystemConfigModify] Failed to remove boot at start: %v", err))
|
||||||
bootServiceError = fmt.Sprintf("Failed to remove boot at start: %v", err)
|
bootServiceError = fmt.Sprintf("Failed to remove boot at start: %v", err)
|
||||||
}
|
}
|
||||||
@@ -546,30 +492,9 @@ func handleSystemConfigModify(w http.ResponseWriter, r *http.Request, instance F
|
|||||||
SendSuccessResponse(w, "System config modified successfully", data)
|
SendSuccessResponse(w, "System config modified successfully", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateFrpcConfig(info InstanceInfo) string {
|
|
||||||
config := FrpcConfig{
|
|
||||||
Common: make(map[string]interface{}),
|
|
||||||
}
|
|
||||||
|
|
||||||
config.Common["server_addr"] = info.ServerAddr
|
|
||||||
config.Common["server_port"] = info.ServerPort
|
|
||||||
config.Common["auth_method"] = info.AuthMethod
|
|
||||||
|
|
||||||
for key, value := range info.Additional {
|
|
||||||
config.Common[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf strings.Builder
|
|
||||||
if err := toml.NewEncoder(&buf).Encode(config); err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetUserInstances(userID int) ([]FrpcInstance, error) {
|
func GetUserInstances(userID int) ([]FrpcInstance, error) {
|
||||||
rows, err := frpcDB.Query(`
|
rows, err := frpcDB.Query(`
|
||||||
SELECT id, userID, name, serverAddr, serverPort, auth_method, bootAtStart, runUser, configPath, createdAt
|
SELECT id, userID, name, bootAtStart, runUser, configPath, createdAt
|
||||||
FROM frpcInstances WHERE userID = ?
|
FROM frpcInstances WHERE userID = ?
|
||||||
`, userID)
|
`, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -582,8 +507,7 @@ func GetUserInstances(userID int) ([]FrpcInstance, error) {
|
|||||||
var instance FrpcInstance
|
var instance FrpcInstance
|
||||||
var createdAtStr string
|
var createdAtStr string
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
&instance.ID, &instance.UserID, &instance.Name, &instance.ServerAddr, &instance.ServerPort,
|
&instance.ID, &instance.UserID, &instance.Name, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr,
|
||||||
&instance.AuthMethod, &instance.BootAtStart, &instance.RunUser, &instance.ConfigPath, &createdAtStr,
|
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -642,9 +566,15 @@ func ListInstancesHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if userType == "admin" || userType == "superuser" {
|
if userType == "admin" || userType == "superuser" {
|
||||||
instanceData["serverAddr"] = inst.ServerAddr
|
serverAddr, err := getKeyText(inst.ConfigPath, "serverAddr", "")
|
||||||
instanceData["serverPort"] = inst.ServerPort
|
serverPort, err := getKeyText(inst.ConfigPath, "serverPort", "")
|
||||||
instanceData["auth_method"] = inst.AuthMethod
|
authMethod, err := getKeyText(inst.ConfigPath, "auth.method", "")
|
||||||
|
if err != nil {
|
||||||
|
postLog.Error(fmt.Sprintf("[ListInstancesHandler] Failed to read config for instance %d: %v", inst.ID, err))
|
||||||
|
}
|
||||||
|
instanceData["serverAddr"] = serverAddr
|
||||||
|
instanceData["serverPort"] = serverPort
|
||||||
|
instanceData["auth_method"] = authMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
instanceList[i] = instanceData
|
instanceList[i] = instanceData
|
||||||
@@ -725,16 +655,14 @@ func StartInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := GetUserByID(instance.UserID)
|
initType := GetInitSystem()
|
||||||
|
serviceName, err := GetServiceNameByInstanceID(instanceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to get user info: %v", err))
|
postLog.Error(fmt.Sprintf("[StartInstanceHandler] Failed to get service name: %v", err))
|
||||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info")
|
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
initType := GetInitSystem()
|
|
||||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", user.Username, instance.Name)
|
|
||||||
|
|
||||||
switch initType {
|
switch initType {
|
||||||
case "windows":
|
case "windows":
|
||||||
if err := StartWindowsService(serviceName); err != nil {
|
if err := StartWindowsService(serviceName); err != nil {
|
||||||
@@ -841,16 +769,14 @@ func StopInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := GetUserByID(instance.UserID)
|
initType := GetInitSystem()
|
||||||
|
serviceName, err := GetServiceNameByInstanceID(instanceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to get user info: %v", err))
|
postLog.Error(fmt.Sprintf("[StopInstanceHandler] Failed to get service name: %v", err))
|
||||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info")
|
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
initType := GetInitSystem()
|
|
||||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", user.Username, instance.Name)
|
|
||||||
|
|
||||||
switch initType {
|
switch initType {
|
||||||
case "windows":
|
case "windows":
|
||||||
if err := StopWindowsService(serviceName); err != nil {
|
if err := StopWindowsService(serviceName); err != nil {
|
||||||
@@ -957,16 +883,14 @@ func RestartInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := GetUserByID(instance.UserID)
|
initType := GetInitSystem()
|
||||||
|
serviceName, err := GetServiceNameByInstanceID(instanceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to get user info: %v", err))
|
postLog.Error(fmt.Sprintf("[RestartInstanceHandler] Failed to get service name: %v", err))
|
||||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info")
|
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
initType := GetInitSystem()
|
|
||||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", user.Username, instance.Name)
|
|
||||||
|
|
||||||
switch initType {
|
switch initType {
|
||||||
case "windows":
|
case "windows":
|
||||||
if err := RestartWindowsService(serviceName); err != nil {
|
if err := RestartWindowsService(serviceName); err != nil {
|
||||||
@@ -1047,16 +971,14 @@ func GetInstanceStatusHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := GetUserByID(instance.UserID)
|
|
||||||
if err != nil {
|
|
||||||
postLog.Error(fmt.Sprintf("[GetInstanceStatusHandler] Failed to get user info: %v", err))
|
|
||||||
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get user info")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
initType := GetInitSystem()
|
initType := GetInitSystem()
|
||||||
|
|
||||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", user.Username, instance.Name)
|
serviceName, err := GetServiceNameByInstanceID(instanceID)
|
||||||
|
if err != nil {
|
||||||
|
postLog.Error(fmt.Sprintf("[GetInstanceStatusHandler] Failed to get service name: %v", err))
|
||||||
|
SendErrorResponse(w, http.StatusInternalServerError, "Failed to get service name")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
responseData := map[string]interface{}{
|
responseData := map[string]interface{}{
|
||||||
"name": instance.Name,
|
"name": instance.Name,
|
||||||
@@ -1065,7 +987,7 @@ func GetInstanceStatusHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
"isRunning": false,
|
"isRunning": false,
|
||||||
}
|
}
|
||||||
|
|
||||||
isRunning := IsInstanceRunning(instance.Name)
|
isRunning := IsInstanceRunning(instanceID)
|
||||||
responseData["isRunning"] = isRunning
|
responseData["isRunning"] = isRunning
|
||||||
|
|
||||||
SendSuccessResponse(w, "Instance status retrieved successfully", responseData)
|
SendSuccessResponse(w, "Instance status retrieved successfully", responseData)
|
||||||
|
|||||||
@@ -7,17 +7,11 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"super-frpc/postLog"
|
"super-frpc/postLog"
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreateProxyRequest struct {
|
|
||||||
InstanceID string `json:"instanceID"`
|
|
||||||
ProxyInfo FrpcProxyInfo `json:"proxyInfo"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateProxyHandler(w http.ResponseWriter, r *http.Request) {
|
func CreateProxyHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != http.MethodPost {
|
if r.Method != http.MethodPost {
|
||||||
SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method")
|
SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method")
|
||||||
@@ -129,30 +123,6 @@ func CreateProxyHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
postLog.Info(fmt.Sprintf("[CreateProxyHandler] Proxy %s created successfully for instance %d", proxyInfo.Name, instance.ID))
|
postLog.Info(fmt.Sprintf("[CreateProxyHandler] Proxy %s created successfully for instance %d", proxyInfo.Name, instance.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func addFrpcProxy(configContent string, info FrpcProxyInfo) (string, error) {
|
|
||||||
var config FrpcConfig
|
|
||||||
if _, err := toml.Decode(configContent, &config); err != nil {
|
|
||||||
return "", fmt.Errorf("failed to parse config: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
proxy := map[string]interface{}{
|
|
||||||
"name": info.Name,
|
|
||||||
"type": info.Type,
|
|
||||||
"localIP": info.LocalIP,
|
|
||||||
"localPort": info.LocalPort,
|
|
||||||
"remotePort": info.RemotePort,
|
|
||||||
}
|
|
||||||
|
|
||||||
config.Proxies = append(config.Proxies, proxy)
|
|
||||||
|
|
||||||
var buf strings.Builder
|
|
||||||
if err := toml.NewEncoder(&buf).Encode(config); err != nil {
|
|
||||||
return "", fmt.Errorf("failed to write config: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteProxyHandler(w http.ResponseWriter, r *http.Request) {
|
func DeleteProxyHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != http.MethodPost {
|
if r.Method != http.MethodPost {
|
||||||
SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method")
|
SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method")
|
||||||
@@ -249,36 +219,6 @@ func DeleteProxyHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
postLog.Info(fmt.Sprintf("[DeleteProxyHandler] Proxy %s deleted successfully from instance %d", proxyName, instance.ID))
|
postLog.Info(fmt.Sprintf("[DeleteProxyHandler] Proxy %s deleted successfully from instance %d", proxyName, instance.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeFrpcProxy(configContent string, proxyName string) (string, error) {
|
|
||||||
var config FrpcConfig
|
|
||||||
if _, err := toml.Decode(configContent, &config); err != nil {
|
|
||||||
return "", fmt.Errorf("failed to parse config: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var found bool
|
|
||||||
var newProxies []map[string]interface{}
|
|
||||||
for _, proxy := range config.Proxies {
|
|
||||||
if name, ok := proxy["name"].(string); ok && name == proxyName {
|
|
||||||
found = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
newProxies = append(newProxies, proxy)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
return "", fmt.Errorf("proxy %s not found", proxyName)
|
|
||||||
}
|
|
||||||
|
|
||||||
config.Proxies = newProxies
|
|
||||||
|
|
||||||
var buf strings.Builder
|
|
||||||
if err := toml.NewEncoder(&buf).Encode(config); err != nil {
|
|
||||||
return "", fmt.Errorf("failed to write config: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListProxiesHandler(w http.ResponseWriter, r *http.Request) {
|
func ListProxiesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != http.MethodGet {
|
if r.Method != http.MethodGet {
|
||||||
SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method")
|
SendErrorResponse(w, http.StatusMethodNotAllowed, "Invalid request method")
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -5,7 +5,6 @@ go 1.24.0
|
|||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v1.4.0
|
github.com/BurntSushi/toml v1.4.0
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
golang.org/x/sys v0.37.0
|
|
||||||
modernc.org/sqlite v1.46.1
|
modernc.org/sqlite v1.46.1
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -16,6 +15,7 @@ require (
|
|||||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
||||||
|
golang.org/x/sys v0.37.0 // indirect
|
||||||
modernc.org/libc v1.67.6 // indirect
|
modernc.org/libc v1.67.6 // indirect
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
modernc.org/memory v1.11.0 // indirect
|
modernc.org/memory v1.11.0 // indirect
|
||||||
|
|||||||
134
os.go
134
os.go
@@ -44,28 +44,47 @@ func GetInitSystem() string {
|
|||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
func createBootService(username, instanceName, configPath, runUser string) error {
|
func createBootService(instanceID int) error {
|
||||||
|
instance, err := DBQueryFrpcInstanceByID(instanceID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to query frpc instance: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
initType := GetInitSystem()
|
initType := GetInitSystem()
|
||||||
|
|
||||||
switch initType {
|
switch initType {
|
||||||
case "systemd":
|
case "systemd":
|
||||||
return createSystemdService(username, instanceName, configPath, runUser)
|
return createSystemdService(instanceID, instance.ConfigPath, instance.RunUser)
|
||||||
case "init.d":
|
case "init.d":
|
||||||
return createInitDService(username, instanceName, configPath, runUser)
|
return createInitDService(instanceID, instance.ConfigPath, instance.RunUser)
|
||||||
case "windows":
|
case "windows":
|
||||||
return createWindowsBootService(username, instanceName, configPath)
|
return createWindowsBootService(instanceID, instance.ConfigPath)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported init system: %s", initType)
|
return fmt.Errorf("unsupported init system: %s", initType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSystemdService(username, instanceName, configPath, runUser string) error {
|
func createSystemdService(instanceID int, configPath, runUser string) error {
|
||||||
frpcPath, err := GetFrpcPath()
|
frpcPath, err := GetFrpcPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", username, instanceName)
|
serviceName, err := GetServiceNameByInstanceID(instanceID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
instance, err := DBQueryFrpcInstanceByID(instanceID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to query frpc instance: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := GetUserByID(instance.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get user info: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
serviceContent := fmt.Sprintf(`[Unit]
|
serviceContent := fmt.Sprintf(`[Unit]
|
||||||
Description=superfrpc_%s_%s
|
Description=superfrpc_%s_%s
|
||||||
After=network.target
|
After=network.target
|
||||||
@@ -79,7 +98,7 @@ RestartSec=5
|
|||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
`, username, instanceName, frpcPath, configPath, runUser)
|
`, user.Username, instance.Name, frpcPath, configPath, runUser)
|
||||||
|
|
||||||
servicePath := filepath.Join("/etc/systemd/system", serviceName+".service")
|
servicePath := filepath.Join("/etc/systemd/system", serviceName+".service")
|
||||||
if err := os.WriteFile(servicePath, []byte(serviceContent), 0644); err != nil {
|
if err := os.WriteFile(servicePath, []byte(serviceContent), 0644); err != nil {
|
||||||
@@ -100,13 +119,26 @@ WantedBy=multi-user.target
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createInitDService(username, instanceName, configPath, runUser string) error {
|
func createInitDService(instanceID int, configPath, runUser string) error {
|
||||||
frpcPath, err := GetFrpcPath()
|
frpcPath, err := GetFrpcPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", username, instanceName)
|
serviceName, err := GetServiceNameByInstanceID(instanceID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
instance, err := DBQueryFrpcInstanceByID(instanceID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to query frpc instance: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := GetUserByID(instance.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get user info: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
var serviceContent string
|
var serviceContent string
|
||||||
runUserArg := ""
|
runUserArg := ""
|
||||||
@@ -162,7 +194,7 @@ case "$1" in
|
|||||||
esac
|
esac
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
`, serviceName, username, instanceName, serviceName, frpcPath, configPath, runUserArg)
|
`, serviceName, user.Username, instance.Name, serviceName, frpcPath, configPath, runUserArg)
|
||||||
} else {
|
} else {
|
||||||
serviceContent = fmt.Sprintf(`#!/bin/sh
|
serviceContent = fmt.Sprintf(`#!/bin/sh
|
||||||
|
|
||||||
@@ -213,7 +245,7 @@ case "$1" in
|
|||||||
esac
|
esac
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
`, serviceName, username, instanceName, serviceName, frpcPath, configPath)
|
`, serviceName, user.Username, instance.Name, serviceName, frpcPath, configPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
servicePath := filepath.Join("/etc/init.d", serviceName)
|
servicePath := filepath.Join("/etc/init.d", serviceName)
|
||||||
@@ -230,13 +262,17 @@ exit 0
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createWindowsBootService(username, instanceName, configPath string) error {
|
func createWindowsBootService(instanceID int, configPath string) error {
|
||||||
frpcPath, err := GetFrpcPath()
|
frpcPath, err := GetFrpcPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", username, instanceName)
|
serviceName, err := GetServiceNameByInstanceID(instanceID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
command := fmt.Sprintf("\"%s\" -c \"%s\"", frpcPath, configPath)
|
command := fmt.Sprintf("\"%s\" -c \"%s\"", frpcPath, configPath)
|
||||||
|
|
||||||
cmd := exec.Command("sc", "create", serviceName, "binPath=", command, "start=", "auto")
|
cmd := exec.Command("sc", "create", serviceName, "binPath=", command, "start=", "auto")
|
||||||
@@ -249,9 +285,12 @@ func createWindowsBootService(username, instanceName, configPath string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setBootAtStart(username, instanceName string) error {
|
func setBootAtStart(instanceID int) error {
|
||||||
initType := GetInitSystem()
|
initType := GetInitSystem()
|
||||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", username, instanceName)
|
serviceName, err := GetServiceNameByInstanceID(instanceID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
switch initType {
|
switch initType {
|
||||||
case "windows":
|
case "windows":
|
||||||
@@ -280,11 +319,14 @@ func setBootAtStart(username, instanceName string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeBootAtStart(username, instanceName string) error {
|
func removeBootAtStart(instanceID int) error {
|
||||||
initType := GetInitSystem()
|
initType := GetInitSystem()
|
||||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", username, instanceName)
|
serviceName, err := GetServiceNameByInstanceID(instanceID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
switch initType{
|
switch initType {
|
||||||
case "windows":
|
case "windows":
|
||||||
cmd := exec.Command("sc", "config", serviceName, "start=", "disabled")
|
cmd := exec.Command("sc", "config", serviceName, "start=", "disabled")
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
@@ -311,10 +353,12 @@ func removeBootAtStart(username, instanceName string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeBootService(username, instanceName string) error {
|
func removeBootService(instanceID int) error {
|
||||||
initType := GetInitSystem()
|
initType := GetInitSystem()
|
||||||
|
serviceName, err := GetServiceNameByInstanceID(instanceID)
|
||||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", username, instanceName)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
switch initType {
|
switch initType {
|
||||||
case "systemd":
|
case "systemd":
|
||||||
@@ -347,11 +391,23 @@ func removeBootService(username, instanceName string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsInstanceRunning(instanceName string) bool {
|
func IsInstanceRunning(instanceID int) bool {
|
||||||
initType := GetInitSystem()
|
initType := GetInitSystem()
|
||||||
|
serviceName, err := GetServiceNameByInstanceID(instanceID)
|
||||||
|
if err != nil {
|
||||||
|
postLog.Error(fmt.Sprintf("[IsInstanceRunning] Failed to get service name: %v", err))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
instance, err := DBQueryFrpcInstanceByID(instanceID)
|
||||||
|
if err != nil {
|
||||||
|
postLog.Error(fmt.Sprintf("[IsInstanceRunning] Failed to query instance: %v", err))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
switch initType {
|
switch initType {
|
||||||
case "windows":
|
case "windows":
|
||||||
cmd := exec.Command("sc", "query", instanceName)
|
cmd := exec.Command("sc", "query", serviceName)
|
||||||
|
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
postLog.Debug(fmt.Sprintf("[IsInstanceRunning] Sc query output: %s", output))
|
postLog.Debug(fmt.Sprintf("[IsInstanceRunning] Sc query output: %s", output))
|
||||||
@@ -362,15 +418,15 @@ func IsInstanceRunning(instanceName string) bool {
|
|||||||
|
|
||||||
outputStr := string(output)
|
outputStr := string(output)
|
||||||
if strings.Contains(outputStr, "RUNNING") {
|
if strings.Contains(outputStr, "RUNNING") {
|
||||||
postLog.Info(fmt.Sprintf("[IsInstanceRunning] Windows service %s is running", instanceName))
|
postLog.Info(fmt.Sprintf("[IsInstanceRunning] Windows service %s is running", instance.Name))
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
postLog.Info(fmt.Sprintf("[IsInstanceRunning] Windows service %s is stopped", instanceName))
|
postLog.Info(fmt.Sprintf("[IsInstanceRunning] Windows service %s is stopped", instance.Name))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
case "systemd":
|
case "systemd":
|
||||||
cmd := exec.Command("systemctl", "is-active", instanceName)
|
cmd := exec.Command("systemctl", "is-active", serviceName)
|
||||||
|
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -380,17 +436,17 @@ func IsInstanceRunning(instanceName string) bool {
|
|||||||
|
|
||||||
status := strings.TrimSpace(string(output))
|
status := strings.TrimSpace(string(output))
|
||||||
if status == "active" {
|
if status == "active" {
|
||||||
postLog.Info(fmt.Sprintf("[IsInstanceRunning] Systemd service %s is running", instanceName))
|
postLog.Info(fmt.Sprintf("[IsInstanceRunning] Systemd service %s is running", serviceName))
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
postLog.Info(fmt.Sprintf("[IsInstanceRunning] Systemd service %s is stopped", instanceName))
|
postLog.Info(fmt.Sprintf("[IsInstanceRunning] Systemd service %s is stopped", serviceName))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
case "init.d":
|
case "init.d":
|
||||||
servicePath := fmt.Sprintf("/etc/init.d/%s", instanceName)
|
servicePath := fmt.Sprintf("/etc/init.d/%s", serviceName)
|
||||||
if _, err := os.Stat(servicePath); err != nil {
|
if _, err := os.Stat(servicePath); err != nil {
|
||||||
postLog.Info(fmt.Sprintf("[IsInstanceRunning] Init.d service %s not found", instanceName))
|
postLog.Info(fmt.Sprintf("[IsInstanceRunning] Init.d service %s not found", serviceName))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,11 +471,17 @@ func IsInstanceRunning(instanceName string) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetInstancePid(instanceName string) int {
|
func GetInstancePid(instanceID int) int {
|
||||||
initType := GetInitSystem()
|
initType := GetInitSystem()
|
||||||
|
serviceName, err := GetServiceNameByInstanceID(instanceID)
|
||||||
|
if err != nil {
|
||||||
|
postLog.Error(fmt.Sprintf("[GetInstancePid] Failed to get service name: %v", err))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
switch initType {
|
switch initType {
|
||||||
case "windows":
|
case "windows":
|
||||||
cmd := exec.Command("sc", "query", instanceName, "info")
|
cmd := exec.Command("sc", "query", serviceName, "info")
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
postLog.Error(fmt.Sprintf("[GetInstancePid] Failed to get Windows service info: %s, output: %s", err, output))
|
postLog.Error(fmt.Sprintf("[GetInstancePid] Failed to get Windows service info: %s, output: %s", err, output))
|
||||||
@@ -441,7 +503,7 @@ func GetInstancePid(instanceName string) int {
|
|||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
case "systemd":
|
case "systemd":
|
||||||
cmd := exec.Command("systemctl", "show", instanceName, "--property", "MainPID")
|
cmd := exec.Command("systemctl", "show", serviceName, "--property", "MainPID")
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
postLog.Error(fmt.Sprintf("[GetInstancePid] Failed to get PID from systemd: %s, output: %s", err, output))
|
postLog.Error(fmt.Sprintf("[GetInstancePid] Failed to get PID from systemd: %s, output: %s", err, output))
|
||||||
@@ -460,9 +522,9 @@ func GetInstancePid(instanceName string) int {
|
|||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
case "init.d":
|
case "init.d":
|
||||||
servicePath := fmt.Sprintf("/etc/init.d/%s", instanceName)
|
servicePath := fmt.Sprintf("/etc/init.d/%s", serviceName)
|
||||||
if _, err := os.Stat(servicePath); err != nil {
|
if _, err := os.Stat(servicePath); err != nil {
|
||||||
postLog.Error(fmt.Sprintf("[GetInstancePid] Init.d service %s not found", instanceName))
|
postLog.Error(fmt.Sprintf("[GetInstancePid] Init.d service %s not found", serviceName))
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
cmd := exec.Command(servicePath, "status")
|
cmd := exec.Command(servicePath, "status")
|
||||||
@@ -483,7 +545,7 @@ func GetInstancePid(instanceName string) int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pidFile := fmt.Sprintf("/var/run/%s.pid", instanceName)
|
pidFile := fmt.Sprintf("/var/run/%s.pid", serviceName)
|
||||||
if content, err := os.ReadFile(pidFile); err == nil {
|
if content, err := os.ReadFile(pidFile); err == nil {
|
||||||
pidStr := strings.TrimSpace(string(content))
|
pidStr := strings.TrimSpace(string(content))
|
||||||
pid, err := strconv.Atoi(pidStr)
|
pid, err := strconv.Atoi(pidStr)
|
||||||
|
|||||||
Reference in New Issue
Block a user