Большое обновление GUI интерфейс
Большое обновление GUI интерфейс - Добавлен фраемворr Walles - Удалена консольная версия - Проработан интерфейс и дизайн - Добавлено кеширование для быстрой реакции. - Сделан .ps1 сборщик для удобной сборки проекта. - Обновлён Readme
This commit is contained in:
320
Backend/admin/go/admin.go
Normal file
320
Backend/admin/go/admin.go
Normal file
@@ -0,0 +1,320 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
|
||||
webserver "vServer/Backend/WebServer"
|
||||
"vServer/Backend/admin/go/proxy"
|
||||
"vServer/Backend/admin/go/services"
|
||||
"vServer/Backend/admin/go/sites"
|
||||
"vServer/Backend/admin/go/vaccess"
|
||||
config "vServer/Backend/config"
|
||||
tools "vServer/Backend/tools"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
var appContext context.Context
|
||||
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
var isSingleInstance bool = false
|
||||
|
||||
func (a *App) Startup(ctx context.Context) {
|
||||
a.ctx = ctx
|
||||
appContext = ctx
|
||||
|
||||
// Проверяем, не запущен ли уже vServer
|
||||
if !tools.CheckSingleInstance() {
|
||||
runtime.EventsEmit(ctx, "server:already_running", true)
|
||||
// Только мониторинг, не запускаем сервисы
|
||||
config.LoadConfig()
|
||||
go a.monitorServices()
|
||||
isSingleInstance = false
|
||||
return
|
||||
}
|
||||
|
||||
isSingleInstance = true
|
||||
|
||||
// Инициализируем время запуска
|
||||
tools.ServerUptime("start")
|
||||
|
||||
// Загружаем конфигурацию
|
||||
config.LoadConfig()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
// Запускаем handler
|
||||
webserver.StartHandler()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
// Загружаем сертификаты
|
||||
webserver.Cert_start()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
// Запускаем серверы
|
||||
go webserver.StartHTTPS()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
go webserver.StartHTTP()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
// Запускаем PHP
|
||||
webserver.PHP_Start()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
// Запускаем MySQL асинхронно
|
||||
go webserver.StartMySQLServer(false)
|
||||
|
||||
// Запускаем мониторинг статусов
|
||||
go a.monitorServices()
|
||||
}
|
||||
|
||||
func (a *App) GetAllServicesStatus() services.AllServicesStatus {
|
||||
return services.GetAllServicesStatus()
|
||||
}
|
||||
|
||||
func (a *App) CheckServicesReady() bool {
|
||||
status := services.GetAllServicesStatus()
|
||||
return status.HTTP.Status && status.HTTPS.Status && status.MySQL.Status && status.PHP.Status
|
||||
}
|
||||
|
||||
func (a *App) Shutdown(ctx context.Context) {
|
||||
// Останавливаем все сервисы при закрытии приложения
|
||||
if isSingleInstance {
|
||||
webserver.StopHTTPServer()
|
||||
webserver.StopHTTPSServer()
|
||||
webserver.PHP_Stop()
|
||||
webserver.StopMySQLServer()
|
||||
|
||||
// Освобождаем мьютекс
|
||||
tools.ReleaseMutex()
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) monitorServices() {
|
||||
time.Sleep(1 * time.Second) // Ждём секунду перед первой проверкой
|
||||
|
||||
for {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
status := services.GetAllServicesStatus()
|
||||
runtime.EventsEmit(appContext, "service:changed", status)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) GetSitesList() []sites.SiteInfo {
|
||||
return sites.GetSitesList()
|
||||
}
|
||||
|
||||
func (a *App) GetProxyList() []proxy.ProxyInfo {
|
||||
return proxy.GetProxyList()
|
||||
}
|
||||
|
||||
func (a *App) StartServer() string {
|
||||
webserver.Cert_start()
|
||||
|
||||
go webserver.StartHTTPS()
|
||||
go webserver.StartHTTP()
|
||||
|
||||
webserver.PHP_Start()
|
||||
go webserver.StartMySQLServer(false)
|
||||
|
||||
return "Server started"
|
||||
}
|
||||
|
||||
func (a *App) StopServer() string {
|
||||
webserver.StopHTTPServer()
|
||||
webserver.StopHTTPSServer()
|
||||
webserver.PHP_Stop()
|
||||
webserver.StopMySQLServer()
|
||||
|
||||
return "Server stopped"
|
||||
}
|
||||
|
||||
func (a *App) ReloadConfig() string {
|
||||
config.LoadConfig()
|
||||
return "Config reloaded"
|
||||
}
|
||||
|
||||
func (a *App) GetConfig() interface{} {
|
||||
return config.ConfigData
|
||||
}
|
||||
|
||||
func (a *App) SaveConfig(configJSON string) string {
|
||||
// Форматируем JSON перед сохранением
|
||||
var tempConfig interface{}
|
||||
err := json.Unmarshal([]byte(configJSON), &tempConfig)
|
||||
if err != nil {
|
||||
return "Error: Invalid JSON"
|
||||
}
|
||||
|
||||
formattedJSON, err := json.MarshalIndent(tempConfig, "", " ")
|
||||
if err != nil {
|
||||
return "Error: " + err.Error()
|
||||
}
|
||||
|
||||
// Сохранение конфига в файл
|
||||
err = os.WriteFile(config.ConfigPath, formattedJSON, 0644)
|
||||
if err != nil {
|
||||
return "Error: " + err.Error()
|
||||
}
|
||||
|
||||
// Перезагружаем конфигурацию
|
||||
config.LoadConfig()
|
||||
return "Config saved"
|
||||
}
|
||||
|
||||
func (a *App) RestartAllServices() string {
|
||||
// Останавливаем все сервисы
|
||||
webserver.StopHTTPServer()
|
||||
webserver.StopHTTPSServer()
|
||||
webserver.PHP_Stop()
|
||||
webserver.StopMySQLServer()
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// Перезагружаем конфиг
|
||||
config.LoadConfig()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Обновляем кэш статусов сайтов
|
||||
webserver.UpdateSiteStatusCache()
|
||||
|
||||
// Перезагружаем сертификаты
|
||||
webserver.Cert_start()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
// Запускаем серверы заново
|
||||
go webserver.StartHTTPS()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
go webserver.StartHTTP()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
webserver.PHP_Start()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
go webserver.StartMySQLServer(false)
|
||||
|
||||
return "All services restarted"
|
||||
}
|
||||
|
||||
// Управление отдельными сервисами
|
||||
func (a *App) StartHTTPService() string {
|
||||
// Обновляем кэш перед запуском
|
||||
webserver.UpdateSiteStatusCache()
|
||||
go webserver.StartHTTP()
|
||||
return "HTTP started"
|
||||
}
|
||||
|
||||
func (a *App) StopHTTPService() string {
|
||||
webserver.StopHTTPServer()
|
||||
return "HTTP stopped"
|
||||
}
|
||||
|
||||
func (a *App) StartHTTPSService() string {
|
||||
// Обновляем кэш перед запуском
|
||||
webserver.UpdateSiteStatusCache()
|
||||
go webserver.StartHTTPS()
|
||||
return "HTTPS started"
|
||||
}
|
||||
|
||||
func (a *App) StopHTTPSService() string {
|
||||
webserver.StopHTTPSServer()
|
||||
return "HTTPS stopped"
|
||||
}
|
||||
|
||||
func (a *App) StartMySQLService() string {
|
||||
go webserver.StartMySQLServer(false)
|
||||
return "MySQL started"
|
||||
}
|
||||
|
||||
func (a *App) StopMySQLService() string {
|
||||
webserver.StopMySQLServer()
|
||||
return "MySQL stopped"
|
||||
}
|
||||
|
||||
func (a *App) StartPHPService() string {
|
||||
webserver.PHP_Start()
|
||||
return "PHP started"
|
||||
}
|
||||
|
||||
func (a *App) StopPHPService() string {
|
||||
webserver.PHP_Stop()
|
||||
return "PHP stopped"
|
||||
}
|
||||
|
||||
func (a *App) EnableProxyService() string {
|
||||
config.ConfigData.Soft_Settings.Proxy_enabled = true
|
||||
|
||||
// Сохраняем в файл
|
||||
configJSON, _ := json.Marshal(config.ConfigData)
|
||||
os.WriteFile(config.ConfigPath, configJSON, 0644)
|
||||
|
||||
return "Proxy enabled"
|
||||
}
|
||||
|
||||
func (a *App) DisableProxyService() string {
|
||||
config.ConfigData.Soft_Settings.Proxy_enabled = false
|
||||
|
||||
// Сохраняем в файл
|
||||
configJSON, _ := json.Marshal(config.ConfigData)
|
||||
os.WriteFile(config.ConfigPath, configJSON, 0644)
|
||||
|
||||
return "Proxy disabled"
|
||||
}
|
||||
|
||||
func (a *App) OpenSiteFolder(host string) string {
|
||||
folderPath := "WebServer/www/" + host
|
||||
|
||||
// Получаем абсолютный путь
|
||||
absPath, err := tools.AbsPath(folderPath)
|
||||
if err != nil {
|
||||
return "Error: " + err.Error()
|
||||
}
|
||||
|
||||
// Открываем папку в проводнике
|
||||
cmd := exec.Command("explorer", absPath)
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return "Error: " + err.Error()
|
||||
}
|
||||
|
||||
return "Folder opened"
|
||||
}
|
||||
|
||||
func (a *App) GetVAccessRules(host string, isProxy bool) *vaccess.VAccessConfig {
|
||||
config, err := vaccess.GetVAccessConfig(host, isProxy)
|
||||
if err != nil {
|
||||
return &vaccess.VAccessConfig{Rules: []vaccess.VAccessRule{}}
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
func (a *App) SaveVAccessRules(host string, isProxy bool, configJSON string) string {
|
||||
var config vaccess.VAccessConfig
|
||||
err := json.Unmarshal([]byte(configJSON), &config)
|
||||
if err != nil {
|
||||
return "Error: Invalid JSON"
|
||||
}
|
||||
|
||||
err = vaccess.SaveVAccessConfig(host, isProxy, &config)
|
||||
if err != nil {
|
||||
return "Error: " + err.Error()
|
||||
}
|
||||
|
||||
return "vAccess saved"
|
||||
}
|
||||
|
||||
func (a *App) UpdateSiteCache() string {
|
||||
webserver.UpdateSiteStatusCache()
|
||||
return "Cache updated"
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
command "vServer/Backend/admin/go/command"
|
||||
config "vServer/Backend/config"
|
||||
tools "vServer/Backend/tools"
|
||||
)
|
||||
|
||||
var adminServer *http.Server
|
||||
|
||||
// Запуск Admin сервера
|
||||
func StartAdmin() {
|
||||
|
||||
// Получаем значения из конфига во время выполнения
|
||||
port_admin := config.ConfigData.Soft_Settings.Admin_port
|
||||
host_admin := config.ConfigData.Soft_Settings.Admin_host
|
||||
|
||||
if tools.Port_check("ADMIN", host_admin, port_admin) {
|
||||
return
|
||||
}
|
||||
|
||||
// Создаем оптимизированный мультиплексор для админ сервера
|
||||
mux := http.NewServeMux()
|
||||
|
||||
// Регистрируем специализированные обработчики (быстрая маршрутизация)
|
||||
mux.HandleFunc("/api/", command.ApiHandler) // API эндпоинты
|
||||
mux.HandleFunc("/json/", command.JsonHandler) // JSON данные
|
||||
mux.HandleFunc("/service/", command.ServiceHandler) // Сервисные команды POST
|
||||
mux.HandleFunc("/", command.StaticHandler) // Статические файлы
|
||||
|
||||
// Создаем Admin сервер (только localhost для безопасности)
|
||||
adminServer = &http.Server{
|
||||
Addr: host_admin + ":" + port_admin,
|
||||
Handler: mux,
|
||||
}
|
||||
|
||||
tools.Logs_file(0, "ADMIN", "🛠️ Admin панель запущена на порту "+port_admin, "logs_http.log", true)
|
||||
|
||||
if err := adminServer.ListenAndServe(); err != nil {
|
||||
// Игнорируем нормальную ошибку при остановке сервера
|
||||
if err.Error() != "http: Server closed" {
|
||||
tools.Logs_file(1, "ADMIN", "❌ Ошибка запуска админ сервера: "+err.Error(), "logs_http.log", true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
webserver "vServer/Backend/WebServer"
|
||||
admin "vServer/Backend/admin"
|
||||
json "vServer/Backend/admin/go/json"
|
||||
)
|
||||
|
||||
func SecurePost(w http.ResponseWriter, r *http.Request) bool {
|
||||
// Проверяем, что запрос POST (не GET из браузера)
|
||||
|
||||
if webserver.Secure_post {
|
||||
|
||||
if r.Method != "POST" {
|
||||
http.Error(w, "Метод не разрешен. Используйте POST", http.StatusMethodNotAllowed)
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// API обработчик для /api/*
|
||||
func ApiHandler(w http.ResponseWriter, r *http.Request) {
|
||||
path := r.URL.Path
|
||||
|
||||
switch path {
|
||||
case "/api/metrics":
|
||||
json.GetAllMetrics(w)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
// JSON обработчик для /json/*
|
||||
func JsonHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
path := r.URL.Path
|
||||
|
||||
switch path {
|
||||
case "/json/server_status.json":
|
||||
w.Write(json.GetServerStatusJSON())
|
||||
case "/json/menu.json":
|
||||
w.Write(json.GetMenuJSON())
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
// Обработчик сервисных команд для /service/*
|
||||
func ServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
path := r.URL.Path
|
||||
|
||||
if !SecurePost(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
if SiteList(w, r, path) {
|
||||
return
|
||||
}
|
||||
|
||||
if site_add(w, path) {
|
||||
return
|
||||
}
|
||||
|
||||
if Service_Run(w, r, path) {
|
||||
return
|
||||
}
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
||||
// Обработчик статических файлов из embed
|
||||
func StaticHandler(w http.ResponseWriter, r *http.Request) {
|
||||
path := r.URL.Path
|
||||
|
||||
// Убираем ведущий слэш
|
||||
path = strings.TrimPrefix(path, "/")
|
||||
|
||||
// Если пустой путь, показываем index.html
|
||||
if path == "" {
|
||||
path = "html/index.html"
|
||||
} else {
|
||||
path = "html/" + path
|
||||
}
|
||||
|
||||
// Читаем файл из файловой системы (embed или диск)
|
||||
fileSystem := admin.GetFileSystem()
|
||||
content, err := fs.ReadFile(fileSystem, path)
|
||||
if err != nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Устанавливаем правильный Content-Type
|
||||
ext := filepath.Ext(path)
|
||||
switch ext {
|
||||
case ".css":
|
||||
w.Header().Set("Content-Type", "text/css")
|
||||
case ".js":
|
||||
w.Header().Set("Content-Type", "application/javascript")
|
||||
case ".json":
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
case ".html":
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
}
|
||||
|
||||
// Предотвращаем кеширование в режиме разработки (когда UseEmbedded = false)
|
||||
if !admin.UseEmbedded {
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
w.Header().Set("Pragma", "no-cache")
|
||||
w.Header().Set("Expires", "0")
|
||||
}
|
||||
|
||||
// Отдаем файл
|
||||
w.Write(content)
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
webserver "vServer/Backend/WebServer"
|
||||
json "vServer/Backend/admin/go/json"
|
||||
)
|
||||
|
||||
// Обработчик команд управления серверами
|
||||
func Service_Run(w http.ResponseWriter, r *http.Request, path string) bool {
|
||||
|
||||
switch path {
|
||||
case "/service/MySql_Stop":
|
||||
webserver.StopMySQLServer()
|
||||
json.UpdateServerStatus("MySQL Server", "stopped")
|
||||
return true
|
||||
|
||||
case "/service/MySql_Start":
|
||||
webserver.StartMySQLServer(false)
|
||||
json.UpdateServerStatus("MySQL Server", "running")
|
||||
return true
|
||||
|
||||
case "/service/Http_Stop":
|
||||
webserver.StopHTTPServer()
|
||||
json.UpdateServerStatus("HTTP Server", "stopped")
|
||||
return true
|
||||
|
||||
case "/service/Http_Start":
|
||||
go webserver.StartHTTP()
|
||||
json.UpdateServerStatus("HTTP Server", "running")
|
||||
return true
|
||||
|
||||
case "/service/Https_Stop":
|
||||
webserver.StopHTTPSServer()
|
||||
json.UpdateServerStatus("HTTPS Server", "stopped")
|
||||
return true
|
||||
|
||||
case "/service/Https_Start":
|
||||
go webserver.StartHTTPS()
|
||||
json.UpdateServerStatus("HTTPS Server", "running")
|
||||
return true
|
||||
|
||||
case "/service/Php_Start":
|
||||
webserver.PHP_Start()
|
||||
json.UpdateServerStatus("PHP Server", "running")
|
||||
return true
|
||||
|
||||
case "/service/Php_Stop":
|
||||
webserver.PHP_Stop()
|
||||
json.UpdateServerStatus("PHP Server", "stopped")
|
||||
return true
|
||||
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
return false // Команда не найдена
|
||||
}
|
||||
}
|
||||
@@ -1,269 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"vServer/Backend/config"
|
||||
"vServer/Backend/tools"
|
||||
)
|
||||
|
||||
var entries []os.DirEntry
|
||||
|
||||
func path_www() {
|
||||
|
||||
wwwPath, err := tools.AbsPath("WebServer/www")
|
||||
tools.Error_check(err, "Ошибка получения пути")
|
||||
|
||||
entries, err = os.ReadDir(wwwPath)
|
||||
tools.Error_check(err, "Ошибка чтения директории")
|
||||
|
||||
}
|
||||
|
||||
func site_type(entry os.DirEntry) string {
|
||||
|
||||
certPath, err := tools.AbsPath("WebServer/cert/" + entry.Name())
|
||||
if err == nil {
|
||||
if _, err := os.Stat(certPath); err == nil {
|
||||
return "https"
|
||||
}
|
||||
}
|
||||
return "http"
|
||||
}
|
||||
|
||||
func site_alliace(siteName string) []string {
|
||||
// Получаем абсолютный путь к config.json
|
||||
configPath, err := tools.AbsPath("WebServer/config.json")
|
||||
tools.Error_check(err, "Ошибка получения пути к config.json")
|
||||
|
||||
// Читаем содержимое config.json
|
||||
configData, err := os.ReadFile(configPath)
|
||||
tools.Error_check(err, "Ошибка чтения config.json")
|
||||
|
||||
// Структура для парсинга Site_www
|
||||
type SiteConfig struct {
|
||||
Name string `json:"name"`
|
||||
Host string `json:"host"`
|
||||
Alias []string `json:"alias"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
type Config struct {
|
||||
SiteWWW []SiteConfig `json:"Site_www"`
|
||||
}
|
||||
|
||||
var config Config
|
||||
err = json.Unmarshal(configData, &config)
|
||||
tools.Error_check(err, "Ошибка парсинга config.json")
|
||||
|
||||
// Ищем алиасы для конкретного сайта
|
||||
for _, site := range config.SiteWWW {
|
||||
if site.Host == siteName {
|
||||
return site.Alias
|
||||
}
|
||||
}
|
||||
|
||||
// Возвращаем пустой массив если сайт не найден
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func site_status(siteName string) string {
|
||||
configPath := "WebServer/config.json"
|
||||
|
||||
// Читаем конфигурационный файл
|
||||
data, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return "error"
|
||||
}
|
||||
|
||||
// Парсим JSON
|
||||
var config map[string]interface{}
|
||||
if err := json.Unmarshal(data, &config); err != nil {
|
||||
return "error"
|
||||
}
|
||||
|
||||
// Получаем список сайтов
|
||||
siteWww, ok := config["Site_www"].([]interface{})
|
||||
if !ok {
|
||||
return "error"
|
||||
}
|
||||
|
||||
// Ищем сайт по host
|
||||
for _, siteInterface := range siteWww {
|
||||
site, ok := siteInterface.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Проверяем только по host (имя папки)
|
||||
if host, ok := site["host"].(string); ok && host == siteName {
|
||||
if status, ok := site["status"].(string); ok {
|
||||
return status
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "inactive"
|
||||
}
|
||||
|
||||
func SiteList(w http.ResponseWriter, r *http.Request, path string) bool {
|
||||
|
||||
switch path {
|
||||
|
||||
case "/service/Site_List":
|
||||
sites := []map[string]interface{}{}
|
||||
|
||||
path_www()
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
site := map[string]interface{}{
|
||||
"host": entry.Name(),
|
||||
"type": site_type(entry),
|
||||
"aliases": site_alliace(entry.Name()),
|
||||
"status": site_status(entry.Name()),
|
||||
}
|
||||
sites = append(sites, site)
|
||||
}
|
||||
}
|
||||
|
||||
metrics := map[string]interface{}{
|
||||
"sites": sites,
|
||||
}
|
||||
|
||||
data, _ := json.MarshalIndent(metrics, "", " ")
|
||||
w.Write(data)
|
||||
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
func addSiteToConfig(siteName string) error {
|
||||
// Получаем абсолютный путь к config.json
|
||||
configPath, err := tools.AbsPath("WebServer/config.json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Читаем содержимое config.json
|
||||
configData, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Парсим как общий JSON объект
|
||||
var config map[string]interface{}
|
||||
err = json.Unmarshal(configData, &config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Получаем массив сайтов
|
||||
siteWWW, ok := config["Site_www"].([]interface{})
|
||||
if !ok {
|
||||
siteWWW = []interface{}{}
|
||||
}
|
||||
|
||||
// Создаем новый сайт в том же формате что уже есть
|
||||
newSite := map[string]interface{}{
|
||||
"name": siteName,
|
||||
"host": siteName,
|
||||
"alias": []string{""},
|
||||
"status": "active",
|
||||
}
|
||||
|
||||
// Добавляем новый сайт в массив
|
||||
config["Site_www"] = append(siteWWW, newSite)
|
||||
|
||||
// Сохраняем обновленный конфиг
|
||||
updatedData, err := json.MarshalIndent(config, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Делаем массивы алиасов в одну строку
|
||||
re := regexp.MustCompile(`"alias": \[\s+"([^"]*?)"\s+\]`)
|
||||
compactData := re.ReplaceAll(updatedData, []byte(`"alias": ["$1"]`))
|
||||
|
||||
// Исправляем отступы после Site_www
|
||||
dataStr := string(compactData)
|
||||
dataStr = strings.ReplaceAll(dataStr, ` ],
|
||||
"Soft_Settings"`, ` ],
|
||||
|
||||
"Soft_Settings"`)
|
||||
compactData = []byte(dataStr)
|
||||
|
||||
err = os.WriteFile(configPath, compactData, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func site_add(w http.ResponseWriter, path string) bool {
|
||||
|
||||
// URL параметры: /service/Site_Add/sitename
|
||||
if strings.HasPrefix(path, "/service/Site_Add/") {
|
||||
siteName := strings.TrimPrefix(path, "/service/Site_Add/")
|
||||
|
||||
if siteName == "" {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte(`{"status":"error","message":"Не указано имя сайта в URL"}`))
|
||||
return true
|
||||
}
|
||||
|
||||
wwwPath := "WebServer/www/" + siteName
|
||||
|
||||
// Проверяем существует ли уже такой сайт
|
||||
if _, err := os.Stat(wwwPath); err == nil {
|
||||
w.WriteHeader(409) // Conflict
|
||||
w.Write([]byte(`{"status":"error","message":"Сайт ` + siteName + ` уже существует"}`))
|
||||
return true
|
||||
}
|
||||
|
||||
err := os.MkdirAll(wwwPath, 0755)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(`{"status":"error","message":"Ошибка создания папки сайта"}`))
|
||||
return true
|
||||
}
|
||||
|
||||
// Добавляем сайт в config.json
|
||||
err = addSiteToConfig(siteName)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(`{"status":"error","message":"Ошибка добавления в конфигурацию: ` + err.Error() + `"}`))
|
||||
return true
|
||||
}
|
||||
|
||||
// Создаем папку public_www
|
||||
publicWwwPath := wwwPath + "/public_www"
|
||||
err = os.MkdirAll(publicWwwPath, 0755)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(`{"status":"error","message":"Ошибка создания папки public_www"}`))
|
||||
return true
|
||||
}
|
||||
|
||||
indexFilePath := wwwPath + "/public_www/index.html"
|
||||
indexContent := "Привет друг! Твой сайт создан!"
|
||||
err = os.WriteFile(indexFilePath, []byte(indexContent), 0644)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(`{"status":"error","message":"Ошибка создания index.html: ` + err.Error() + `"}`))
|
||||
return true
|
||||
}
|
||||
|
||||
w.Write([]byte(`{"status":"ok","message":"Сайт ` + siteName + ` успешно создан и добавлен в конфигурацию"}`))
|
||||
config.LoadConfig()
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package json
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// Данные серверов
|
||||
var ServerStatus = []map[string]interface{}{
|
||||
{"NameService": "HTTP Server", "Port": 80, "Status": "stopped"},
|
||||
{"NameService": "HTTPS Server", "Port": 443, "Status": "stopped"},
|
||||
{"NameService": "PHP Server", "Port": 9000, "Status": "stopped"},
|
||||
{"NameService": "MySQL Server", "Port": 3306, "Status": "stopped"},
|
||||
}
|
||||
|
||||
// Данные меню
|
||||
var MenuData = []map[string]interface{}{
|
||||
{"name": "Dashboard", "icon": "🏠", "url": "#dashboard", "active": true},
|
||||
{"name": "Серверы", "icon": "🖥️", "url": "#servers", "active": false},
|
||||
{"name": "Сайты", "icon": "🌐", "url": "#sites", "active": false},
|
||||
{"name": "SSL Сертификаты", "icon": "🔒", "url": "#certificates", "active": false},
|
||||
{"name": "Файловый менеджер", "icon": "📁", "url": "#files", "active": false},
|
||||
{"name": "Базы данных", "icon": "🗄️", "url": "#databases", "active": false},
|
||||
{"name": "Логи", "icon": "📋", "url": "#logs", "active": false},
|
||||
{"name": "Настройки", "icon": "⚙️", "url": "#settings", "active": false},
|
||||
}
|
||||
|
||||
// Функция обновления статуса сервера
|
||||
func UpdateServerStatus(serviceName, status string) {
|
||||
for i := range ServerStatus {
|
||||
if ServerStatus[i]["NameService"] == serviceName {
|
||||
ServerStatus[i]["Status"] = status
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Получить JSON серверов
|
||||
func GetServerStatusJSON() []byte {
|
||||
data, _ := json.Marshal(ServerStatus)
|
||||
return data
|
||||
}
|
||||
|
||||
// Получить JSON меню
|
||||
func GetMenuJSON() []byte {
|
||||
data, _ := json.Marshal(MenuData)
|
||||
return data
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
"vServer/Backend/tools"
|
||||
)
|
||||
|
||||
var CPU_NAME string
|
||||
var CPU_GHz string
|
||||
var CPU_Cores string
|
||||
var CPU_Using string
|
||||
var Disk_Size string
|
||||
var Disk_Free string
|
||||
var Disk_Used string
|
||||
var Disk_Name string
|
||||
var RAM_Using string
|
||||
var RAM_Total string
|
||||
|
||||
// Инициализация при запуске пакета
|
||||
func init() {
|
||||
// Загружаем статичные данные один раз при старте
|
||||
UpdateMetrics()
|
||||
// Загружаем динамические данные в фоне
|
||||
go updateMetricsBackground()
|
||||
}
|
||||
|
||||
func UpdateMetrics() {
|
||||
commands := []string{
|
||||
`$name = (Get-WmiObject Win32_DiskDrive | Where-Object { $_.Index -eq (Get-WmiObject Win32_DiskPartition | Where-Object { $_.DeviceID -eq ((Get-WmiObject Win32_LogicalDiskToPartition | Where-Object { $_.Dependent -match "C:" }).Antecedent -split '"')[1] }).DiskIndex }).Model`,
|
||||
`$size = "{0} GB" -f [math]::Round(((Get-PSDrive -Name C).Used + (Get-PSDrive -Name C).Free) / 1GB)`,
|
||||
"$cpuInfo = Get-CimInstance Win32_Processor",
|
||||
"$cpuCores = $cpuInfo.NumberOfCores",
|
||||
"$cpuName = $cpuInfo.Name",
|
||||
`$ram_total = [math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 2)`,
|
||||
"Write-Output \"$cpuName|$cpuCores|$name|$size|$ram_total\"",
|
||||
}
|
||||
|
||||
// Выполняем команды и получаем результат
|
||||
result := tools.RunPersistentScript(commands)
|
||||
|
||||
// Парсим результат для статичных данных
|
||||
parts := strings.Split(result, "|")
|
||||
if len(parts) >= 4 {
|
||||
cpuName := strings.TrimSpace(parts[0])
|
||||
cpuCores := strings.TrimSpace(parts[1])
|
||||
diskName := strings.TrimSpace(parts[2])
|
||||
diskSize := strings.TrimSpace(parts[3])
|
||||
ramTotal := strings.TrimSpace(parts[4])
|
||||
|
||||
// Обновляем глобальные переменные
|
||||
CPU_NAME = cpuName
|
||||
CPU_Cores = cpuCores
|
||||
Disk_Name = diskName
|
||||
Disk_Size = diskSize
|
||||
RAM_Total = ramTotal
|
||||
}
|
||||
}
|
||||
|
||||
// Фоновое обновление динамических метрик (внутреннее)
|
||||
func updateMetricsBackground() {
|
||||
|
||||
updateDynamicMetrics := func() {
|
||||
commands := []string{
|
||||
"$cpuInfo = Get-CimInstance Win32_Processor",
|
||||
"$cpuGHz = $cpuInfo.MaxClockSpeed",
|
||||
"$cpuUsage = $cpuInfo.LoadPercentage",
|
||||
`$used = "{0} GB" -f [math]::Round(((Get-PSDrive -Name C).Used / 1GB))`,
|
||||
`$free = "{0} GB" -f [math]::Round(((Get-PSDrive -Name C).Free / 1GB))`,
|
||||
`$ram_using = [math]::Round((Get-CimInstance Win32_OperatingSystem | % {($_.TotalVisibleMemorySize - $_.FreePhysicalMemory) / 1MB}), 2)`,
|
||||
"Write-Output \"$cpuGHz|$cpuUsage|$used|$free|$ram_using\"",
|
||||
}
|
||||
|
||||
// Один запуск PowerShell для динамических команд!
|
||||
result := tools.RunPersistentScript(commands)
|
||||
|
||||
// Парсим результат для динамических данных
|
||||
parts := strings.Split(result, "|")
|
||||
if len(parts) >= 4 {
|
||||
cpuGHz := strings.TrimSpace(parts[0])
|
||||
cpuUsage := strings.TrimSpace(parts[1])
|
||||
diskUsed := strings.TrimSpace(parts[2])
|
||||
diskFree := strings.TrimSpace(parts[3])
|
||||
ramUsing := strings.TrimSpace(parts[4])
|
||||
// Обновляем глобальные переменные
|
||||
CPU_GHz = cpuGHz
|
||||
CPU_Using = cpuUsage
|
||||
Disk_Used = diskUsed
|
||||
Disk_Free = diskFree
|
||||
RAM_Using = ramUsing
|
||||
}
|
||||
}
|
||||
|
||||
// Выполняем сразу при запуске
|
||||
updateDynamicMetrics()
|
||||
|
||||
// Затем каждые 5 секунд
|
||||
for range time.NewTicker(5 * time.Second).C {
|
||||
updateDynamicMetrics()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Получить JSON системных метрик
|
||||
func GetAllMetrics(w http.ResponseWriter) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(GetMetricsJSON())
|
||||
}
|
||||
|
||||
// Получить JSON метрик
|
||||
func GetMetricsJSON() []byte {
|
||||
metrics := map[string]interface{}{
|
||||
"cpu_name": CPU_NAME,
|
||||
"cpu_ghz": CPU_GHz,
|
||||
"cpu_cores": CPU_Cores,
|
||||
"cpu_usage": CPU_Using,
|
||||
"disk_name": Disk_Name,
|
||||
"disk_size": Disk_Size,
|
||||
"disk_used": Disk_Used,
|
||||
"disk_free": Disk_Free,
|
||||
"ram_using": RAM_Using,
|
||||
"ram_total": RAM_Total,
|
||||
"server_uptime": tools.ServerUptime("get"),
|
||||
}
|
||||
|
||||
data, _ := json.Marshal(metrics)
|
||||
return data
|
||||
}
|
||||
30
Backend/admin/go/proxy/proxy.go
Normal file
30
Backend/admin/go/proxy/proxy.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
config "vServer/Backend/config"
|
||||
)
|
||||
|
||||
func GetProxyList() []ProxyInfo {
|
||||
proxies := make([]ProxyInfo, 0)
|
||||
|
||||
for _, proxyConfig := range config.ConfigData.Proxy_Service {
|
||||
status := "disabled"
|
||||
if proxyConfig.Enable {
|
||||
status = "active"
|
||||
}
|
||||
|
||||
proxyInfo := ProxyInfo{
|
||||
Enable: proxyConfig.Enable,
|
||||
ExternalDomain: proxyConfig.ExternalDomain,
|
||||
LocalAddress: proxyConfig.LocalAddress,
|
||||
LocalPort: proxyConfig.LocalPort,
|
||||
ServiceHTTPSuse: proxyConfig.ServiceHTTPSuse,
|
||||
AutoHTTPS: proxyConfig.AutoHTTPS,
|
||||
Status: status,
|
||||
}
|
||||
proxies = append(proxies, proxyInfo)
|
||||
}
|
||||
|
||||
return proxies
|
||||
}
|
||||
|
||||
12
Backend/admin/go/proxy/types.go
Normal file
12
Backend/admin/go/proxy/types.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package proxy
|
||||
|
||||
type ProxyInfo struct {
|
||||
Enable bool `json:"enable"`
|
||||
ExternalDomain string `json:"external_domain"`
|
||||
LocalAddress string `json:"local_address"`
|
||||
LocalPort string `json:"local_port"`
|
||||
ServiceHTTPSuse bool `json:"service_https_use"`
|
||||
AutoHTTPS bool `json:"auto_https"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
92
Backend/admin/go/services/services.go
Normal file
92
Backend/admin/go/services/services.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
webserver "vServer/Backend/WebServer"
|
||||
config "vServer/Backend/config"
|
||||
)
|
||||
|
||||
func GetAllServicesStatus() AllServicesStatus {
|
||||
return AllServicesStatus{
|
||||
HTTP: getHTTPStatus(),
|
||||
HTTPS: getHTTPSStatus(),
|
||||
MySQL: getMySQLStatus(),
|
||||
PHP: getPHPStatus(),
|
||||
Proxy: getProxyStatus(),
|
||||
}
|
||||
}
|
||||
|
||||
func getHTTPStatus() ServiceStatus {
|
||||
// Используем внутренний статус вместо TCP проверки
|
||||
return ServiceStatus{
|
||||
Name: "HTTP",
|
||||
Status: webserver.GetHTTPStatus(),
|
||||
Port: "80",
|
||||
Info: "",
|
||||
}
|
||||
}
|
||||
|
||||
func getHTTPSStatus() ServiceStatus {
|
||||
// Используем внутренний статус вместо TCP проверки
|
||||
return ServiceStatus{
|
||||
Name: "HTTPS",
|
||||
Status: webserver.GetHTTPSStatus(),
|
||||
Port: "443",
|
||||
Info: "",
|
||||
}
|
||||
}
|
||||
|
||||
func getMySQLStatus() ServiceStatus {
|
||||
port := fmt.Sprintf("%d", config.ConfigData.Soft_Settings.Mysql_port)
|
||||
|
||||
// Используем внутренний статус вместо TCP проверки
|
||||
// чтобы не вызывать connect_errors в MySQL
|
||||
return ServiceStatus{
|
||||
Name: "MySQL",
|
||||
Status: webserver.GetMySQLStatus(),
|
||||
Port: port,
|
||||
Info: "",
|
||||
}
|
||||
}
|
||||
|
||||
func getPHPStatus() ServiceStatus {
|
||||
basePort := config.ConfigData.Soft_Settings.Php_port
|
||||
|
||||
// Диапазон портов для 4 воркеров
|
||||
portRange := fmt.Sprintf("%d-%d", basePort, basePort+3)
|
||||
|
||||
// Используем внутренний статус вместо TCP проверки
|
||||
return ServiceStatus{
|
||||
Name: "PHP",
|
||||
Status: webserver.GetPHPStatus(),
|
||||
Port: portRange,
|
||||
Info: "",
|
||||
}
|
||||
}
|
||||
|
||||
func getProxyStatus() ServiceStatus {
|
||||
activeCount := 0
|
||||
totalCount := len(config.ConfigData.Proxy_Service)
|
||||
|
||||
for _, proxy := range config.ConfigData.Proxy_Service {
|
||||
if proxy.Enable {
|
||||
activeCount++
|
||||
}
|
||||
}
|
||||
|
||||
info := fmt.Sprintf("%d из %d", activeCount, totalCount)
|
||||
|
||||
// Проверяем глобальный флаг и статус HTTP/HTTPS
|
||||
proxyEnabled := config.ConfigData.Soft_Settings.Proxy_enabled
|
||||
httpRunning := webserver.GetHTTPStatus()
|
||||
httpsRunning := webserver.GetHTTPSStatus()
|
||||
|
||||
status := proxyEnabled && (httpRunning || httpsRunning)
|
||||
|
||||
return ServiceStatus{
|
||||
Name: "Proxy",
|
||||
Status: status,
|
||||
Port: "-",
|
||||
Info: info,
|
||||
}
|
||||
}
|
||||
16
Backend/admin/go/services/types.go
Normal file
16
Backend/admin/go/services/types.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package services
|
||||
|
||||
type ServiceStatus struct {
|
||||
Name string `json:"name"`
|
||||
Status bool `json:"status"`
|
||||
Port string `json:"port"`
|
||||
Info string `json:"info"`
|
||||
}
|
||||
|
||||
type AllServicesStatus struct {
|
||||
HTTP ServiceStatus `json:"http"`
|
||||
HTTPS ServiceStatus `json:"https"`
|
||||
MySQL ServiceStatus `json:"mysql"`
|
||||
PHP ServiceStatus `json:"php"`
|
||||
Proxy ServiceStatus `json:"proxy"`
|
||||
}
|
||||
24
Backend/admin/go/sites/sites.go
Normal file
24
Backend/admin/go/sites/sites.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package sites
|
||||
|
||||
import (
|
||||
config "vServer/Backend/config"
|
||||
)
|
||||
|
||||
func GetSitesList() []SiteInfo {
|
||||
sites := make([]SiteInfo, 0)
|
||||
|
||||
for _, site := range config.ConfigData.Site_www {
|
||||
siteInfo := SiteInfo{
|
||||
Name: site.Name,
|
||||
Host: site.Host,
|
||||
Alias: site.Alias,
|
||||
Status: site.Status,
|
||||
RootFile: site.Root_file,
|
||||
RootFileRouting: site.Root_file_routing,
|
||||
}
|
||||
sites = append(sites, siteInfo)
|
||||
}
|
||||
|
||||
return sites
|
||||
}
|
||||
|
||||
11
Backend/admin/go/sites/types.go
Normal file
11
Backend/admin/go/sites/types.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package sites
|
||||
|
||||
type SiteInfo struct {
|
||||
Name string `json:"name"`
|
||||
Host string `json:"host"`
|
||||
Alias []string `json:"alias"`
|
||||
Status string `json:"status"`
|
||||
RootFile string `json:"root_file"`
|
||||
RootFileRouting bool `json:"root_file_routing"`
|
||||
}
|
||||
|
||||
14
Backend/admin/go/vaccess/types.go
Normal file
14
Backend/admin/go/vaccess/types.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package vaccess
|
||||
|
||||
type VAccessRule struct {
|
||||
Type string `json:"type"`
|
||||
TypeFile []string `json:"type_file"`
|
||||
PathAccess []string `json:"path_access"`
|
||||
IPList []string `json:"ip_list"`
|
||||
ExceptionsDir []string `json:"exceptions_dir"`
|
||||
UrlError string `json:"url_error"`
|
||||
}
|
||||
|
||||
type VAccessConfig struct {
|
||||
Rules []VAccessRule `json:"rules"`
|
||||
}
|
||||
158
Backend/admin/go/vaccess/vaccess.go
Normal file
158
Backend/admin/go/vaccess/vaccess.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package vaccess
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
tools "vServer/Backend/tools"
|
||||
)
|
||||
|
||||
func GetVAccessPath(host string, isProxy bool) string {
|
||||
if isProxy {
|
||||
return fmt.Sprintf("WebServer/tools/Proxy_vAccess/%s_vAccess.conf", host)
|
||||
}
|
||||
return fmt.Sprintf("WebServer/www/%s/vAccess.conf", host)
|
||||
}
|
||||
|
||||
func GetVAccessConfig(host string, isProxy bool) (*VAccessConfig, error) {
|
||||
filePath := GetVAccessPath(host, isProxy)
|
||||
|
||||
// Проверяем существование файла
|
||||
absPath, _ := tools.AbsPath(filePath)
|
||||
if _, err := os.Stat(absPath); os.IsNotExist(err) {
|
||||
// Файл не существует - возвращаем пустую конфигурацию
|
||||
return &VAccessConfig{Rules: []VAccessRule{}}, nil
|
||||
}
|
||||
|
||||
file, err := os.Open(absPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
config := &VAccessConfig{}
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
var currentRule *VAccessRule
|
||||
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
|
||||
// Пропускаем пустые строки и комментарии
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Парсим параметры
|
||||
if strings.Contains(line, ":") {
|
||||
parts := strings.SplitN(line, ":", 2)
|
||||
if len(parts) == 2 {
|
||||
key := strings.TrimSpace(parts[0])
|
||||
value := strings.TrimSpace(parts[1])
|
||||
|
||||
switch key {
|
||||
case "type":
|
||||
// Начало нового правила - сохраняем предыдущее
|
||||
if currentRule != nil && currentRule.Type != "" {
|
||||
config.Rules = append(config.Rules, *currentRule)
|
||||
}
|
||||
// Создаём новое правило
|
||||
currentRule = &VAccessRule{}
|
||||
currentRule.Type = value
|
||||
case "type_file":
|
||||
if currentRule != nil {
|
||||
currentRule.TypeFile = splitAndTrim(value)
|
||||
}
|
||||
case "path_access":
|
||||
if currentRule != nil {
|
||||
currentRule.PathAccess = splitAndTrim(value)
|
||||
}
|
||||
case "ip_list":
|
||||
if currentRule != nil {
|
||||
currentRule.IPList = splitAndTrim(value)
|
||||
}
|
||||
case "exceptions_dir":
|
||||
if currentRule != nil {
|
||||
currentRule.ExceptionsDir = splitAndTrim(value)
|
||||
}
|
||||
case "url_error":
|
||||
if currentRule != nil {
|
||||
currentRule.UrlError = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Добавляем последнее правило
|
||||
if currentRule != nil && currentRule.Type != "" {
|
||||
config.Rules = append(config.Rules, *currentRule)
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func SaveVAccessConfig(host string, isProxy bool, config *VAccessConfig) error {
|
||||
filePath := GetVAccessPath(host, isProxy)
|
||||
|
||||
// Создаём директорию если не существует
|
||||
dir := ""
|
||||
if isProxy {
|
||||
dir = "WebServer/tools/Proxy_vAccess"
|
||||
} else {
|
||||
dir = fmt.Sprintf("WebServer/www/%s", host)
|
||||
}
|
||||
|
||||
absDir, _ := tools.AbsPath(dir)
|
||||
os.MkdirAll(absDir, 0755)
|
||||
|
||||
// Получаем абсолютный путь к файлу
|
||||
absPath, err := tools.AbsPath(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Формируем содержимое файла
|
||||
var content strings.Builder
|
||||
|
||||
content.WriteString("# vAccess Configuration\n")
|
||||
content.WriteString("# Правила применяются сверху вниз\n\n")
|
||||
|
||||
for i, rule := range config.Rules {
|
||||
content.WriteString(fmt.Sprintf("# Правило %d\n", i+1))
|
||||
content.WriteString(fmt.Sprintf("type: %s\n", rule.Type))
|
||||
|
||||
if len(rule.TypeFile) > 0 {
|
||||
content.WriteString(fmt.Sprintf("type_file: %s\n", strings.Join(rule.TypeFile, ", ")))
|
||||
}
|
||||
if len(rule.PathAccess) > 0 {
|
||||
content.WriteString(fmt.Sprintf("path_access: %s\n", strings.Join(rule.PathAccess, ", ")))
|
||||
}
|
||||
if len(rule.IPList) > 0 {
|
||||
content.WriteString(fmt.Sprintf("ip_list: %s\n", strings.Join(rule.IPList, ", ")))
|
||||
}
|
||||
if len(rule.ExceptionsDir) > 0 {
|
||||
content.WriteString(fmt.Sprintf("exceptions_dir: %s\n", strings.Join(rule.ExceptionsDir, ", ")))
|
||||
}
|
||||
if rule.UrlError != "" {
|
||||
content.WriteString(fmt.Sprintf("url_error: %s\n", rule.UrlError))
|
||||
}
|
||||
|
||||
content.WriteString("\n")
|
||||
}
|
||||
|
||||
return os.WriteFile(absPath, []byte(content.String()), 0644)
|
||||
}
|
||||
|
||||
func splitAndTrim(s string) []string {
|
||||
parts := strings.Split(s, ",")
|
||||
result := []string{}
|
||||
for _, part := range parts {
|
||||
trimmed := strings.TrimSpace(part)
|
||||
if trimmed != "" {
|
||||
result = append(result, trimmed)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user