Исправление Сервера
This commit is contained in:
@@ -1,190 +1,202 @@
|
||||
package webserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
config "vServer/Backend/config"
|
||||
tools "vServer/Backend/tools"
|
||||
)
|
||||
|
||||
var mysqlProcess *exec.Cmd
|
||||
var mysql_status bool = false
|
||||
var mysql_secure bool = false
|
||||
|
||||
// GetMySQLStatus возвращает статус MySQL
|
||||
func GetMySQLStatus() bool {
|
||||
return mysql_status
|
||||
}
|
||||
|
||||
var mysqldPath string
|
||||
var configPath string
|
||||
var dataDirAbs string
|
||||
var binDirAbs string
|
||||
var binPathAbs string
|
||||
|
||||
var mysql_port int
|
||||
var mysql_ip string
|
||||
|
||||
var console_mysql bool = false
|
||||
|
||||
func AbsPathMySQL() {
|
||||
|
||||
var err error
|
||||
|
||||
mysqldPath, err = tools.AbsPath(filepath.Join("WebServer/soft/MySQL/bin", "mysqld.exe"))
|
||||
tools.CheckError(err)
|
||||
|
||||
configPath, err = tools.AbsPath("WebServer/soft/MySQL/my.ini")
|
||||
tools.CheckError(err)
|
||||
|
||||
dataDirAbs, err = tools.AbsPath("WebServer/soft/MySQL/bin/data")
|
||||
tools.CheckError(err)
|
||||
|
||||
binDirAbs, err = tools.AbsPath("WebServer/soft/MySQL/bin")
|
||||
tools.CheckError(err)
|
||||
|
||||
binPathAbs, err = tools.AbsPath("WebServer/soft/MySQL/bin")
|
||||
tools.CheckError(err)
|
||||
|
||||
}
|
||||
|
||||
// config_patch возвращает путь к mysqld, аргументы и бинарную директорию
|
||||
func config_patch(secures bool) (string, []string, string) {
|
||||
|
||||
// Получаем абсолютные пути
|
||||
AbsPathMySQL()
|
||||
|
||||
// Объявляем args на уровне функции
|
||||
var args []string
|
||||
|
||||
if secures {
|
||||
|
||||
args = []string{
|
||||
"--defaults-file=" + configPath,
|
||||
"--datadir=" + dataDirAbs,
|
||||
"--shared-memory",
|
||||
"--skip-grant-tables",
|
||||
"--console",
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
args = []string{
|
||||
"--defaults-file=" + configPath,
|
||||
"--port=" + fmt.Sprintf("%d", mysql_port),
|
||||
"--bind-address=" + mysql_ip,
|
||||
"--datadir=" + dataDirAbs,
|
||||
"--console",
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return mysqldPath, args, binDirAbs
|
||||
}
|
||||
|
||||
// StartMySQLServer запускает MySQL сервер
|
||||
func StartMySQLServer(secure bool) {
|
||||
|
||||
mysql_port = config.ConfigData.Soft_Settings.Mysql_port
|
||||
mysql_ip = config.ConfigData.Soft_Settings.Mysql_host
|
||||
|
||||
if mysql_status {
|
||||
tools.Logs_file(1, "MySQL", "Сервер MySQL уже запущен", "logs_mysql.log", false)
|
||||
return
|
||||
}
|
||||
|
||||
// Настройка режима
|
||||
mysql_secure = secure
|
||||
mysqldPath, args, binDirAbs := config_patch(secure)
|
||||
|
||||
// Выбор сообщения
|
||||
if secure {
|
||||
tools.Logs_file(0, "MySQL", "Запуск сервера MySQL в режиме безопасности", "logs_mysql.log", false)
|
||||
} else {
|
||||
tools.Logs_file(0, "MySQL", "Запуск сервера MySQL в обычном режиме", "logs_mysql.log", false)
|
||||
}
|
||||
|
||||
// Общая логика запуска
|
||||
mysqlProcess = exec.Command(mysqldPath, args...)
|
||||
mysqlProcess.Dir = binDirAbs
|
||||
tools.Logs_console(mysqlProcess, console_mysql)
|
||||
|
||||
tools.Logs_file(0, "MySQL", fmt.Sprintf("Сервер MySQL запущен на %s:%d", mysql_ip, mysql_port), "logs_mysql.log", false)
|
||||
|
||||
mysql_status = true
|
||||
|
||||
}
|
||||
|
||||
// StopMySQLServer останавливает MySQL сервер
|
||||
func StopMySQLServer() {
|
||||
|
||||
if !mysql_status {
|
||||
return // Уже остановлен
|
||||
}
|
||||
|
||||
// Сначала пробуем завершить процесс корректно
|
||||
if mysqlProcess != nil && mysqlProcess.Process != nil {
|
||||
mysqlProcess.Process.Kill()
|
||||
mysqlProcess = nil
|
||||
}
|
||||
|
||||
// Дополнительно убиваем все mysqld.exe процессы
|
||||
cmd := exec.Command("taskkill", "/F", "/IM", "mysqld.exe")
|
||||
|
||||
// Скрываем окно taskkill
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
HideWindow: true,
|
||||
CreationFlags: 0x08000000,
|
||||
}
|
||||
|
||||
cmd.Run()
|
||||
|
||||
tools.Logs_file(0, "MySQL", "Сервер MySQL остановлен", "logs_mysql.log", false)
|
||||
mysql_status = false
|
||||
|
||||
}
|
||||
|
||||
func ResetPasswordMySQL() {
|
||||
|
||||
NewPasswordMySQL := "root"
|
||||
|
||||
StopMySQLServer()
|
||||
time.Sleep(2 * time.Second)
|
||||
mysql_secure = true
|
||||
StartMySQLServer(true)
|
||||
time.Sleep(2 * time.Second)
|
||||
query := "FLUSH PRIVILEGES; ALTER USER 'root'@'%' IDENTIFIED BY '" + NewPasswordMySQL + "';"
|
||||
СheckMySQLPassword(query)
|
||||
tools.Logs_file(0, "MySQL", "Новый пароль: "+NewPasswordMySQL, "logs_mysql.log", true)
|
||||
println()
|
||||
StopMySQLServer()
|
||||
StartMySQLServer(false)
|
||||
|
||||
}
|
||||
|
||||
// СheckMySQLPassword проверяет пароль для MySQL
|
||||
func СheckMySQLPassword(query string) {
|
||||
|
||||
AbsPathMySQL()
|
||||
|
||||
if mysql_secure {
|
||||
|
||||
// В безопасном режиме подключаемся без пароля
|
||||
cmd := exec.Command(filepath.Join(binPathAbs, "mysql.exe"), "-u", "root", "-pRoot", "-e", query)
|
||||
cmd.Dir = binPathAbs
|
||||
|
||||
// Захватываем вывод для логирования
|
||||
err := tools.Logs_console(cmd, false)
|
||||
|
||||
if err != nil {
|
||||
tools.Logs_file(1, "MySQL", "Вывод MySQL (stdout/stderr):", "logs_mysql.log", true)
|
||||
} else {
|
||||
tools.Logs_file(0, "MySQL", "Команда выполнена успешно", "logs_mysql.log", true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
package webserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
config "vServer/Backend/config"
|
||||
tools "vServer/Backend/tools"
|
||||
)
|
||||
|
||||
var mysqlProcess *exec.Cmd
|
||||
var mysql_status bool = false
|
||||
var mysql_secure bool = false
|
||||
|
||||
// GetMySQLStatus возвращает статус MySQL
|
||||
func GetMySQLStatus() bool {
|
||||
return mysql_status
|
||||
}
|
||||
|
||||
func normalizeBindAddress(host string) string {
|
||||
parts := strings.Split(host, ",")
|
||||
cleaned := make([]string, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
if ip := strings.TrimSpace(part); ip != "" {
|
||||
cleaned = append(cleaned, ip)
|
||||
}
|
||||
}
|
||||
return strings.Join(cleaned, ",")
|
||||
}
|
||||
|
||||
var mysqldPath string
|
||||
var configPath string
|
||||
var dataDirAbs string
|
||||
var binDirAbs string
|
||||
var binPathAbs string
|
||||
|
||||
var mysql_port int
|
||||
var mysql_ip string
|
||||
|
||||
var console_mysql bool = false
|
||||
|
||||
func AbsPathMySQL() {
|
||||
|
||||
var err error
|
||||
|
||||
mysqldPath, err = tools.AbsPath(filepath.Join("WebServer/soft/MySQL/bin", "mysqld.exe"))
|
||||
tools.CheckError(err)
|
||||
|
||||
configPath, err = tools.AbsPath("WebServer/soft/MySQL/my.ini")
|
||||
tools.CheckError(err)
|
||||
|
||||
dataDirAbs, err = tools.AbsPath("WebServer/soft/MySQL/bin/data")
|
||||
tools.CheckError(err)
|
||||
|
||||
binDirAbs, err = tools.AbsPath("WebServer/soft/MySQL/bin")
|
||||
tools.CheckError(err)
|
||||
|
||||
binPathAbs, err = tools.AbsPath("WebServer/soft/MySQL/bin")
|
||||
tools.CheckError(err)
|
||||
|
||||
}
|
||||
|
||||
// config_patch возвращает путь к mysqld, аргументы и бинарную директорию
|
||||
func config_patch(secures bool) (string, []string, string) {
|
||||
|
||||
// Получаем абсолютные пути
|
||||
AbsPathMySQL()
|
||||
|
||||
// Объявляем args на уровне функции
|
||||
var args []string
|
||||
|
||||
if secures {
|
||||
|
||||
args = []string{
|
||||
"--defaults-file=" + configPath,
|
||||
"--datadir=" + dataDirAbs,
|
||||
"--shared-memory",
|
||||
"--skip-grant-tables",
|
||||
"--console",
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
args = []string{
|
||||
"--defaults-file=" + configPath,
|
||||
"--port=" + fmt.Sprintf("%d", mysql_port),
|
||||
"--bind-address=" + mysql_ip,
|
||||
"--datadir=" + dataDirAbs,
|
||||
"--console",
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return mysqldPath, args, binDirAbs
|
||||
}
|
||||
|
||||
// StartMySQLServer запускает MySQL сервер
|
||||
func StartMySQLServer(secure bool) {
|
||||
|
||||
mysql_port = config.ConfigData.Soft_Settings.Mysql_port
|
||||
mysql_ip = normalizeBindAddress(config.ConfigData.Soft_Settings.Mysql_host)
|
||||
|
||||
if mysql_status {
|
||||
tools.Logs_file(1, "MySQL", "Сервер MySQL уже запущен", "logs_mysql.log", false)
|
||||
return
|
||||
}
|
||||
|
||||
// Настройка режима
|
||||
mysql_secure = secure
|
||||
mysqldPath, args, binDirAbs := config_patch(secure)
|
||||
|
||||
// Выбор сообщения
|
||||
if secure {
|
||||
tools.Logs_file(0, "MySQL", "Запуск сервера MySQL в режиме безопасности", "logs_mysql.log", false)
|
||||
} else {
|
||||
tools.Logs_file(0, "MySQL", "Запуск сервера MySQL в обычном режиме", "logs_mysql.log", false)
|
||||
}
|
||||
|
||||
// Общая логика запуска
|
||||
mysqlProcess = exec.Command(mysqldPath, args...)
|
||||
mysqlProcess.Dir = binDirAbs
|
||||
tools.Logs_console(mysqlProcess, console_mysql)
|
||||
|
||||
tools.Logs_file(0, "MySQL", fmt.Sprintf("Сервер MySQL запущен на %s:%d", mysql_ip, mysql_port), "logs_mysql.log", false)
|
||||
|
||||
mysql_status = true
|
||||
|
||||
}
|
||||
|
||||
// StopMySQLServer останавливает MySQL сервер
|
||||
func StopMySQLServer() {
|
||||
|
||||
if !mysql_status {
|
||||
return // Уже остановлен
|
||||
}
|
||||
|
||||
// Сначала пробуем завершить процесс корректно
|
||||
if mysqlProcess != nil && mysqlProcess.Process != nil {
|
||||
mysqlProcess.Process.Kill()
|
||||
mysqlProcess = nil
|
||||
}
|
||||
|
||||
// Дополнительно убиваем все mysqld.exe процессы
|
||||
cmd := exec.Command("taskkill", "/F", "/IM", "mysqld.exe")
|
||||
|
||||
// Скрываем окно taskkill
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
HideWindow: true,
|
||||
CreationFlags: 0x08000000,
|
||||
}
|
||||
|
||||
cmd.Run()
|
||||
|
||||
tools.Logs_file(0, "MySQL", "Сервер MySQL остановлен", "logs_mysql.log", false)
|
||||
mysql_status = false
|
||||
|
||||
}
|
||||
|
||||
func ResetPasswordMySQL() {
|
||||
|
||||
NewPasswordMySQL := "root"
|
||||
|
||||
StopMySQLServer()
|
||||
time.Sleep(2 * time.Second)
|
||||
mysql_secure = true
|
||||
StartMySQLServer(true)
|
||||
time.Sleep(2 * time.Second)
|
||||
query := "FLUSH PRIVILEGES; ALTER USER 'root'@'%' IDENTIFIED BY '" + NewPasswordMySQL + "';"
|
||||
СheckMySQLPassword(query)
|
||||
tools.Logs_file(0, "MySQL", "Новый пароль: "+NewPasswordMySQL, "logs_mysql.log", true)
|
||||
println()
|
||||
StopMySQLServer()
|
||||
StartMySQLServer(false)
|
||||
|
||||
}
|
||||
|
||||
// СheckMySQLPassword проверяет пароль для MySQL
|
||||
func СheckMySQLPassword(query string) {
|
||||
|
||||
AbsPathMySQL()
|
||||
|
||||
if mysql_secure {
|
||||
|
||||
// В безопасном режиме подключаемся без пароля
|
||||
cmd := exec.Command(filepath.Join(binPathAbs, "mysql.exe"), "-u", "root", "-pRoot", "-e", query)
|
||||
cmd.Dir = binPathAbs
|
||||
|
||||
// Захватываем вывод для логирования
|
||||
err := tools.Logs_console(cmd, false)
|
||||
|
||||
if err != nil {
|
||||
tools.Logs_file(1, "MySQL", "Вывод MySQL (stdout/stderr):", "logs_mysql.log", true)
|
||||
} else {
|
||||
tools.Logs_file(0, "MySQL", "Команда выполнена успешно", "logs_mysql.log", true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
4370
front_vue/package-lock.json
generated
4370
front_vue/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,162 +1,162 @@
|
||||
<script setup>
|
||||
const { t } = useI18n()
|
||||
const configStore = useConfigStore()
|
||||
const servicesStore = useServicesStore()
|
||||
const { success } = useNotification()
|
||||
|
||||
const form = reactive({
|
||||
mysqlHost: '',
|
||||
mysqlPort: 3306,
|
||||
phpHost: '',
|
||||
phpPort: 8000,
|
||||
proxyEnabled: false,
|
||||
acmeEnabled: false,
|
||||
})
|
||||
|
||||
const saving = ref(false)
|
||||
|
||||
onMounted(async () => {
|
||||
await configStore.load()
|
||||
const s = configStore.softSettings
|
||||
form.mysqlHost = s.mysql_host || '127.0.0.1'
|
||||
form.mysqlPort = s.mysql_port || 3306
|
||||
form.phpHost = s.php_host || 'localhost'
|
||||
form.phpPort = s.php_port || 8000
|
||||
form.proxyEnabled = s.proxy_enabled || false
|
||||
form.acmeEnabled = s.ACME_enabled || false
|
||||
})
|
||||
|
||||
const saveSettings = async () => {
|
||||
saving.value = true
|
||||
const configData = {
|
||||
...configStore.data,
|
||||
Soft_Settings: {
|
||||
mysql_host: form.mysqlHost,
|
||||
mysql_port: Number(form.mysqlPort),
|
||||
php_host: form.phpHost,
|
||||
php_port: Number(form.phpPort),
|
||||
proxy_enabled: form.proxyEnabled,
|
||||
ACME_enabled: form.acmeEnabled,
|
||||
},
|
||||
}
|
||||
await configStore.save(configData)
|
||||
await servicesStore.restartAll()
|
||||
saving.value = false
|
||||
success(t('notify.settingsSaved'))
|
||||
}
|
||||
|
||||
const toggleProxy = async () => {
|
||||
form.proxyEnabled = !form.proxyEnabled
|
||||
if (form.proxyEnabled) await servicesStore.enableProxy()
|
||||
else await servicesStore.disableProxy()
|
||||
success(form.proxyEnabled ? t('notify.proxyEnabled') : t('notify.proxyDisabled'))
|
||||
}
|
||||
|
||||
const toggleAcme = async () => {
|
||||
form.acmeEnabled = !form.acmeEnabled
|
||||
if (form.acmeEnabled) await servicesStore.enableACME()
|
||||
else await servicesStore.disableACME()
|
||||
success(form.acmeEnabled ? t('notify.certManagerEnabled') : t('notify.certManagerDisabled'))
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="settings-view">
|
||||
<div class="settings-header">
|
||||
<VSectionHeader :title="t('settings.title')" />
|
||||
<VButton variant="success" icon="fas fa-save" :loading="saving" @click="saveSettings">
|
||||
{{ saving ? t('settings.saving') : t('settings.save') }}
|
||||
</VButton>
|
||||
</div>
|
||||
|
||||
<div class="settings-grid">
|
||||
<div class="settings-card">
|
||||
<h3 class="settings-card-title"><i class="fas fa-database"></i> {{ t('settings.mysql') }}</h3>
|
||||
<div class="settings-form">
|
||||
<VInput v-model="form.mysqlHost" :label="t('settings.hostAddr')" placeholder="127.0.0.1" />
|
||||
<VInput v-model="form.mysqlPort" :label="t('settings.port')" type="number" placeholder="3306" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-card">
|
||||
<h3 class="settings-card-title"><i class="fab fa-php"></i> {{ t('settings.php') }}</h3>
|
||||
<div class="settings-form">
|
||||
<VInput v-model="form.phpHost" :label="t('settings.hostAddr')" placeholder="localhost" />
|
||||
<VInput v-model="form.phpPort" :label="t('settings.port')" type="number" placeholder="8000" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-card">
|
||||
<h3 class="settings-card-title">
|
||||
<VTooltip :text="t('settings.proxyHint')" />
|
||||
<i class="fas fa-network-wired"></i> {{ t('settings.proxyManager') }}
|
||||
</h3>
|
||||
<div class="settings-form">
|
||||
<VToggle v-model="form.proxyEnabled" :label="t('settings.proxyManager')" @update:model-value="toggleProxy" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-card">
|
||||
<h3 class="settings-card-title">
|
||||
<VTooltip :text="t('settings.certHint')" />
|
||||
<i class="fas fa-certificate"></i> {{ t('settings.certManager') }}
|
||||
</h3>
|
||||
<div class="settings-form">
|
||||
<VToggle v-model="form.acmeEnabled" :label="t('settings.certManager')" @update:model-value="toggleAcme" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.settings-view {
|
||||
animation: fadeIn var(--transition-slow);
|
||||
}
|
||||
|
||||
.settings-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: var(--space-lg);
|
||||
}
|
||||
|
||||
.settings-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: var(--space-lg);
|
||||
}
|
||||
|
||||
.settings-card {
|
||||
background: rgba(var(--accent-rgb), 0.02);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius);
|
||||
padding: var(--space-lg);
|
||||
transition: all var(--transition-base);
|
||||
}
|
||||
|
||||
.settings-card:hover {
|
||||
background: rgba(var(--accent-rgb), 0.04);
|
||||
border-color: rgba(var(--accent-rgb), 0.2);
|
||||
}
|
||||
|
||||
.settings-card-title {
|
||||
font-size: var(--text-md);
|
||||
font-weight: var(--font-semibold);
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 20px 0;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid rgba(var(--accent-rgb), 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
.settings-card-title i {
|
||||
color: var(--accent-purple-light);
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.settings-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
<script setup>
|
||||
const { t } = useI18n()
|
||||
const configStore = useConfigStore()
|
||||
const servicesStore = useServicesStore()
|
||||
const { success } = useNotification()
|
||||
|
||||
const form = reactive({
|
||||
mysqlHost: '',
|
||||
mysqlPort: 3306,
|
||||
phpHost: '',
|
||||
phpPort: 8000,
|
||||
proxyEnabled: false,
|
||||
acmeEnabled: false,
|
||||
})
|
||||
|
||||
const saving = ref(false)
|
||||
|
||||
onMounted(async () => {
|
||||
await configStore.load()
|
||||
const s = configStore.softSettings
|
||||
form.mysqlHost = s.mysql_host || '127.0.0.1'
|
||||
form.mysqlPort = s.mysql_port || 3306
|
||||
form.phpHost = s.php_host || 'localhost'
|
||||
form.phpPort = s.php_port || 8000
|
||||
form.proxyEnabled = s.proxy_enabled || false
|
||||
form.acmeEnabled = s.ACME_enabled || false
|
||||
})
|
||||
|
||||
const saveSettings = async () => {
|
||||
saving.value = true
|
||||
const configData = {
|
||||
...configStore.data,
|
||||
Soft_Settings: {
|
||||
mysql_host: form.mysqlHost,
|
||||
mysql_port: Number(form.mysqlPort),
|
||||
php_host: form.phpHost,
|
||||
php_port: Number(form.phpPort),
|
||||
proxy_enabled: form.proxyEnabled,
|
||||
ACME_enabled: form.acmeEnabled,
|
||||
},
|
||||
}
|
||||
await configStore.save(configData)
|
||||
await servicesStore.restartAll()
|
||||
saving.value = false
|
||||
success(t('notify.settingsSaved'))
|
||||
}
|
||||
|
||||
const toggleProxy = async () => {
|
||||
form.proxyEnabled = !form.proxyEnabled
|
||||
if (form.proxyEnabled) await servicesStore.enableProxy()
|
||||
else await servicesStore.disableProxy()
|
||||
success(form.proxyEnabled ? t('notify.proxyEnabled') : t('notify.proxyDisabled'))
|
||||
}
|
||||
|
||||
const toggleAcme = async () => {
|
||||
form.acmeEnabled = !form.acmeEnabled
|
||||
if (form.acmeEnabled) await servicesStore.enableACME()
|
||||
else await servicesStore.disableACME()
|
||||
success(form.acmeEnabled ? t('notify.certManagerEnabled') : t('notify.certManagerDisabled'))
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="settings-view">
|
||||
<div class="settings-header">
|
||||
<VSectionHeader :title="t('settings.title')" />
|
||||
<VButton variant="success" icon="fas fa-save" :loading="saving" @click="saveSettings">
|
||||
{{ saving ? t('settings.saving') : t('settings.save') }}
|
||||
</VButton>
|
||||
</div>
|
||||
|
||||
<div class="settings-grid">
|
||||
<div class="settings-card">
|
||||
<h3 class="settings-card-title"><i class="fas fa-database"></i> {{ t('settings.mysql') }}</h3>
|
||||
<div class="settings-form">
|
||||
<VInput v-model="form.mysqlHost" :label="t('settings.hostAddr')" placeholder="127.0.0.1, 192.168.1.9" />
|
||||
<VInput v-model="form.mysqlPort" :label="t('settings.port')" type="number" placeholder="3306" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-card">
|
||||
<h3 class="settings-card-title"><i class="fab fa-php"></i> {{ t('settings.php') }}</h3>
|
||||
<div class="settings-form">
|
||||
<VInput v-model="form.phpHost" :label="t('settings.hostAddr')" placeholder="localhost" />
|
||||
<VInput v-model="form.phpPort" :label="t('settings.port')" type="number" placeholder="8000" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-card">
|
||||
<h3 class="settings-card-title">
|
||||
<VTooltip :text="t('settings.proxyHint')" />
|
||||
<i class="fas fa-network-wired"></i> {{ t('settings.proxyManager') }}
|
||||
</h3>
|
||||
<div class="settings-form">
|
||||
<VToggle v-model="form.proxyEnabled" :label="t('settings.proxyManager')" @update:model-value="toggleProxy" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-card">
|
||||
<h3 class="settings-card-title">
|
||||
<VTooltip :text="t('settings.certHint')" />
|
||||
<i class="fas fa-certificate"></i> {{ t('settings.certManager') }}
|
||||
</h3>
|
||||
<div class="settings-form">
|
||||
<VToggle v-model="form.acmeEnabled" :label="t('settings.certManager')" @update:model-value="toggleAcme" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.settings-view {
|
||||
animation: fadeIn var(--transition-slow);
|
||||
}
|
||||
|
||||
.settings-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: var(--space-lg);
|
||||
}
|
||||
|
||||
.settings-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: var(--space-lg);
|
||||
}
|
||||
|
||||
.settings-card {
|
||||
background: rgba(var(--accent-rgb), 0.02);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius);
|
||||
padding: var(--space-lg);
|
||||
transition: all var(--transition-base);
|
||||
}
|
||||
|
||||
.settings-card:hover {
|
||||
background: rgba(var(--accent-rgb), 0.04);
|
||||
border-color: rgba(var(--accent-rgb), 0.2);
|
||||
}
|
||||
|
||||
.settings-card-title {
|
||||
font-size: var(--text-md);
|
||||
font-weight: var(--font-semibold);
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 20px 0;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid rgba(var(--accent-rgb), 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
.settings-card-title i {
|
||||
color: var(--accent-purple-light);
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.settings-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user