Большое обновление GUI интерфейс

Большое обновление GUI интерфейс

- Добавлен фраемворr Walles
- Удалена консольная версия
- Проработан интерфейс и дизайн
- Добавлено кеширование для быстрой реакции.
- Сделан .ps1 сборщик для удобной сборки проекта.
- Обновлён Readme
This commit is contained in:
2025-11-14 08:40:25 +07:00
parent 752f294392
commit 02ae56b78c
93 changed files with 7477 additions and 3504 deletions

320
Backend/admin/go/admin.go Normal file
View 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"
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}

View File

@@ -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 // Команда не найдена
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View 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
}

View 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"`
}

View 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,
}
}

View 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"`
}

View 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
}

View 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"`
}

View 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"`
}

View 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
}