2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 09:13:49 +08:00

feat: php支持74和80

This commit is contained in:
耗子
2024-11-01 21:42:20 +08:00
parent 336a9bc675
commit b96c6ad899
17 changed files with 582 additions and 1298 deletions

View File

@@ -1,6 +1,8 @@
package php
import (
"fmt"
"github.com/go-chi/chi/v5"
"github.com/TheTNB/panel/pkg/apploader"
@@ -8,76 +10,25 @@ import (
)
func init() {
apploader.Register(&types.App{
Slug: "php80",
Route: func(r chi.Router) {
service := NewService(80)
r.Get("/load", service.Load)
r.Get("/config", service.GetConfig)
r.Post("/config", service.UpdateConfig)
r.Get("/fpmConfig", service.GetFPMConfig)
r.Post("/fpmConfig", service.UpdateFPMConfig)
r.Get("/errorLog", service.ErrorLog)
r.Get("/slowLog", service.SlowLog)
r.Post("/clearErrorLog", service.ClearErrorLog)
r.Post("/clearSlowLog", service.ClearSlowLog)
r.Get("/extensions", service.ExtensionList)
r.Post("/extensions", service.InstallExtension)
r.Delete("/extensions", service.UninstallExtension)
},
})
apploader.Register(&types.App{
Slug: "php81",
Route: func(r chi.Router) {
service := NewService(81)
r.Get("/load", service.Load)
r.Get("/config", service.GetConfig)
r.Post("/config", service.UpdateConfig)
r.Get("/fpmConfig", service.GetFPMConfig)
r.Post("/fpmConfig", service.UpdateFPMConfig)
r.Get("/errorLog", service.ErrorLog)
r.Get("/slowLog", service.SlowLog)
r.Post("/clearErrorLog", service.ClearErrorLog)
r.Post("/clearSlowLog", service.ClearSlowLog)
r.Get("/extensions", service.ExtensionList)
r.Post("/extensions", service.InstallExtension)
r.Delete("/extensions", service.UninstallExtension)
},
})
apploader.Register(&types.App{
Slug: "php82",
Route: func(r chi.Router) {
service := NewService(82)
r.Get("/load", service.Load)
r.Get("/config", service.GetConfig)
r.Post("/config", service.UpdateConfig)
r.Get("/fpmConfig", service.GetFPMConfig)
r.Post("/fpmConfig", service.UpdateFPMConfig)
r.Get("/errorLog", service.ErrorLog)
r.Get("/slowLog", service.SlowLog)
r.Post("/clearErrorLog", service.ClearErrorLog)
r.Post("/clearSlowLog", service.ClearSlowLog)
r.Get("/extensions", service.ExtensionList)
r.Post("/extensions", service.InstallExtension)
r.Delete("/extensions", service.UninstallExtension)
},
})
apploader.Register(&types.App{
Slug: "php83",
Route: func(r chi.Router) {
service := NewService(83)
r.Get("/load", service.Load)
r.Get("/config", service.GetConfig)
r.Post("/config", service.UpdateConfig)
r.Get("/fpmConfig", service.GetFPMConfig)
r.Post("/fpmConfig", service.UpdateFPMConfig)
r.Get("/errorLog", service.ErrorLog)
r.Get("/slowLog", service.SlowLog)
r.Post("/clearErrorLog", service.ClearErrorLog)
r.Post("/clearSlowLog", service.ClearSlowLog)
r.Get("/extensions", service.ExtensionList)
r.Post("/extensions", service.InstallExtension)
r.Delete("/extensions", service.UninstallExtension)
},
})
php := []uint{74, 80, 81, 82, 83}
for _, version := range php {
apploader.Register(&types.App{
Slug: fmt.Sprintf("php%d", version),
Route: func(r chi.Router) {
service := NewService(version)
r.Get("/load", service.Load)
r.Get("/config", service.GetConfig)
r.Post("/config", service.UpdateConfig)
r.Get("/fpmConfig", service.GetFPMConfig)
r.Post("/fpmConfig", service.UpdateFPMConfig)
r.Get("/errorLog", service.ErrorLog)
r.Get("/slowLog", service.SlowLog)
r.Post("/clearErrorLog", service.ClearErrorLog)
r.Post("/clearSlowLog", service.ClearSlowLog)
r.Get("/extensions", service.ExtensionList)
r.Post("/extensions", service.InstallExtension)
r.Delete("/extensions", service.UninstallExtension)
},
})
}
}

View File

@@ -138,17 +138,6 @@ func (s *Service) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
func (s *Service) ExtensionList(w http.ResponseWriter, r *http.Request) {
extensions := s.getExtensions()
// ionCube 只支持 PHP 8.3 以下版本
if cast.ToUint(s.version) < 83 {
extensions = append(extensions, Extension{
Name: "ionCube",
Slug: "ionCube Loader",
Description: "ionCube 是一个专业级的 PHP 加密解密工具。",
Installed: false,
})
}
raw, err := shell.Execf("%s/server/php/%d/bin/php -m", app.Root, s.version)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
@@ -182,10 +171,10 @@ func (s *Service) InstallExtension(w http.ResponseWriter, r *http.Request) {
return
}
cmd := fmt.Sprintf(`curl -fsLm 10 --retry 3 "https://dl.cdn.haozi.net/panel/php_exts/%s.sh" | bash -s -- "install" "%d" >> /tmp/%s.log 2>&1`, url.QueryEscape(req.Slug), s.version, req.Slug)
cmd := fmt.Sprintf(`curl -fsLm 10 --retry 3 'https://dl.cdn.haozi.net/panel/php_exts/%s.sh' | bash -s -- 'install' '%d' >> '/tmp/%s.log' 2>&1`, url.PathEscape(req.Slug), s.version, req.Slug)
officials := []string{"fileinfo", "exif", "imap", "pdo_pgsql", "zip", "bz2", "readline", "snmp", "ldap", "enchant", "pspell", "calendar", "gmp", "sysvmsg", "sysvsem", "sysvshm", "xsl", "intl", "gettext"}
if slices.Contains(officials, req.Slug) {
cmd = fmt.Sprintf(`curl -fsLm 10 --retry 3 "https://dl.cdn.haozi.net/panel/php_exts/official.sh" | bash -s -- "install" "%d" "%s" >> /tmp/%s.log 2>&1`, s.version, req.Slug, req.Slug)
cmd = fmt.Sprintf(`curl -fsLm 10 --retry 3 'https://dl.cdn.haozi.net/panel/php_exts/official.sh' | bash -s -- 'install' '%d' '%s' >> '/tmp/%s.log' 2>&1`, s.version, req.Slug, req.Slug)
}
task := new(biz.Task)
@@ -213,10 +202,10 @@ func (s *Service) UninstallExtension(w http.ResponseWriter, r *http.Request) {
return
}
cmd := fmt.Sprintf(`curl -fsLm 10 --retry 3 "https://dl.cdn.haozi.net/panel/php_exts/%s.sh" | bash -s -- "uninstall" "%d" >> /tmp/%s.log 2>&1`, url.QueryEscape(req.Slug), s.version, req.Slug)
cmd := fmt.Sprintf(`curl -fsLm 10 --retry 3 'https://dl.cdn.haozi.net/panel/php_exts/%s.sh' | bash -s -- 'uninstall' '%d' >> '/tmp/%s.log' 2>&1`, url.PathEscape(req.Slug), s.version, req.Slug)
officials := []string{"fileinfo", "exif", "imap", "pdo_pgsql", "zip", "bz2", "readline", "snmp", "ldap", "enchant", "pspell", "calendar", "gmp", "sysvmsg", "sysvsem", "sysvshm", "xsl", "intl", "gettext"}
if slices.Contains(officials, req.Slug) {
cmd = fmt.Sprintf(`curl -fsLm 10 --retry 3 "https://dl.cdn.haozi.net/panel/php_exts/official.sh" | bash -s -- "uninstall" "%d" "%s" >> /tmp/%s.log 2>&1`, s.version, req.Slug, req.Slug)
cmd = fmt.Sprintf(`curl -fsLm 10 --retry 3 'https://dl.cdn.haozi.net/panel/php_exts/official.sh' | bash -s -- 'uninstall' '%d' '%s' >> '/tmp/%s.log' 2>&1`, s.version, req.Slug, req.Slug)
}
task := new(biz.Task)
@@ -237,137 +226,136 @@ func (s *Service) getExtensions() []Extension {
{
Name: "fileinfo",
Slug: "fileinfo",
Description: "Fileinfo 是一个用于识别文件类型的库",
Description: "Fileinfo 是一个用于识别文件类型的库",
},
{
Name: "OPcache",
Slug: "Zend OPcache",
Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销",
Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销",
},
{
Name: "PhpRedis",
Slug: "redis",
Description: "PhpRedis 是一个用 C 语言编写的 PHP 模块,用来连接并操作 Redis 数据库上的数据",
Description: "PhpRedis 是一个用 C 语言编写的 PHP 模块,用来连接并操作 Redis 数据库上的数据",
},
{
Name: "ImageMagick",
Slug: "imagick",
Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件",
Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件",
},
{
Name: "exif",
Slug: "exif",
Description: "通过 exif 扩展,您可以操作图像元数据",
Description: "通过 exif 扩展,您可以操作图像元数据",
},
{
Name: "pdo_pgsql",
Slug: "pdo_pgsql",
Description: "需先安装PostgreSQLpdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象PDO接口以启用从 PHP 到 PostgreSQL 数据库的访问",
Description: "pdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象PDO接口以启用从 PHP 到 PostgreSQL 数据库的访问需先安装PostgreSQL",
},
{
Name: "imap",
Slug: "imap",
Description: "IMAP 扩展允许 PHP 读取、搜索、删除、下载和管理邮件",
Description: "IMAP 扩展允许 PHP 读取、搜索、删除、下载和管理邮件",
},
{
Name: "zip",
Slug: "zip",
Description: "Zip 是一个用于处理 ZIP 文件的库",
Description: "Zip 是一个用于处理 ZIP 文件的库",
},
{
Name: "bz2",
Slug: "bz2",
Description: "Bzip2 是一个用于压缩和解压缩文件的库",
Description: "Bzip2 是一个用于压缩和解压缩文件的库",
},
{
Name: "readline",
Slug: "readline",
Description: "Readline 是一个库,它提供了一种用于处理文本的接口",
Description: "Readline 是一个库,它提供了一种用于处理文本的接口",
},
{
Name: "snmp",
Slug: "snmp",
Description: "SNMP 是一种用于网络管理的协议",
Description: "SNMP 是一种用于网络管理的协议",
},
{
Name: "ldap",
Slug: "ldap",
Description: "LDAP 是一种用于访问目录服务的协议",
Description: "LDAP 是一种用于访问目录服务的协议",
},
{
Name: "enchant",
Slug: "enchant",
Description: "Enchant 是一个拼写检查库",
Description: "Enchant 是一个拼写检查库",
},
{
Name: "pspell",
Slug: "pspell",
Description: "Pspell 是一个拼写检查库",
Description: "Pspell 是一个拼写检查库",
},
{
Name: "calendar",
Slug: "calendar",
Description: "Calendar 是一个用于处理日期的库",
Description: "Calendar 是一个用于处理日期的库",
},
{
Name: "gmp",
Slug: "gmp",
Description: "GMP 是一个用于处理大整数的库",
Description: "GMP 是一个用于处理大整数的库",
},
{
Name: "sysvmsg",
Slug: "sysvmsg",
Description: "Sysvmsg 是一个用于处理 System V 消息队列的库",
Description: "Sysvmsg 是一个用于处理 System V 消息队列的库",
},
{
Name: "sysvsem",
Slug: "sysvsem",
Description: "Sysvsem 是一个用于处理 System V 信号量的库",
Description: "Sysvsem 是一个用于处理 System V 信号量的库",
},
{
Name: "sysvshm",
Slug: "sysvshm",
Description: "Sysvshm 是一个用于处理 System V 共享内存的库",
Description: "Sysvshm 是一个用于处理 System V 共享内存的库",
},
{
Name: "xsl",
Slug: "xsl",
Description: "XSL 是一个用于处理 XML 文档的库",
Description: "XSL 是一个用于处理 XML 文档的库",
},
{
Name: "intl",
Slug: "intl",
Description: "Intl 是一个用于处理国际化和本地化的库",
Description: "Intl 是一个用于处理国际化和本地化的库",
},
{
Name: "gettext",
Slug: "gettext",
Description: "Gettext 是一个用于处理多语言的库",
Description: "Gettext 是一个用于处理多语言的库",
},
{
Name: "igbinary",
Slug: "igbinary",
Description: "Igbinary 是一个用于序列化和反序列化数据的库",
Description: "Igbinary 是一个用于序列化和反序列化数据的库",
},
{
Name: "ionCube",
Slug: "ionCube Loader",
Description: "ionCube 是一个专业级的 PHP 加密解密工具(需在 OPcache 之后安装)",
},
{
Name: "Swoole",
Slug: "swoole",
Description: "Swoole 是一个用于构建高性能的异步并发服务器的 PHP 扩展",
},
{
Name: "Swow",
Slug: "Swow",
Description: "Swow 是一个用于构建高性能的异步并发服务器的 PHP 扩展。",
Description: "Swoole 是一个用于构建高性能的异步并发服务器的 PHP 扩展",
},
}
// ionCube 只支持 PHP 8.3 以下版本
if cast.ToUint(s.version) < 83 {
// Swow 不支持 PHP 8.0 以下版本
if cast.ToUint(s.version) >= 80 {
extensions = append(extensions, Extension{
Name: "ionCube",
Slug: "ionCube Loader",
Description: "ionCube 是一个专业级的 PHP 加密解密工具。",
Installed: false,
Name: "Swow",
Slug: "Swow",
Description: "Swow 是一个用于构建高性能的异步并发服务器的 PHP 扩展。",
})
}

View File

@@ -76,7 +76,7 @@ func (r *containerImageRepo) Pull(req *request.ContainerImagePull) error {
if req.Auth {
sb.WriteString(fmt.Sprintf("docker login -u %s -p %s", req.Username, req.Password))
if _, err := shell.ExecfWithTimeout(1*time.Minute, sb.String()); err != nil { // nolint: govet
if _, err := shell.Exec(sb.String()); err != nil {
return fmt.Errorf("login failed: %w", err)
}
sb.Reset()
@@ -84,7 +84,7 @@ func (r *containerImageRepo) Pull(req *request.ContainerImagePull) error {
sb.WriteString(fmt.Sprintf("docker pull %s", req.Name))
if _, err := shell.Execf(sb.String()); err != nil { // nolint: govet
if _, err := shell.Exec(sb.String()); err != nil {
return err
}

View File

@@ -115,7 +115,7 @@ func (r *containerNetworkRepo) Create(req *request.ContainerNetworkCreate) (stri
sb.WriteString(fmt.Sprintf(" --opt %s=%s", option.Key, option.Value))
}
return shell.ExecfWithTimeout(120*time.Second, sb.String()) // nolint: govet
return shell.Exec(sb.String())
}
// Remove 删除网络

View File

@@ -89,7 +89,7 @@ func (r *containerVolumeRepo) Create(req *request.ContainerVolumeCreate) (string
sb.WriteString(fmt.Sprintf(" --opt %s=%s", option.Key, option.Value))
}
return shell.ExecfWithTimeout(120*time.Second, sb.String()) // nolint: govet
return shell.Exec(sb.String())
}
// Remove 删除存储卷

View File

@@ -2,7 +2,9 @@ package queuejob
import (
"errors"
"log/slog"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/pkg/shell"
)
@@ -36,7 +38,7 @@ func (r *ProcessTask) Handle(args ...any) error {
return err
}
if _, err = shell.Execf(task.Shell); err != nil { // nolint: govet
if _, err = shell.Exec(task.Shell); err != nil {
return err
}
@@ -48,5 +50,6 @@ func (r *ProcessTask) Handle(args ...any) error {
}
func (r *ProcessTask) ErrHandle(err error) {
app.Logger.Warn("background task failed", slog.Any("task_id", r.taskID), slog.Any("err", err))
_ = r.taskRepo.UpdateStatus(r.taskID, biz.TaskStatusFailed)
}

View File

@@ -162,8 +162,26 @@ func (s *FileService) Upload(w http.ResponseWriter, r *http.Request) {
Success(w, nil)
}
func (s *FileService) Exist(w http.ResponseWriter, r *http.Request) {
binder := chix.NewBind(r)
defer binder.Release()
var paths []string
if err := binder.Body(&paths); err != nil {
Error(w, http.StatusInternalServerError, "%v", err)
return
}
var results []bool
for item := range slices.Values(paths) {
results = append(results, io.Exists(item))
}
Success(w, results)
}
func (s *FileService) Move(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.FileMove](r)
req, err := Bind[request.FileControl](r)
if err != nil {
Error(w, http.StatusInternalServerError, "%v", err)
return
@@ -183,7 +201,7 @@ func (s *FileService) Move(w http.ResponseWriter, r *http.Request) {
}
func (s *FileService) Copy(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.FileCopy](r)
req, err := Bind[request.FileControl](r)
if err != nil {
Error(w, http.StatusInternalServerError, "%v", err)
return

View File

@@ -266,7 +266,7 @@ func (r *Firewall) Forward(rule Forward, operation Operation) error {
}
ruleBuilder.WriteString(" --permanent")
_, err := shell.Execf(ruleBuilder.String()) // nolint: govet
_, err := shell.Exec(ruleBuilder.String())
if err != nil {
return err
}

View File

@@ -16,7 +16,23 @@ import (
"github.com/creack/pty"
)
// Execf 执行 shell 命令
// Exec 执行 shell 命令
func Exec(shell string) (string, error) {
_ = os.Setenv("LC_ALL", "C")
cmd := exec.Command("bash", "-c", shell)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return strings.TrimSpace(stdout.String()), fmt.Errorf("run %s failed, err: %s", shell, strings.TrimSpace(stderr.String()))
}
return strings.TrimSpace(stdout.String()), nil
}
// Execf 安全执行 shell 命令
func Execf(shell string, args ...any) (string, error) {
if !preCheckArg(args) {
return "", errors.New("command contains illegal characters")

View File

@@ -0,0 +1,398 @@
<script setup lang="ts">
import Editor from '@guolao/vue-monaco-editor'
import { NButton, NDataTable, NPopconfirm } from 'naive-ui'
import php from '@/api/apps/php'
import systemctl from '@/api/panel/systemctl'
import { renderIcon } from '@/utils'
const props = defineProps({
version: Number
})
const { version } = toRefs(props)
const currentTab = ref('status')
const status = ref(false)
const isEnabled = ref(false)
const config = ref('')
const fpmConfig = ref('')
const errorLog = ref('')
const slowLog = ref('')
const statusType = computed(() => {
return status.value ? 'success' : 'error'
})
const statusStr = computed(() => {
return status.value ? '正常运行中' : '已停止运行'
})
const extensionColumns: any = [
{
title: '拓展名',
key: 'name',
minWidth: 250,
resizable: true,
ellipsis: { tooltip: true }
},
{
title: '描述',
key: 'description',
resizable: true,
minWidth: 250,
ellipsis: { tooltip: true }
},
{
title: '操作',
key: 'actions',
width: 240,
align: 'center',
hideInExcel: true,
render(row: any) {
return [
!row.installed
? h(
NButton,
{
size: 'small',
type: 'primary',
secondary: true,
onClick: () => handleInstallExtension(row.slug)
},
{
default: () => '安装',
icon: renderIcon('material-symbols:download-rounded', { size: 14 })
}
)
: null,
row.installed
? h(
NPopconfirm,
{
onPositiveClick: () => handleUninstallExtension(row.slug)
},
{
default: () => {
return '确定卸载' + row.name + '吗?'
},
trigger: () => {
return h(
NButton,
{
size: 'small',
type: 'error'
},
{
default: () => '删除',
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
}
)
}
}
)
: null
]
}
}
]
const loadColumns: any = [
{
title: '属性',
key: 'name',
minWidth: 200,
resizable: true,
ellipsis: { tooltip: true }
},
{
title: '当前值',
key: 'value',
minWidth: 200,
ellipsis: { tooltip: true }
}
]
const extensions = ref<any[]>([])
const load = ref<any[]>([])
const getLoad = async () => {
const { data } = await php.load(version.value)
return data
}
const getExtensions = async () => {
const { data } = await php.extensions(version.value)
return data
}
const getStatus = async () => {
await systemctl.status(`php-fpm-${version.value}`).then((res: any) => {
status.value = res.data
})
}
const getIsEnabled = async () => {
await systemctl.isEnabled(`php-fpm-${version.value}`).then((res: any) => {
isEnabled.value = res.data
})
}
const getErrorLog = async () => {
php.errorLog(version.value).then((res: any) => {
errorLog.value = res.data
})
}
const getSlowLog = async () => {
php.slowLog(version.value).then((res: any) => {
slowLog.value = res.data
})
}
const getConfig = async () => {
php.config(version.value).then((res: any) => {
config.value = res.data
})
}
const getFPMConfig = async () => {
php.fpmConfig(version.value).then((res: any) => {
fpmConfig.value = res.data
})
}
const handleSaveConfig = async () => {
await php.saveConfig(version.value, config.value)
window.$message.success('保存成功')
}
const handleSaveFPMConfig = async () => {
await php.saveFPMConfig(version.value, fpmConfig.value)
window.$message.success('保存成功')
await getFPMConfig()
}
const handleClearErrorLog = async () => {
await php.clearErrorLog(version.value)
window.$message.success('清空成功')
}
const handleClearSlowLog = async () => {
await php.clearSlowLog(version.value)
window.$message.success('清空成功')
}
const handleIsEnabled = async () => {
if (isEnabled.value) {
await systemctl.enable(`php-fpm-${version.value}`)
window.$message.success('开启自启动成功')
} else {
await systemctl.disable(`php-fpm-${version.value}`)
window.$message.success('禁用自启动成功')
}
await getIsEnabled()
}
const handleStart = async () => {
await systemctl.start(`php-fpm-${version.value}`)
window.$message.success('启动成功')
await getStatus()
}
const handleStop = async () => {
await systemctl.stop(`php-fpm-${version.value}`)
window.$message.success('停止成功')
await getStatus()
}
const handleRestart = async () => {
await systemctl.restart(`php-fpm-${version.value}`)
window.$message.success('重启成功')
await getStatus()
}
const handleReload = async () => {
await systemctl.reload(`php-fpm-${version.value}`)
window.$message.success('重载成功')
await getStatus()
}
const handleInstallExtension = async (slug: string) => {
await php.installExtension(version.value, slug).then(() => {
window.$message.success('任务已提交,请稍后查看任务进度')
})
}
const handleUninstallExtension = async (name: string) => {
await php.uninstallExtension(version.value, name).then(() => {
window.$message.success('任务已提交,请稍后查看任务进度')
})
}
onMounted(() => {
getStatus()
getIsEnabled()
getExtensions().then((res) => {
extensions.value = res
})
getLoad().then((res) => {
load.value = res
})
getErrorLog()
getSlowLog()
getConfig()
getFPMConfig()
})
</script>
<template>
<common-page show-footer>
<template #action>
<n-button
v-if="currentTab == 'config'"
class="ml-16"
type="primary"
@click="handleSaveConfig"
>
<TheIcon :size="18" icon="material-symbols:save-outline" />
保存
</n-button>
<n-button
v-if="currentTab == 'fpm-config'"
class="ml-16"
type="primary"
@click="handleSaveFPMConfig"
>
<TheIcon :size="18" icon="material-symbols:save-outline" />
保存
</n-button>
<n-button
v-if="currentTab == 'error-log'"
class="ml-16"
type="primary"
@click="handleClearErrorLog"
>
<TheIcon :size="18" icon="material-symbols:delete-outline" />
清空错误日志
</n-button>
<n-button
v-if="currentTab == 'slow-log'"
class="ml-16"
type="primary"
@click="handleClearSlowLog"
>
<TheIcon :size="18" icon="material-symbols:delete-outline" />
清空慢日志
</n-button>
</template>
<n-tabs v-model:value="currentTab" type="line" animated>
<n-tab-pane name="status" tab="运行状态">
<n-space vertical>
<n-card title="运行状态" rounded-10>
<template #header-extra>
<n-switch v-model:value="isEnabled" @update:value="handleIsEnabled">
<template #checked> 自启动开 </template>
<template #unchecked> 自启动关 </template>
</n-switch>
</template>
<n-space vertical>
<n-alert :type="statusType">
{{ statusStr }}
</n-alert>
<n-space>
<n-button type="success" @click="handleStart">
<TheIcon :size="24" icon="material-symbols:play-arrow-outline-rounded" />
启动
</n-button>
<n-popconfirm @positive-click="handleStop">
<template #trigger>
<n-button type="error">
<TheIcon :size="24" icon="material-symbols:stop-outline-rounded" />
停止
</n-button>
</template>
停止 PHP {{ version }} 会导致使用 PHP {{ version }} 的网站无法访问确定要停止吗
</n-popconfirm>
<n-button type="warning" @click="handleRestart">
<TheIcon :size="18" icon="material-symbols:replay-rounded" />
重启
</n-button>
<n-button type="primary" @click="handleReload">
<TheIcon :size="20" icon="material-symbols:refresh-rounded" />
重载
</n-button>
</n-space>
</n-space>
</n-card>
</n-space>
</n-tab-pane>
<n-tab-pane name="extensions" tab="拓展管理">
<n-card title="拓展列表" :segmented="true" rounded-10>
<n-data-table
striped
remote
:scroll-x="1000"
:loading="false"
:columns="extensionColumns"
:data="extensions"
:row-key="(row: any) => row.slug"
/>
</n-card>
</n-tab-pane>
<n-tab-pane name="config" tab="主配置">
<n-space vertical>
<n-alert type="warning">
此处修改的是 PHP {{ version }} 主配置文件,如果您不了解各参数的含义,请不要随意修改!
</n-alert>
<Editor
v-model:value="config"
language="ini"
theme="vs-dark"
height="60vh"
mt-8
:options="{
automaticLayout: true,
formatOnType: true,
formatOnPaste: true
}"
/>
</n-space>
</n-tab-pane>
<n-tab-pane name="fpm-config" tab="FPM 配置">
<n-space vertical>
<n-alert type="warning">
此处修改的是 PHP {{ version }} FPM 配置文件,如果您不了解各参数的含义,请不要随意修改!
</n-alert>
<Editor
v-model:value="fpmConfig"
language="ini"
theme="vs-dark"
height="60vh"
mt-8
:options="{
automaticLayout: true,
formatOnType: true,
formatOnPaste: true
}"
/>
</n-space>
</n-tab-pane>
<n-tab-pane name="load" tab="负载状态">
<n-data-table
striped
remote
:scroll-x="400"
:loading="false"
:columns="loadColumns"
:data="load"
/>
</n-tab-pane>
<n-tab-pane name="error-log" tab="错误日志">
<realtime-log :path="errorLog" />
</n-tab-pane>
<n-tab-pane name="slow-log" tab="慢日志">
<realtime-log :path="slowLog" />
</n-tab-pane>
</n-tabs>
</common-page>
</template>

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
import PhpView from '@/views/apps/php/PhpView.vue'
defineOptions({
name: 'apps-php74-index'
})
</script>
<template>
<php-view :version="74" />
</template>

View File

@@ -0,0 +1,23 @@
import type { RouteType } from '~/types/router'
const Layout = () => import('@/layout/IndexView.vue')
export default {
name: 'php74',
path: '/apps/php74',
component: Layout,
isHidden: true,
children: [
{
name: 'apps-php74-index',
path: '',
component: () => import('./IndexView.vue'),
meta: {
title: 'PHP 7.4',
icon: 'logos:php',
role: ['admin'],
requireAuth: true
}
}
]
} as RouteType

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
import PhpView from '@/views/apps/php/PhpView.vue'
defineOptions({
name: 'apps-php80-index'
})
</script>
<template>
<php-view :version="80" />
</template>

View File

@@ -0,0 +1,23 @@
import type { RouteType } from '~/types/router'
const Layout = () => import('@/layout/IndexView.vue')
export default {
name: 'php80',
path: '/apps/php80',
component: Layout,
isHidden: true,
children: [
{
name: 'apps-php80-index',
path: '',
component: () => import('./IndexView.vue'),
meta: {
title: 'PHP 8.0',
icon: 'logos:php',
role: ['admin'],
requireAuth: true
}
}
]
} as RouteType

View File

@@ -1,397 +1,11 @@
<script setup lang="ts">
import PhpView from '@/views/apps/php/PhpView.vue'
defineOptions({
name: 'apps-php81-index'
})
import Editor from '@guolao/vue-monaco-editor'
import { NButton, NDataTable, NPopconfirm } from 'naive-ui'
import php from '@/api/apps/php'
import systemctl from '@/api/panel/systemctl'
import { renderIcon } from '@/utils'
const currentTab = ref('status')
const version = Number(81)
const status = ref(false)
const isEnabled = ref(false)
const config = ref('')
const fpmConfig = ref('')
const errorLog = ref('')
const slowLog = ref('')
const statusType = computed(() => {
return status.value ? 'success' : 'error'
})
const statusStr = computed(() => {
return status.value ? '正常运行中' : '已停止运行'
})
const extensionColumns: any = [
{
title: '拓展名',
key: 'name',
minWidth: 250,
resizable: true,
ellipsis: { tooltip: true }
},
{
title: '描述',
key: 'description',
resizable: true,
minWidth: 250,
ellipsis: { tooltip: true }
},
{
title: '操作',
key: 'actions',
width: 240,
align: 'center',
hideInExcel: true,
render(row: any) {
return [
!row.installed
? h(
NButton,
{
size: 'small',
type: 'primary',
secondary: true,
onClick: () => handleInstallExtension(row.slug)
},
{
default: () => '安装',
icon: renderIcon('material-symbols:download-rounded', { size: 14 })
}
)
: null,
row.installed
? h(
NPopconfirm,
{
onPositiveClick: () => handleUninstallExtension(row.slug)
},
{
default: () => {
return '确定卸载' + row.name + '吗?'
},
trigger: () => {
return h(
NButton,
{
size: 'small',
type: 'error'
},
{
default: () => '删除',
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
}
)
}
}
)
: null
]
}
}
]
const loadColumns: any = [
{
title: '属性',
key: 'name',
minWidth: 200,
resizable: true,
ellipsis: { tooltip: true }
},
{
title: '当前值',
key: 'value',
minWidth: 200,
ellipsis: { tooltip: true }
}
]
const extensions = ref<any[]>([])
const load = ref<any[]>([])
const getLoad = async () => {
const { data } = await php.load(version)
return data
}
const getExtensions = async () => {
const { data } = await php.extensions(version)
return data
}
const getStatus = async () => {
await systemctl.status(`php-fpm-${version}`).then((res: any) => {
status.value = res.data
})
}
const getIsEnabled = async () => {
await systemctl.isEnabled(`php-fpm-${version}`).then((res: any) => {
isEnabled.value = res.data
})
}
const getErrorLog = async () => {
php.errorLog(version).then((res: any) => {
errorLog.value = res.data
})
}
const getSlowLog = async () => {
php.slowLog(version).then((res: any) => {
slowLog.value = res.data
})
}
const getConfig = async () => {
php.config(version).then((res: any) => {
config.value = res.data
})
}
const getFPMConfig = async () => {
php.fpmConfig(version).then((res: any) => {
fpmConfig.value = res.data
})
}
const handleSaveConfig = async () => {
await php.saveConfig(version, config.value)
window.$message.success('保存成功')
}
const handleSaveFPMConfig = async () => {
await php.saveFPMConfig(version, fpmConfig.value)
window.$message.success('保存成功')
await getFPMConfig()
}
const handleClearErrorLog = async () => {
await php.clearErrorLog(version)
window.$message.success('清空成功')
}
const handleClearSlowLog = async () => {
await php.clearSlowLog(version)
window.$message.success('清空成功')
}
const handleIsEnabled = async () => {
if (isEnabled.value) {
await systemctl.enable(`php-fpm-${version}`)
window.$message.success('开启自启动成功')
} else {
await systemctl.disable(`php-fpm-${version}`)
window.$message.success('禁用自启动成功')
}
await getIsEnabled()
}
const handleStart = async () => {
await systemctl.start(`php-fpm-${version}`)
window.$message.success('启动成功')
await getStatus()
}
const handleStop = async () => {
await systemctl.stop(`php-fpm-${version}`)
window.$message.success('停止成功')
await getStatus()
}
const handleRestart = async () => {
await systemctl.restart(`php-fpm-${version}`)
window.$message.success('重启成功')
await getStatus()
}
const handleReload = async () => {
await systemctl.reload(`php-fpm-${version}`)
window.$message.success('重载成功')
await getStatus()
}
const handleInstallExtension = async (slug: string) => {
await php.installExtension(version, slug).then(() => {
window.$message.success('任务已提交,请稍后查看任务进度')
})
}
const handleUninstallExtension = async (name: string) => {
await php.uninstallExtension(version, name).then(() => {
window.$message.success('任务已提交,请稍后查看任务进度')
})
}
onMounted(() => {
getStatus()
getIsEnabled()
getExtensions().then((res) => {
extensions.value = res
})
getLoad().then((res) => {
load.value = res
})
getErrorLog()
getSlowLog()
getConfig()
getFPMConfig()
})
</script>
<template>
<common-page show-footer>
<template #action>
<n-button
v-if="currentTab == 'config'"
class="ml-16"
type="primary"
@click="handleSaveConfig"
>
<TheIcon :size="18" icon="material-symbols:save-outline" />
保存
</n-button>
<n-button
v-if="currentTab == 'fpm-config'"
class="ml-16"
type="primary"
@click="handleSaveFPMConfig"
>
<TheIcon :size="18" icon="material-symbols:save-outline" />
保存
</n-button>
<n-button
v-if="currentTab == 'error-log'"
class="ml-16"
type="primary"
@click="handleClearErrorLog"
>
<TheIcon :size="18" icon="material-symbols:delete-outline" />
清空错误日志
</n-button>
<n-button
v-if="currentTab == 'slow-log'"
class="ml-16"
type="primary"
@click="handleClearSlowLog"
>
<TheIcon :size="18" icon="material-symbols:delete-outline" />
清空慢日志
</n-button>
</template>
<n-tabs v-model:value="currentTab" type="line" animated>
<n-tab-pane name="status" tab="运行状态">
<n-space vertical>
<n-card title="运行状态" rounded-10>
<template #header-extra>
<n-switch v-model:value="isEnabled" @update:value="handleIsEnabled">
<template #checked> 自启动开 </template>
<template #unchecked> 自启动关 </template>
</n-switch>
</template>
<n-space vertical>
<n-alert :type="statusType">
{{ statusStr }}
</n-alert>
<n-space>
<n-button type="success" @click="handleStart">
<TheIcon :size="24" icon="material-symbols:play-arrow-outline-rounded" />
启动
</n-button>
<n-popconfirm @positive-click="handleStop">
<template #trigger>
<n-button type="error">
<TheIcon :size="24" icon="material-symbols:stop-outline-rounded" />
停止
</n-button>
</template>
停止 PHP {{ version }} 会导致使用 PHP {{ version }} 的网站无法访问确定要停止吗
</n-popconfirm>
<n-button type="warning" @click="handleRestart">
<TheIcon :size="18" icon="material-symbols:replay-rounded" />
重启
</n-button>
<n-button type="primary" @click="handleReload">
<TheIcon :size="20" icon="material-symbols:refresh-rounded" />
重载
</n-button>
</n-space>
</n-space>
</n-card>
</n-space>
</n-tab-pane>
<n-tab-pane name="extensions" tab="拓展管理">
<n-card title="拓展列表" :segmented="true" rounded-10>
<n-data-table
striped
remote
:scroll-x="1000"
:loading="false"
:columns="extensionColumns"
:data="extensions"
:row-key="(row: any) => row.slug"
/>
</n-card>
</n-tab-pane>
<n-tab-pane name="config" tab="主配置">
<n-space vertical>
<n-alert type="warning">
此处修改的是 PHP {{ version }} 主配置文件,如果您不了解各参数的含义,请不要随意修改!
</n-alert>
<Editor
v-model:value="config"
language="ini"
theme="vs-dark"
height="60vh"
mt-8
:options="{
automaticLayout: true,
formatOnType: true,
formatOnPaste: true
}"
/>
</n-space>
</n-tab-pane>
<n-tab-pane name="fpm-config" tab="FPM 配置">
<n-space vertical>
<n-alert type="warning">
此处修改的是 PHP {{ version }} FPM 配置文件,如果您不了解各参数的含义,请不要随意修改!
</n-alert>
<Editor
v-model:value="fpmConfig"
language="ini"
theme="vs-dark"
height="60vh"
mt-8
:options="{
automaticLayout: true,
formatOnType: true,
formatOnPaste: true
}"
/>
</n-space>
</n-tab-pane>
<n-tab-pane name="load" tab="负载状态">
<n-data-table
striped
remote
:scroll-x="400"
:loading="false"
:columns="loadColumns"
:data="load"
/>
</n-tab-pane>
<n-tab-pane name="error-log" tab="错误日志">
<realtime-log :path="errorLog" />
</n-tab-pane>
<n-tab-pane name="slow-log" tab="慢日志">
<realtime-log :path="slowLog" />
</n-tab-pane>
</n-tabs>
</common-page>
<php-view :version="81" />
</template>

View File

@@ -1,397 +1,11 @@
<script setup lang="ts">
import PhpView from '@/views/apps/php/PhpView.vue'
defineOptions({
name: 'apps-php82-index'
})
import Editor from '@guolao/vue-monaco-editor'
import { NButton, NDataTable, NPopconfirm } from 'naive-ui'
import php from '@/api/apps/php'
import systemctl from '@/api/panel/systemctl'
import { renderIcon } from '@/utils'
const currentTab = ref('status')
const version = Number(82)
const status = ref(false)
const isEnabled = ref(false)
const config = ref('')
const fpmConfig = ref('')
const errorLog = ref('')
const slowLog = ref('')
const statusType = computed(() => {
return status.value ? 'success' : 'error'
})
const statusStr = computed(() => {
return status.value ? '正常运行中' : '已停止运行'
})
const extensionColumns: any = [
{
title: '拓展名',
key: 'name',
minWidth: 250,
resizable: true,
ellipsis: { tooltip: true }
},
{
title: '描述',
key: 'description',
resizable: true,
minWidth: 250,
ellipsis: { tooltip: true }
},
{
title: '操作',
key: 'actions',
width: 240,
align: 'center',
hideInExcel: true,
render(row: any) {
return [
!row.installed
? h(
NButton,
{
size: 'small',
type: 'primary',
secondary: true,
onClick: () => handleInstallExtension(row.slug)
},
{
default: () => '安装',
icon: renderIcon('material-symbols:download-rounded', { size: 14 })
}
)
: null,
row.installed
? h(
NPopconfirm,
{
onPositiveClick: () => handleUninstallExtension(row.slug)
},
{
default: () => {
return '确定卸载' + row.name + '吗?'
},
trigger: () => {
return h(
NButton,
{
size: 'small',
type: 'error'
},
{
default: () => '删除',
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
}
)
}
}
)
: null
]
}
}
]
const loadColumns: any = [
{
title: '属性',
key: 'name',
minWidth: 200,
resizable: true,
ellipsis: { tooltip: true }
},
{
title: '当前值',
key: 'value',
minWidth: 200,
ellipsis: { tooltip: true }
}
]
const extensions = ref<any[]>([])
const load = ref<any[]>([])
const getLoad = async () => {
const { data } = await php.load(version)
return data
}
const getExtensions = async () => {
const { data } = await php.extensions(version)
return data
}
const getStatus = async () => {
await systemctl.status(`php-fpm-${version}`).then((res: any) => {
status.value = res.data
})
}
const getIsEnabled = async () => {
await systemctl.isEnabled(`php-fpm-${version}`).then((res: any) => {
isEnabled.value = res.data
})
}
const getErrorLog = async () => {
php.errorLog(version).then((res: any) => {
errorLog.value = res.data
})
}
const getSlowLog = async () => {
php.slowLog(version).then((res: any) => {
slowLog.value = res.data
})
}
const getConfig = async () => {
php.config(version).then((res: any) => {
config.value = res.data
})
}
const getFPMConfig = async () => {
php.fpmConfig(version).then((res: any) => {
fpmConfig.value = res.data
})
}
const handleSaveConfig = async () => {
await php.saveConfig(version, config.value)
window.$message.success('保存成功')
}
const handleSaveFPMConfig = async () => {
await php.saveFPMConfig(version, fpmConfig.value)
window.$message.success('保存成功')
await getFPMConfig()
}
const handleClearErrorLog = async () => {
await php.clearErrorLog(version)
window.$message.success('清空成功')
}
const handleClearSlowLog = async () => {
await php.clearSlowLog(version)
window.$message.success('清空成功')
}
const handleIsEnabled = async () => {
if (isEnabled.value) {
await systemctl.enable(`php-fpm-${version}`)
window.$message.success('开启自启动成功')
} else {
await systemctl.disable(`php-fpm-${version}`)
window.$message.success('禁用自启动成功')
}
await getIsEnabled()
}
const handleStart = async () => {
await systemctl.start(`php-fpm-${version}`)
window.$message.success('启动成功')
await getStatus()
}
const handleStop = async () => {
await systemctl.stop(`php-fpm-${version}`)
window.$message.success('停止成功')
await getStatus()
}
const handleRestart = async () => {
await systemctl.restart(`php-fpm-${version}`)
window.$message.success('重启成功')
await getStatus()
}
const handleReload = async () => {
await systemctl.reload(`php-fpm-${version}`)
window.$message.success('重载成功')
await getStatus()
}
const handleInstallExtension = async (slug: string) => {
await php.installExtension(version, slug).then(() => {
window.$message.success('任务已提交,请稍后查看任务进度')
})
}
const handleUninstallExtension = async (name: string) => {
await php.uninstallExtension(version, name).then(() => {
window.$message.success('任务已提交,请稍后查看任务进度')
})
}
onMounted(() => {
getStatus()
getIsEnabled()
getExtensions().then((res) => {
extensions.value = res
})
getLoad().then((res) => {
load.value = res
})
getErrorLog()
getSlowLog()
getConfig()
getFPMConfig()
})
</script>
<template>
<common-page show-footer>
<template #action>
<n-button
v-if="currentTab == 'config'"
class="ml-16"
type="primary"
@click="handleSaveConfig"
>
<TheIcon :size="18" icon="material-symbols:save-outline" />
保存
</n-button>
<n-button
v-if="currentTab == 'fpm-config'"
class="ml-16"
type="primary"
@click="handleSaveFPMConfig"
>
<TheIcon :size="18" icon="material-symbols:save-outline" />
保存
</n-button>
<n-button
v-if="currentTab == 'error-log'"
class="ml-16"
type="primary"
@click="handleClearErrorLog"
>
<TheIcon :size="18" icon="material-symbols:delete-outline" />
清空错误日志
</n-button>
<n-button
v-if="currentTab == 'slow-log'"
class="ml-16"
type="primary"
@click="handleClearSlowLog"
>
<TheIcon :size="18" icon="material-symbols:delete-outline" />
清空慢日志
</n-button>
</template>
<n-tabs v-model:value="currentTab" type="line" animated>
<n-tab-pane name="status" tab="运行状态">
<n-space vertical>
<n-card title="运行状态" rounded-10>
<template #header-extra>
<n-switch v-model:value="isEnabled" @update:value="handleIsEnabled">
<template #checked> 自启动开 </template>
<template #unchecked> 自启动关 </template>
</n-switch>
</template>
<n-space vertical>
<n-alert :type="statusType">
{{ statusStr }}
</n-alert>
<n-space>
<n-button type="success" @click="handleStart">
<TheIcon :size="24" icon="material-symbols:play-arrow-outline-rounded" />
启动
</n-button>
<n-popconfirm @positive-click="handleStop">
<template #trigger>
<n-button type="error">
<TheIcon :size="24" icon="material-symbols:stop-outline-rounded" />
停止
</n-button>
</template>
停止 PHP {{ version }} 会导致使用 PHP {{ version }} 的网站无法访问确定要停止吗
</n-popconfirm>
<n-button type="warning" @click="handleRestart">
<TheIcon :size="18" icon="material-symbols:replay-rounded" />
重启
</n-button>
<n-button type="primary" @click="handleReload">
<TheIcon :size="20" icon="material-symbols:refresh-rounded" />
重载
</n-button>
</n-space>
</n-space>
</n-card>
</n-space>
</n-tab-pane>
<n-tab-pane name="extensions" tab="拓展管理">
<n-card title="拓展列表" :segmented="true" rounded-10>
<n-data-table
striped
remote
:scroll-x="1000"
:loading="false"
:columns="extensionColumns"
:data="extensions"
:row-key="(row: any) => row.slug"
/>
</n-card>
</n-tab-pane>
<n-tab-pane name="config" tab="主配置">
<n-space vertical>
<n-alert type="warning">
此处修改的是 PHP {{ version }} 主配置文件,如果您不了解各参数的含义,请不要随意修改!
</n-alert>
<Editor
v-model:value="config"
language="ini"
theme="vs-dark"
height="60vh"
mt-8
:options="{
automaticLayout: true,
formatOnType: true,
formatOnPaste: true
}"
/>
</n-space>
</n-tab-pane>
<n-tab-pane name="fpm-config" tab="FPM 配置">
<n-space vertical>
<n-alert type="warning">
此处修改的是 PHP {{ version }} FPM 配置文件,如果您不了解各参数的含义,请不要随意修改!
</n-alert>
<Editor
v-model:value="fpmConfig"
language="ini"
theme="vs-dark"
height="60vh"
mt-8
:options="{
automaticLayout: true,
formatOnType: true,
formatOnPaste: true
}"
/>
</n-space>
</n-tab-pane>
<n-tab-pane name="load" tab="负载状态">
<n-data-table
striped
remote
:scroll-x="400"
:loading="false"
:columns="loadColumns"
:data="load"
/>
</n-tab-pane>
<n-tab-pane name="error-log" tab="错误日志">
<realtime-log :path="errorLog" />
</n-tab-pane>
<n-tab-pane name="slow-log" tab="慢日志">
<realtime-log :path="slowLog" />
</n-tab-pane>
</n-tabs>
</common-page>
<php-view :version="82" />
</template>

View File

@@ -1,397 +1,11 @@
<script setup lang="ts">
import PhpView from '@/views/apps/php/PhpView.vue'
defineOptions({
name: 'apps-php83-index'
})
import Editor from '@guolao/vue-monaco-editor'
import { NButton, NDataTable, NPopconfirm } from 'naive-ui'
import php from '@/api/apps/php'
import systemctl from '@/api/panel/systemctl'
import { renderIcon } from '@/utils'
const currentTab = ref('status')
const version = Number(83)
const status = ref(false)
const isEnabled = ref(false)
const config = ref('')
const fpmConfig = ref('')
const errorLog = ref('')
const slowLog = ref('')
const statusType = computed(() => {
return status.value ? 'success' : 'error'
})
const statusStr = computed(() => {
return status.value ? '正常运行中' : '已停止运行'
})
const extensionColumns: any = [
{
title: '拓展名',
key: 'name',
minWidth: 250,
resizable: true,
ellipsis: { tooltip: true }
},
{
title: '描述',
key: 'description',
resizable: true,
minWidth: 250,
ellipsis: { tooltip: true }
},
{
title: '操作',
key: 'actions',
width: 240,
align: 'center',
hideInExcel: true,
render(row: any) {
return [
!row.installed
? h(
NButton,
{
size: 'small',
type: 'primary',
secondary: true,
onClick: () => handleInstallExtension(row.slug)
},
{
default: () => '安装',
icon: renderIcon('material-symbols:download-rounded', { size: 14 })
}
)
: null,
row.installed
? h(
NPopconfirm,
{
onPositiveClick: () => handleUninstallExtension(row.slug)
},
{
default: () => {
return '确定卸载' + row.name + '吗?'
},
trigger: () => {
return h(
NButton,
{
size: 'small',
type: 'error'
},
{
default: () => '删除',
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
}
)
}
}
)
: null
]
}
}
]
const loadColumns: any = [
{
title: '属性',
key: 'name',
minWidth: 200,
resizable: true,
ellipsis: { tooltip: true }
},
{
title: '当前值',
key: 'value',
minWidth: 200,
ellipsis: { tooltip: true }
}
]
const extensions = ref<any[]>([])
const load = ref<any[]>([])
const getLoad = async () => {
const { data } = await php.load(version)
return data
}
const getExtensions = async () => {
const { data } = await php.extensions(version)
return data
}
const getStatus = async () => {
await systemctl.status(`php-fpm-${version}`).then((res: any) => {
status.value = res.data
})
}
const getIsEnabled = async () => {
await systemctl.isEnabled(`php-fpm-${version}`).then((res: any) => {
isEnabled.value = res.data
})
}
const getErrorLog = async () => {
php.errorLog(version).then((res: any) => {
errorLog.value = res.data
})
}
const getSlowLog = async () => {
php.slowLog(version).then((res: any) => {
slowLog.value = res.data
})
}
const getConfig = async () => {
php.config(version).then((res: any) => {
config.value = res.data
})
}
const getFPMConfig = async () => {
php.fpmConfig(version).then((res: any) => {
fpmConfig.value = res.data
})
}
const handleSaveConfig = async () => {
await php.saveConfig(version, config.value)
window.$message.success('保存成功')
}
const handleSaveFPMConfig = async () => {
await php.saveFPMConfig(version, fpmConfig.value)
window.$message.success('保存成功')
await getFPMConfig()
}
const handleClearErrorLog = async () => {
await php.clearErrorLog(version)
window.$message.success('清空成功')
}
const handleClearSlowLog = async () => {
await php.clearSlowLog(version)
window.$message.success('清空成功')
}
const handleIsEnabled = async () => {
if (isEnabled.value) {
await systemctl.enable(`php-fpm-${version}`)
window.$message.success('开启自启动成功')
} else {
await systemctl.disable(`php-fpm-${version}`)
window.$message.success('禁用自启动成功')
}
await getIsEnabled()
}
const handleStart = async () => {
await systemctl.start(`php-fpm-${version}`)
window.$message.success('启动成功')
await getStatus()
}
const handleStop = async () => {
await systemctl.stop(`php-fpm-${version}`)
window.$message.success('停止成功')
await getStatus()
}
const handleRestart = async () => {
await systemctl.restart(`php-fpm-${version}`)
window.$message.success('重启成功')
await getStatus()
}
const handleReload = async () => {
await systemctl.reload(`php-fpm-${version}`)
window.$message.success('重载成功')
await getStatus()
}
const handleInstallExtension = async (slug: string) => {
await php.installExtension(version, slug).then(() => {
window.$message.success('任务已提交,请稍后查看任务进度')
})
}
const handleUninstallExtension = async (name: string) => {
await php.uninstallExtension(version, name).then(() => {
window.$message.success('任务已提交,请稍后查看任务进度')
})
}
onMounted(() => {
getStatus()
getIsEnabled()
getExtensions().then((res) => {
extensions.value = res
})
getLoad().then((res) => {
load.value = res
})
getErrorLog()
getSlowLog()
getConfig()
getFPMConfig()
})
</script>
<template>
<common-page show-footer>
<template #action>
<n-button
v-if="currentTab == 'config'"
class="ml-16"
type="primary"
@click="handleSaveConfig"
>
<TheIcon :size="18" icon="material-symbols:save-outline" />
保存
</n-button>
<n-button
v-if="currentTab == 'fpm-config'"
class="ml-16"
type="primary"
@click="handleSaveFPMConfig"
>
<TheIcon :size="18" icon="material-symbols:save-outline" />
保存
</n-button>
<n-button
v-if="currentTab == 'error-log'"
class="ml-16"
type="primary"
@click="handleClearErrorLog"
>
<TheIcon :size="18" icon="material-symbols:delete-outline" />
清空错误日志
</n-button>
<n-button
v-if="currentTab == 'slow-log'"
class="ml-16"
type="primary"
@click="handleClearSlowLog"
>
<TheIcon :size="18" icon="material-symbols:delete-outline" />
清空慢日志
</n-button>
</template>
<n-tabs v-model:value="currentTab" type="line" animated>
<n-tab-pane name="status" tab="运行状态">
<n-space vertical>
<n-card title="运行状态" rounded-10>
<template #header-extra>
<n-switch v-model:value="isEnabled" @update:value="handleIsEnabled">
<template #checked> 自启动开 </template>
<template #unchecked> 自启动关 </template>
</n-switch>
</template>
<n-space vertical>
<n-alert :type="statusType">
{{ statusStr }}
</n-alert>
<n-space>
<n-button type="success" @click="handleStart">
<TheIcon :size="24" icon="material-symbols:play-arrow-outline-rounded" />
启动
</n-button>
<n-popconfirm @positive-click="handleStop">
<template #trigger>
<n-button type="error">
<TheIcon :size="24" icon="material-symbols:stop-outline-rounded" />
停止
</n-button>
</template>
停止 PHP {{ version }} 会导致使用 PHP {{ version }} 的网站无法访问确定要停止吗
</n-popconfirm>
<n-button type="warning" @click="handleRestart">
<TheIcon :size="18" icon="material-symbols:replay-rounded" />
重启
</n-button>
<n-button type="primary" @click="handleReload">
<TheIcon :size="20" icon="material-symbols:refresh-rounded" />
重载
</n-button>
</n-space>
</n-space>
</n-card>
</n-space>
</n-tab-pane>
<n-tab-pane name="extensions" tab="拓展管理">
<n-card title="拓展列表" :segmented="true" rounded-10>
<n-data-table
striped
remote
:scroll-x="1000"
:loading="false"
:columns="extensionColumns"
:data="extensions"
:row-key="(row: any) => row.slug"
/>
</n-card>
</n-tab-pane>
<n-tab-pane name="config" tab="主配置">
<n-space vertical>
<n-alert type="warning">
此处修改的是 PHP {{ version }} 主配置文件,如果您不了解各参数的含义,请不要随意修改!
</n-alert>
<Editor
v-model:value="config"
language="ini"
theme="vs-dark"
height="60vh"
mt-8
:options="{
automaticLayout: true,
formatOnType: true,
formatOnPaste: true
}"
/>
</n-space>
</n-tab-pane>
<n-tab-pane name="fpm-config" tab="FPM 配置">
<n-space vertical>
<n-alert type="warning">
此处修改的是 PHP {{ version }} FPM 配置文件,如果您不了解各参数的含义,请不要随意修改!
</n-alert>
<Editor
v-model:value="fpmConfig"
language="ini"
theme="vs-dark"
height="60vh"
mt-8
:options="{
automaticLayout: true,
formatOnType: true,
formatOnPaste: true
}"
/>
</n-space>
</n-tab-pane>
<n-tab-pane name="load" tab="负载状态">
<n-data-table
striped
remote
:scroll-x="400"
:loading="false"
:columns="loadColumns"
:data="load"
/>
</n-tab-pane>
<n-tab-pane name="error-log" tab="错误日志">
<realtime-log :path="errorLog" />
</n-tab-pane>
<n-tab-pane name="slow-log" tab="慢日志">
<realtime-log :path="slowLog" />
</n-tab-pane>
</n-tabs>
</common-page>
<php-view :version="83" />
</template>