diff --git a/app/http/controllers/plugins/redis/redis_controller.go b/app/http/controllers/plugins/redis/redis_controller.go new file mode 100644 index 00000000..257b700c --- /dev/null +++ b/app/http/controllers/plugins/redis/redis_controller.go @@ -0,0 +1,206 @@ +package redis + +import ( + "strings" + + "github.com/goravel/framework/contracts/http" + + "panel/app/http/controllers" + "panel/pkg/tools" +) + +type RedisController struct { +} + +type Info struct { + Name string `json:"name"` + Value string `json:"value"` +} + +func NewRedisController() *RedisController { + return &RedisController{} +} + +// Status 获取运行状态 +func (c *RedisController) Status(ctx http.Context) { + if !controllers.Check(ctx, "redis") { + return + } + + status := tools.ExecShell("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'") + if len(status) == 0 { + controllers.Error(ctx, http.StatusInternalServerError, "获取Redis状态失败") + return + } + + if status == "active" { + controllers.Success(ctx, true) + } else { + controllers.Success(ctx, false) + } +} + +// Reload 重载配置 +func (c *RedisController) Reload(ctx http.Context) { + if !controllers.Check(ctx, "redis") { + return + } + + tools.ExecShell("systemctl reload redis") + status := tools.ExecShell("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'") + if len(status) == 0 { + controllers.Error(ctx, http.StatusInternalServerError, "获取Redis状态失败") + return + } + + if status == "active" { + controllers.Success(ctx, true) + } else { + controllers.Success(ctx, false) + } +} + +// Restart 重启服务 +func (c *RedisController) Restart(ctx http.Context) { + if !controllers.Check(ctx, "redis") { + return + } + + tools.ExecShell("systemctl restart redis") + status := tools.ExecShell("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'") + if len(status) == 0 { + controllers.Error(ctx, http.StatusInternalServerError, "获取Redis状态失败") + return + } + + if status == "active" { + controllers.Success(ctx, true) + } else { + controllers.Success(ctx, false) + } +} + +// Start 启动服务 +func (c *RedisController) Start(ctx http.Context) { + if !controllers.Check(ctx, "redis") { + return + } + + tools.ExecShell("systemctl start redis") + status := tools.ExecShell("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'") + if len(status) == 0 { + controllers.Error(ctx, http.StatusInternalServerError, "获取Redis状态失败") + return + } + + if status == "active" { + controllers.Success(ctx, true) + } else { + controllers.Success(ctx, false) + } +} + +// Stop 停止服务 +func (c *RedisController) Stop(ctx http.Context) { + if !controllers.Check(ctx, "redis") { + return + } + + tools.ExecShell("systemctl stop redis") + status := tools.ExecShell("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'") + if len(status) == 0 { + controllers.Error(ctx, http.StatusInternalServerError, "获取Redis状态失败") + return + } + + if status != "active" { + controllers.Success(ctx, true) + } else { + controllers.Success(ctx, false) + } +} + +// GetConfig 获取配置 +func (c *RedisController) GetConfig(ctx http.Context) { + if !controllers.Check(ctx, "redis") { + return + } + + // 获取配置 + config := tools.ReadFile("/www/server/redis/redis.conf") + if len(config) == 0 { + controllers.Error(ctx, http.StatusInternalServerError, "获取Redis配置失败") + return + } + + controllers.Success(ctx, config) +} + +// SaveConfig 保存配置 +func (c *RedisController) SaveConfig(ctx http.Context) { + if !controllers.Check(ctx, "redis") { + return + } + + config := ctx.Request().Input("config") + if len(config) == 0 { + controllers.Error(ctx, http.StatusBadRequest, "配置不能为空") + return + } + + if !tools.WriteFile("/www/server/redis/redis.conf", config, 0644) { + controllers.Error(ctx, http.StatusInternalServerError, "写入Redis配置失败") + return + } + + tools.ExecShell("systemctl restart redis") + + controllers.Success(ctx, nil) +} + +// Load 获取负载 +func (c *RedisController) Load(ctx http.Context) { + if !controllers.Check(ctx, "redis") { + return + } + + status := tools.ExecShell("systemctl status redis | grep Active | grep -v grep | awk '{print $2}'") + if status != "active" { + controllers.Error(ctx, http.StatusInternalServerError, "Redis 已停止运行") + return + } + + raw := tools.ExecShell("redis-cli info") + if len(raw) == 0 { + controllers.Error(ctx, http.StatusInternalServerError, "获取Redis负载失败") + return + } + + infoLines := strings.Split(raw, "\n") + dataRaw := make(map[string]string) + + for _, item := range infoLines { + parts := strings.Split(item, ":") + if len(parts) == 2 { + dataRaw[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) + } + } + + data := []Info{ + {"TCP 端口", dataRaw["tcp_port"]}, + {"已运行天数", dataRaw["uptime_in_days"]}, + {"连接的客户端数", dataRaw["connected_clients"]}, + {"已分配的内存总量", dataRaw["used_memory_human"]}, + {"占用内存总量", dataRaw["used_memory_rss_human"]}, + {"占用内存峰值", dataRaw["used_memory_peak_human"]}, + {"内存碎片比率", dataRaw["mem_fragmentation_ratio"]}, + {"运行以来连接过的客户端的总数", dataRaw["total_connections_received"]}, + {"运行以来执行过的命令的总数", dataRaw["total_commands_processed"]}, + {"每秒执行的命令数", dataRaw["instantaneous_ops_per_sec"]}, + {"查找数据库键成功次数", dataRaw["keyspace_hits"]}, + {"查找数据库键失败次数", dataRaw["keyspace_misses"]}, + {"最近一次 fork() 操作耗费的毫秒数", dataRaw["latest_fork_usec"]}, + } + + controllers.Success(ctx, data) +} diff --git a/app/plugins/redis/redis.go b/app/plugins/redis/redis.go new file mode 100644 index 00000000..1b86d6b4 --- /dev/null +++ b/app/plugins/redis/redis.go @@ -0,0 +1,13 @@ +package redis + +var ( + Name = "Redis" + Description = "Redis 是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。" + Slug = "redis" + Version = "7.0.12" + Requires = []string{} + Excludes = []string{} + Install = `bash /www/panel/scripts/redis/install.sh` + Uninstall = `bash /www/panel/scripts/redis/uninstall.sh` + Update = `bash /www/panel/scripts/redis/update.sh` +) diff --git a/app/services/plugin.go b/app/services/plugin.go index 7e2522a2..9d8d38c7 100644 --- a/app/services/plugin.go +++ b/app/services/plugin.go @@ -15,6 +15,7 @@ import ( "panel/app/plugins/php82" "panel/app/plugins/phpmyadmin" "panel/app/plugins/pureftpd" + "panel/app/plugins/redis" "panel/app/plugins/s3fs" "panel/app/plugins/supervisor" ) @@ -159,6 +160,17 @@ func (r *PluginImpl) All() []PanelPlugin { Uninstall: pureftpd.Uninstall, Update: pureftpd.Update, }) + p = append(p, PanelPlugin{ + Name: redis.Name, + Description: redis.Description, + Slug: redis.Slug, + Version: redis.Version, + Requires: redis.Requires, + Excludes: redis.Excludes, + Install: redis.Install, + Uninstall: redis.Uninstall, + Update: redis.Update, + }) p = append(p, PanelPlugin{ Name: s3fs.Name, Description: s3fs.Description, diff --git a/public/panel/views/plugins/redis.html b/public/panel/views/plugins/redis.html new file mode 100644 index 00000000..77bb13b7 --- /dev/null +++ b/public/panel/views/plugins/redis.html @@ -0,0 +1,190 @@ + +Redis +
+
+
+
+
Redis 管理
+
+
+
    +
  • 运行状态
  • +
  • 配置修改
  • +
  • 负载状态
  • +
+
+
+
当前状态:获取中
+
+ + + + +
+
+
+
此处修改的是Redis主配置文件,如果你不了解各参数的含义,请不要随意修改!
+ 提示:Ctrl+F 搜索关键字,Ctrl+S 保存,Ctrl+H 查找替换! +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/routes/plugin.go b/routes/plugin.go index 1ede94a1..5b06b3de 100644 --- a/routes/plugin.go +++ b/routes/plugin.go @@ -14,6 +14,7 @@ import ( "panel/app/http/controllers/plugins/php82" "panel/app/http/controllers/plugins/phpmyadmin" "panel/app/http/controllers/plugins/pureftpd" + "panel/app/http/controllers/plugins/redis" "panel/app/http/controllers/plugins/s3fs" "panel/app/http/controllers/plugins/supervisor" "panel/app/http/middleware" @@ -185,6 +186,17 @@ func Plugin() { route.Get("port", pureFtpdController.GetPort) route.Post("port", pureFtpdController.SetPort) }) + facades.Route().Prefix("api/plugins/redis").Middleware(middleware.Jwt()).Group(func(route route.Route) { + redisController := redis.NewRedisController() + route.Get("status", redisController.Status) + route.Post("reload", redisController.Reload) + route.Post("start", redisController.Start) + route.Post("stop", redisController.Stop) + route.Post("restart", redisController.Restart) + route.Get("load", redisController.Load) + route.Get("config", redisController.GetConfig) + route.Post("config", redisController.SaveConfig) + }) facades.Route().Prefix("api/plugins/s3fs").Middleware(middleware.Jwt()).Group(func(route route.Route) { s3fsController := s3fs.NewS3fsController() route.Get("list", s3fsController.List) diff --git a/scripts/redis/install.sh b/scripts/redis/install.sh new file mode 100644 index 00000000..f1ebbf32 --- /dev/null +++ b/scripts/redis/install.sh @@ -0,0 +1,116 @@ +#!/bin/bash +export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH + +: ' +Copyright 2022 HaoZi Technology Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +' + +HR="+----------------------------------------------------" +ARCH=$(uname -m) +OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown") +downloadUrl="https://dl.cdn.haozi.net/panel/redis" +setupPath="/www" +redisPath="${setupPath}/server/redis" +redisVersion="7.0.12" +cpuCore=$(cat /proc/cpuinfo | grep "processor" | wc -l) + +if ! id -u "redis" > /dev/null 2>&1; then + groupadd redis + useradd -s /sbin/nologin -g redis redis +fi + +# 安装依赖 +if [ "${OS}" == "centos" ]; then + dnf makecache -y + dnf groupinstall "Development Tools" -y + dnf install systemd-devel openssl-devel -y +elif [ "${OS}" == "debian" ]; then + apt update + apt install build-essential libsystemd-dev libssl-dev -y +else + echo -e $HR + echo "错误:耗子Linux面板不支持该系统" + exit 1 +fi + +# 准备目录 +rm -rf ${redisPath} +mkdir -p ${redisPath} +cd ${redisPath} + +# 下载源码 +wget -T 120 -O ${redisPath}/redis-${redisVersion}.tar.gz ${downloadUrl}/redis-${redisVersion}.tar.gz +tar -zxvf redis-${redisVersion}.tar.gz +rm -f redis-${redisVersion}.tar.gz +mv redis-${redisVersion}/* ./ && rm -rf redis-${redisVersion} +mkdir -p ${redisPath}/bin + +make BUILD_TLS=yes USE_SYSTEMD=yes -j${cpuCore} +if [ "$?" != "0" ]; then + echo -e $HR + echo "错误:Redis编译失败,请截图错误信息寻求帮助。" + rm -rf ${redisPath} + exit 1 +fi +make PREFIX=${redisPath} install +if [ ! -f "${redisPath}/bin/redis-server" ]; then + echo -e $HR + echo "错误:Redis安装失败,请截图错误信息寻求帮助。" + rm -rf ${redisPath} + exit 1 +fi + +# 设置软链接 +ln -sf ${redisPath}/bin/redis-cli /usr/bin/redis-cli +ln -sf ${redisPath}/bin/redis-server /usr/bin/redis-server +ln -sf ${redisPath}/bin/redis-sentinel /usr/bin/redis-sentinel +ln -sf ${redisPath}/bin/redis-benchmark /usr/bin/redis-benchmark +ln -sf ${redisPath}/bin/redis-check-aof /usr/bin/redis-check-aof +ln -sf ${redisPath}/bin/redis-check-rdb /usr/bin/redis-check-rdb + +# 设置配置文件 +VM_OVERCOMMIT_MEMORY=$(cat /etc/sysctl.conf|grep vm.overcommit_memory) +NET_CORE_SOMAXCONN=$(cat /etc/sysctl.conf|grep net.core.somaxconn) +if [ -z "${VM_OVERCOMMIT_MEMORY}" ] && [ -z "${NET_CORE_SOMAXCONN}" ];then + echo "vm.overcommit_memory = 1" >> /etc/sysctl.conf + echo "net.core.somaxconn = 1024" >> /etc/sysctl.conf + sysctl -p +fi + +sed -i 's/dir .\//dir \/www\/server\/redis\//g' ${redisPath}/redis.conf +sed -i 's/# supervised.*/supervised systemd/g' ${redisPath}/redis.conf +sed -i 's/daemonize.*/daemonize no/g' ${redisPath}/redis.conf + +if [ "${ARCH}" == "aarch64" ]; then + echo "ignore-warnings ARM64-COW-BUG" >> ${redisPath}/redis.conf +fi + +chown -R redis:redis ${redisPath} +chmod -R 755 ${redisPath} + +# 设置服务 +cp -r utils/systemd-redis_server.service /etc/systemd/system/redis.service +sed -i "s!ExecStart=.*!ExecStart=${redisPath}/bin/redis-server ${redisPath}/redis.conf!g" /etc/systemd/system/redis.service +sed -i "s!#User=.*!User=redis!g" /etc/systemd/system/redis.service +sed -i "s!#Group=.*!Group=redis!g" /etc/systemd/system/redis.service +sed -i "s!#WorkingDirectory=.*!WorkingDirectory=${redisPath}!g" /etc/systemd/system/redis.service + +systemctl daemon-reload +systemctl enable redis +systemctl start redis + +panel writePlugin redis ${redisVersion} + +echo -e "${HR}\nRedis 安装完成\n${HR}" diff --git a/scripts/redis/uninstall.sh b/scripts/redis/uninstall.sh new file mode 100644 index 00000000..c9e42e0f --- /dev/null +++ b/scripts/redis/uninstall.sh @@ -0,0 +1,38 @@ +#!/bin/bash +export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH + +: ' +Copyright 2022 HaoZi Technology Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +' + +HR="+----------------------------------------------------" + +systemctl stop redis +systemctl disable redis +rm -rf /etc/systemd/system/redis.service +systemctl daemon-reload +pkill -9 redis +rm -rf /www/server/redis + +rm -rf /usr/bin/redis-cli +rm -rf /usr/bin/redis-server +rm -rf /usr/bin/redis-benchmark +rm -rf /usr/bin/redis-check-aof +rm -rf /usr/bin/redis-check-rdb +rm -rf /usr/bin/redis-sentinel + +panel deletePlugin redis + +echo -e "${HR}\nRedis 卸载完成\n${HR}" diff --git a/scripts/redis/update.sh b/scripts/redis/update.sh new file mode 100644 index 00000000..8558b37c --- /dev/null +++ b/scripts/redis/update.sh @@ -0,0 +1,86 @@ +#!/bin/bash +export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH + +: ' +Copyright 2022 HaoZi Technology Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +' + +HR="+----------------------------------------------------" +ARCH=$(uname -m) +OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown") +downloadUrl="https://dl.cdn.haozi.net/panel/redis" +setupPath="/www" +redisPath="${setupPath}/server/redis" +redisVersion="7.0.12" +cpuCore=$(cat /proc/cpuinfo | grep "processor" | wc -l) + +# 准备目录 +cd ${redisPath} + +# 下载源码 +wget -T 120 -O ${redisPath}/redis-${redisVersion}.tar.gz ${downloadUrl}/redis-${redisVersion}.tar.gz +tar -zxvf redis-${redisVersion}.tar.gz +rm -f redis-${redisVersion}.tar.gz +mv redis-${redisVersion}/* ./ && rm -rf redis-${redisVersion} +mkdir -p ${redisPath}/bin + +make BUILD_TLS=yes USE_SYSTEMD=yes -j${cpuCore} +if [ "$?" != "0" ]; then + echo -e $HR + echo "错误:Redis编译失败,请截图错误信息寻求帮助。" + rm -rf ${redisPath} + exit 1 +fi +make PREFIX=${redisPath} install +if [ ! -f "${redisPath}/bin/redis-server" ]; then + echo -e $HR + echo "错误:Redis安装失败,请截图错误信息寻求帮助。" + rm -rf ${redisPath} + exit 1 +fi + +# 设置软链接 +ln -sf ${redisPath}/bin/redis-cli /usr/bin/redis-cli +ln -sf ${redisPath}/bin/redis-server /usr/bin/redis-server +ln -sf ${redisPath}/bin/redis-sentinel /usr/bin/redis-sentinel +ln -sf ${redisPath}/bin/redis-benchmark /usr/bin/redis-benchmark +ln -sf ${redisPath}/bin/redis-check-aof /usr/bin/redis-check-aof +ln -sf ${redisPath}/bin/redis-check-rdb /usr/bin/redis-check-rdb + +# 设置配置文件 +sed -i 's/dir .\//dir \/www\/server\/redis\//g' ${redisPath}/redis.conf +sed -i 's/# supervised.*/supervised systemd/g' ${redisPath}/redis.conf +sed -i 's/daemonize.*/daemonize no/g' ${redisPath}/redis.conf + +if [ "${ARCH}" == "aarch64" ]; then + echo "ignore-warnings ARM64-COW-BUG" >> ${redisPath}/redis.conf +fi + +chown -R redis:redis ${redisPath} +chmod -R 755 ${redisPath} + +# 设置服务 +cp -r utils/systemd-redis_server.service /etc/systemd/system/redis.service +sed -i "s!ExecStart=.*!ExecStart=${redisPath}/bin/redis-server ${redisPath}/redis.conf!g" /etc/systemd/system/redis.service +sed -i "s!#User=.*!User=redis!g" /etc/systemd/system/redis.service +sed -i "s!#Group=.*!Group=redis!g" /etc/systemd/system/redis.service +sed -i "s!#WorkingDirectory=.*!WorkingDirectory=${redisPath}!g" /etc/systemd/system/redis.service + +systemctl daemon-reload +systemctl restart redis + +panel writePlugin redis ${redisVersion} + +echo -e "${HR}\nRedis 升级完成\n${HR}"