From e731c025b575296e4ba30325efdd87616be75edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Thu, 3 Aug 2023 23:33:31 +0800 Subject: [PATCH] feat: pure-ftpd plugin --- .gitignore | 8 +- .../plugins/mysql57/mysql57_controller.go | 42 ++- .../plugins/mysql80/mysql80_controller.go | 42 ++- .../plugins/pureftpd/pureftpd_controller.go | 313 ++++++++++++++++++ app/http/controllers/safe_controller.go | 40 ++- app/plugins/pureftpd/pureftpd.go | 13 + app/services/plugin.go | 12 + config/app.go | 2 +- public/panel/views/plugins/mysql57.html | 18 + public/panel/views/plugins/mysql80.html | 18 + public/panel/views/plugins/pureftpd.html | 305 +++++++++++++++++ .../views/plugins/pureftpd/add_user.html | 80 +++++ public/panel/views/safe.html | 11 +- routes/plugin.go | 15 + scripts/pureftpd/install.sh | 130 ++++++++ scripts/pureftpd/uninstall.sh | 31 ++ scripts/pureftpd/update.sh | 78 +++++ 17 files changed, 1145 insertions(+), 13 deletions(-) create mode 100644 app/http/controllers/plugins/pureftpd/pureftpd_controller.go create mode 100644 app/plugins/pureftpd/pureftpd.go create mode 100644 public/panel/views/plugins/pureftpd.html create mode 100644 public/panel/views/plugins/pureftpd/add_user.html create mode 100644 scripts/pureftpd/install.sh create mode 100644 scripts/pureftpd/uninstall.sh create mode 100644 scripts/pureftpd/update.sh diff --git a/.gitignore b/.gitignore index 11fbb8a3..d3740e75 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,6 @@ tmp -.env -.history -.air.toml -panel.conf +/.air.toml +/panel.conf # Golang # # `go test -c` 生成的二进制文件 @@ -23,7 +21,7 @@ _cgo_export.* *.exe *.o *.so -panel +/panel # 压缩包 # # Git 自带压缩,如果这些压缩包里有代码,建议解压后 commit diff --git a/app/http/controllers/plugins/mysql57/mysql57_controller.go b/app/http/controllers/plugins/mysql57/mysql57_controller.go index f6db4653..11fd370a 100644 --- a/app/http/controllers/plugins/mysql57/mysql57_controller.go +++ b/app/http/controllers/plugins/mysql57/mysql57_controller.go @@ -378,7 +378,26 @@ func (c *Mysql57Controller) DatabaseList(ctx http.Context) { return } - controllers.Success(ctx, databases) + page := ctx.Request().QueryInt("page", 1) + limit := ctx.Request().QueryInt("limit", 10) + startIndex := (page - 1) * limit + endIndex := page * limit + if startIndex > len(databases) { + controllers.Success(ctx, http.Json{ + "total": 0, + "items": []database{}, + }) + return + } + if endIndex > len(databases) { + endIndex = len(databases) + } + pagedDatabases := databases[startIndex:endIndex] + + controllers.Success(ctx, http.Json{ + "total": len(databases), + "items": pagedDatabases, + }) } // AddDatabase 添加数据库 @@ -626,7 +645,26 @@ func (c *Mysql57Controller) UserList(ctx http.Context) { return } - controllers.Success(ctx, userGrants) + page := ctx.Request().QueryInt("page", 1) + limit := ctx.Request().QueryInt("limit", 10) + startIndex := (page - 1) * limit + endIndex := page * limit + if startIndex > len(userGrants) { + controllers.Success(ctx, http.Json{ + "total": 0, + "items": []user{}, + }) + return + } + if endIndex > len(userGrants) { + endIndex = len(userGrants) + } + pagedUserGrants := userGrants[startIndex:endIndex] + + controllers.Success(ctx, http.Json{ + "total": len(userGrants), + "items": pagedUserGrants, + }) } // AddUser 添加用户 diff --git a/app/http/controllers/plugins/mysql80/mysql80_controller.go b/app/http/controllers/plugins/mysql80/mysql80_controller.go index 3d3e1f2f..a44c5fe3 100644 --- a/app/http/controllers/plugins/mysql80/mysql80_controller.go +++ b/app/http/controllers/plugins/mysql80/mysql80_controller.go @@ -378,7 +378,26 @@ func (c *Mysql80Controller) DatabaseList(ctx http.Context) { return } - controllers.Success(ctx, databases) + page := ctx.Request().QueryInt("page", 1) + limit := ctx.Request().QueryInt("limit", 10) + startIndex := (page - 1) * limit + endIndex := page * limit + if startIndex > len(databases) { + controllers.Success(ctx, http.Json{ + "total": 0, + "items": []database{}, + }) + return + } + if endIndex > len(databases) { + endIndex = len(databases) + } + pagedDatabases := databases[startIndex:endIndex] + + controllers.Success(ctx, http.Json{ + "total": len(databases), + "items": pagedDatabases, + }) } // AddDatabase 添加数据库 @@ -626,7 +645,26 @@ func (c *Mysql80Controller) UserList(ctx http.Context) { return } - controllers.Success(ctx, userGrants) + page := ctx.Request().QueryInt("page", 1) + limit := ctx.Request().QueryInt("limit", 10) + startIndex := (page - 1) * limit + endIndex := page * limit + if startIndex > len(userGrants) { + controllers.Success(ctx, http.Json{ + "total": 0, + "items": []user{}, + }) + return + } + if endIndex > len(userGrants) { + endIndex = len(userGrants) + } + pagedUserGrants := userGrants[startIndex:endIndex] + + controllers.Success(ctx, http.Json{ + "total": len(userGrants), + "items": pagedUserGrants, + }) } // AddUser 添加用户 diff --git a/app/http/controllers/plugins/pureftpd/pureftpd_controller.go b/app/http/controllers/plugins/pureftpd/pureftpd_controller.go new file mode 100644 index 00000000..d4027287 --- /dev/null +++ b/app/http/controllers/plugins/pureftpd/pureftpd_controller.go @@ -0,0 +1,313 @@ +package pureftpd + +import ( + "regexp" + "strings" + + "github.com/goravel/framework/contracts/http" + "panel/app/http/controllers" + "panel/pkg/tools" +) + +type PureFtpdController struct { +} + +type User struct { + Username string `json:"username"` + Path string `json:"path"` +} + +func NewPureFtpdController() *PureFtpdController { + return &PureFtpdController{} +} + +// Status 获取运行状态 +func (c *PureFtpdController) Status(ctx http.Context) { + if !controllers.Check(ctx, "pureftpd") { + return + } + + status := tools.ExecShell("systemctl status pure-ftpd | grep Active | grep -v grep | awk '{print $2}'") + if len(status) == 0 { + controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd状态失败") + return + } + + if status == "active" { + controllers.Success(ctx, true) + } else { + controllers.Success(ctx, false) + } +} + +// Reload 重载配置 +func (c *PureFtpdController) Reload(ctx http.Context) { + if !controllers.Check(ctx, "pureftpd") { + return + } + + tools.ExecShell("systemctl reload pure-ftpd") + status := tools.ExecShell("systemctl status pure-ftpd | grep Active | grep -v grep | awk '{print $2}'") + if len(status) == 0 { + controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd状态失败") + return + } + + if status == "active" { + controllers.Success(ctx, true) + } else { + controllers.Success(ctx, false) + } +} + +// Restart 重启服务 +func (c *PureFtpdController) Restart(ctx http.Context) { + if !controllers.Check(ctx, "pureftpd") { + return + } + + tools.ExecShell("systemctl restart pure-ftpd") + status := tools.ExecShell("systemctl status pure-ftpd | grep Active | grep -v grep | awk '{print $2}'") + if len(status) == 0 { + controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd状态失败") + return + } + + if status == "active" { + controllers.Success(ctx, true) + } else { + controllers.Success(ctx, false) + } +} + +// Start 启动服务 +func (c *PureFtpdController) Start(ctx http.Context) { + if !controllers.Check(ctx, "pureftpd") { + return + } + + tools.ExecShell("systemctl start pure-ftpd") + status := tools.ExecShell("systemctl status pure-ftpd | grep Active | grep -v grep | awk '{print $2}'") + if len(status) == 0 { + controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd状态失败") + return + } + + if status == "active" { + controllers.Success(ctx, true) + } else { + controllers.Success(ctx, false) + } +} + +// Stop 停止服务 +func (c *PureFtpdController) Stop(ctx http.Context) { + if !controllers.Check(ctx, "pureftpd") { + return + } + + tools.ExecShell("systemctl stop pure-ftpd") + status := tools.ExecShell("systemctl status pure-ftpd | grep Active | grep -v grep | awk '{print $2}'") + if len(status) == 0 { + controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd状态失败") + return + } + + if status != "active" { + controllers.Success(ctx, true) + } else { + controllers.Success(ctx, false) + } +} + +// List 获取用户列表 +func (c *PureFtpdController) List(ctx http.Context) { + if !controllers.Check(ctx, "pureftpd") { + return + } + + listRaw := tools.ExecShell("pure-pw list") + if len(listRaw) == 0 { + controllers.Success(ctx, http.Json{ + "total": 0, + "items": []User{}, + }) + return + } + + listArr := strings.Split(listRaw, "\n") + var users []User + for _, v := range listArr { + if len(v) == 0 { + continue + } + + match := regexp.MustCompile(`(\S+)\s+(\S+)`).FindStringSubmatch(v) + users = append(users, User{ + Username: match[1], + Path: strings.Replace(match[2], "/./", "/", 1), + }) + } + + page := ctx.Request().QueryInt("page", 1) + limit := ctx.Request().QueryInt("limit", 10) + startIndex := (page - 1) * limit + endIndex := page * limit + if startIndex > len(users) { + controllers.Success(ctx, http.Json{ + "total": 0, + "items": []User{}, + }) + return + } + if endIndex > len(users) { + endIndex = len(users) + } + pagedUsers := users[startIndex:endIndex] + + controllers.Success(ctx, http.Json{ + "total": len(users), + "items": pagedUsers, + }) +} + +// Add 添加用户 +func (c *PureFtpdController) Add(ctx http.Context) { + if !controllers.Check(ctx, "pureftpd") { + return + } + + validator, err := ctx.Request().Validate(map[string]string{ + "username": "required", + "password": "required|min_len:6", + "path": "required", + }) + if err != nil { + controllers.Error(ctx, http.StatusBadRequest, err.Error()) + return + } + if validator.Fails() { + controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One()) + return + } + + username := ctx.Request().Input("username") + password := ctx.Request().Input("password") + path := ctx.Request().Input("path") + + if !strings.HasPrefix(path, "/") { + path = "/" + path + } + if !tools.Exists(path) { + controllers.Error(ctx, http.StatusBadRequest, "目录不存在") + return + } + + tools.Chmod(path, 755) + tools.Chown(path, "www", "www") + tools.ExecShell(`yes '` + password + `' | pure-pw useradd ` + username + ` -u www -g www -d ` + path) + tools.ExecShell("pure-pw mkdb") + + controllers.Success(ctx, nil) +} + +// Delete 删除用户 +func (c *PureFtpdController) Delete(ctx http.Context) { + if !controllers.Check(ctx, "pureftpd") { + return + } + + validator, err := ctx.Request().Validate(map[string]string{ + "username": "required", + }) + if err != nil { + controllers.Error(ctx, http.StatusBadRequest, err.Error()) + return + } + if validator.Fails() { + controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One()) + return + } + + username := ctx.Request().Input("username") + + tools.ExecShell("pure-pw userdel " + username + " -m") + tools.ExecShell("pure-pw mkdb") + + controllers.Success(ctx, nil) +} + +// ChangePassword 修改密码 +func (c *PureFtpdController) ChangePassword(ctx http.Context) { + if !controllers.Check(ctx, "pureftpd") { + return + } + + validator, err := ctx.Request().Validate(map[string]string{ + "username": "required", + "password": "required|min_len:6", + }) + if err != nil { + controllers.Error(ctx, http.StatusBadRequest, err.Error()) + return + } + if validator.Fails() { + controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One()) + return + } + + username := ctx.Request().Input("username") + password := ctx.Request().Input("password") + + tools.ExecShell(`yes '` + password + `' | pure-pw passwd ` + username + ` -m`) + tools.ExecShell("pure-pw mkdb") + + controllers.Success(ctx, nil) +} + +// GetPort 获取端口 +func (c *PureFtpdController) GetPort(ctx http.Context) { + if !controllers.Check(ctx, "pureftpd") { + return + } + + port := tools.ExecShell(`cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`) + if len(port) == 0 { + controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd端口失败") + return + } + + controllers.Success(ctx, port) +} + +// SetPort 设置端口 +func (c *PureFtpdController) SetPort(ctx http.Context) { + if !controllers.Check(ctx, "pureftpd") { + return + } + + validator, err := ctx.Request().Validate(map[string]string{ + "port": "required", + }) + if err != nil { + controllers.Error(ctx, http.StatusBadRequest, err.Error()) + return + } + if validator.Fails() { + controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One()) + return + } + + port := ctx.Request().Input("port") + tools.ExecShell(`sed -i "s/Bind.*/Bind 0.0.0.0,` + port + `/g" /www/server/pure-ftpd/etc/pure-ftpd.conf`) + if tools.IsRHEL() { + tools.ExecShell("firewall-cmd --zone=public --add-port=" + port + "/tcp --permanent") + tools.ExecShell("firewall-cmd --reload") + } else { + tools.ExecShell("ufw allow " + port + "/tcp") + tools.ExecShell("ufw reload") + } + tools.ExecShell("systemctl restart pure-ftpd") + + controllers.Success(ctx, nil) +} diff --git a/app/http/controllers/safe_controller.go b/app/http/controllers/safe_controller.go index 4a772e85..c11c8d52 100644 --- a/app/http/controllers/safe_controller.go +++ b/app/http/controllers/safe_controller.go @@ -48,6 +48,8 @@ func (r *SafeController) GetFirewallRules(ctx http.Context) { Success(ctx, nil) return } + page := ctx.Request().QueryInt("page", 1) + limit := ctx.Request().QueryInt("limit", 10) if tools.IsRHEL() { out := tools.ExecShell("firewall-cmd --list-all 2>&1") @@ -66,7 +68,24 @@ func (r *SafeController) GetFirewallRules(ctx http.Context) { }) } - Success(ctx, rules) + startIndex := (page - 1) * limit + endIndex := page * limit + if startIndex > len(rules) { + Success(ctx, http.Json{ + "total": 0, + "items": []map[string]string{}, + }) + return + } + if endIndex > len(rules) { + endIndex = len(rules) + } + pagedRules := rules[startIndex:endIndex] + + Success(ctx, http.Json{ + "total": len(rules), + "items": pagedRules, + }) } else { out := tools.ExecShell("ufw status | grep -v '(v6)' | grep ALLOW | awk '{print $1}'") if len(out) == 0 { @@ -82,7 +101,24 @@ func (r *SafeController) GetFirewallRules(ctx http.Context) { }) } - Success(ctx, rules) + startIndex := (page - 1) * limit + endIndex := page * limit + if startIndex > len(rules) { + Success(ctx, http.Json{ + "total": 0, + "items": []map[string]string{}, + }) + return + } + if endIndex > len(rules) { + endIndex = len(rules) + } + pagedRules := rules[startIndex:endIndex] + + Success(ctx, http.Json{ + "total": len(rules), + "items": pagedRules, + }) } } diff --git a/app/plugins/pureftpd/pureftpd.go b/app/plugins/pureftpd/pureftpd.go new file mode 100644 index 00000000..614e59c6 --- /dev/null +++ b/app/plugins/pureftpd/pureftpd.go @@ -0,0 +1,13 @@ +package pureftpd + +var ( + Name = "Pure-FTPd" + Description = "Pure-Ftpd 是一个快速、高效、轻便、安全的 FTP 服务器,它以安全和配置简单为设计目标,支持虚拟主机,IPV6,PAM 等功能。" + Slug = "pureftpd" + Version = "1.0.50" + Requires = []string{} + Excludes = []string{} + Install = `bash /www/panel/scripts/pureftpd/install.sh` + Uninstall = `bash /www/panel/scripts/pureftpd/uninstall.sh` + Update = `bash /www/panel/scripts/pureftpd/update.sh` +) diff --git a/app/services/plugin.go b/app/services/plugin.go index d88f634b..7e2522a2 100644 --- a/app/services/plugin.go +++ b/app/services/plugin.go @@ -14,6 +14,7 @@ import ( "panel/app/plugins/php81" "panel/app/plugins/php82" "panel/app/plugins/phpmyadmin" + "panel/app/plugins/pureftpd" "panel/app/plugins/s3fs" "panel/app/plugins/supervisor" ) @@ -147,6 +148,17 @@ func (r *PluginImpl) All() []PanelPlugin { Uninstall: phpmyadmin.Uninstall, Update: phpmyadmin.Update, }) + p = append(p, PanelPlugin{ + Name: pureftpd.Name, + Description: pureftpd.Description, + Slug: pureftpd.Slug, + Version: pureftpd.Version, + Requires: pureftpd.Requires, + Excludes: pureftpd.Excludes, + Install: pureftpd.Install, + Uninstall: pureftpd.Uninstall, + Update: pureftpd.Update, + }) p = append(p, PanelPlugin{ Name: s3fs.Name, Description: s3fs.Description, diff --git a/config/app.go b/config/app.go index 7cbe5bbe..44f60890 100644 --- a/config/app.go +++ b/config/app.go @@ -51,7 +51,7 @@ func init() { // Here you may specify the default timezone for your application. // Example: UTC, Asia/Shanghai // More: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones - "timezone": carbon.UTC, + "timezone": carbon.PRC, // Encryption Key // diff --git a/public/panel/views/plugins/mysql57.html b/public/panel/views/plugins/mysql57.html index 29d377d9..7a9f0d9d 100644 --- a/public/panel/views/plugins/mysql57.html +++ b/public/panel/views/plugins/mysql57.html @@ -203,6 +203,15 @@ Date: 2022-07-22 {field: 'name', title: '库名', fixed: 'left', unresize: true, sort: true} , {fixed: 'right', title: '操作', toolbar: '#mysql-database-list-control', width: 150} ]] + , parseData: function (res) { + return { + "code": res.code, + "msg": res.message, + "count": res.data.total, + "data": res.data.items + }; + } + , page: true }); // 头工具栏事件 table.on('toolbar(mysql-database-list)', function (obj) { @@ -272,6 +281,15 @@ Date: 2022-07-22 , {field: 'grants', title: '权限'} , {fixed: 'right', title: '操作', toolbar: '#mysql-user-list-control', width: 150} ]] + , parseData: function (res) { + return { + "code": res.code, + "msg": res.message, + "count": res.data.total, + "data": res.data.items + }; + } + , page: true }); // 头工具栏事件 table.on('toolbar(mysql-user-list)', function (obj) { diff --git a/public/panel/views/plugins/mysql80.html b/public/panel/views/plugins/mysql80.html index d76b39ee..2c1134ce 100644 --- a/public/panel/views/plugins/mysql80.html +++ b/public/panel/views/plugins/mysql80.html @@ -203,6 +203,15 @@ Date: 2022-07-22 {field: 'name', title: '库名', fixed: 'left', unresize: true, sort: true} , {fixed: 'right', title: '操作', toolbar: '#mysql-database-list-control', width: 150} ]] + , parseData: function (res) { + return { + "code": res.code, + "msg": res.message, + "count": res.data.total, + "data": res.data.items + }; + } + , page: true }); // 头工具栏事件 table.on('toolbar(mysql-database-list)', function (obj) { @@ -272,6 +281,15 @@ Date: 2022-07-22 , {field: 'grants', title: '权限'} , {fixed: 'right', title: '操作', toolbar: '#mysql-user-list-control', width: 150} ]] + , parseData: function (res) { + return { + "code": res.code, + "msg": res.message, + "count": res.data.total, + "data": res.data.items + }; + } + , page: true }); // 头工具栏事件 table.on('toolbar(mysql-user-list)', function (obj) { diff --git a/public/panel/views/plugins/pureftpd.html b/public/panel/views/plugins/pureftpd.html new file mode 100644 index 00000000..1b61f092 --- /dev/null +++ b/public/panel/views/plugins/pureftpd.html @@ -0,0 +1,305 @@ + +Pure-Ftpd +
+
+
+
+
Pure-Ftpd 运行状态
+
+
当前状态:获取中
+
+ + + + +
+
+ 基本设置 +
+
+
+ +
+ +
+
设置Pure-Ftpd的访问端口
+
+
+
+ +
+
+
+
+
+
+
Pure-Ftpd 用户列表
+
+
+ + + + +
+
+
+
+
+ + diff --git a/public/panel/views/plugins/pureftpd/add_user.html b/public/panel/views/plugins/pureftpd/add_user.html new file mode 100644 index 00000000..699cad83 --- /dev/null +++ b/public/panel/views/plugins/pureftpd/add_user.html @@ -0,0 +1,80 @@ + + + diff --git a/public/panel/views/safe.html b/public/panel/views/safe.html index 3886786c..6e591566 100644 --- a/public/panel/views/safe.html +++ b/public/panel/views/safe.html @@ -1,7 +1,7 @@ 系统安全 @@ -218,6 +218,15 @@ Date: 2023-07-21 , {field: 'protocol', title: '协议', sort: true} , {fixed: 'right', title: '操作', toolbar: '#safe-port-setting', width: 150} ]] + , parseData: function (res) { + return { + "code": res.code, + "msg": res.message, + "count": res.data.total, + "data": res.data.items + }; + } + , page: true }); table.on('tool(safe-port)', function (obj) { let data = obj.data; diff --git a/routes/plugin.go b/routes/plugin.go index 6ba22581..1ede94a1 100644 --- a/routes/plugin.go +++ b/routes/plugin.go @@ -13,6 +13,7 @@ import ( "panel/app/http/controllers/plugins/php81" "panel/app/http/controllers/plugins/php82" "panel/app/http/controllers/plugins/phpmyadmin" + "panel/app/http/controllers/plugins/pureftpd" "panel/app/http/controllers/plugins/s3fs" "panel/app/http/controllers/plugins/supervisor" "panel/app/http/middleware" @@ -170,6 +171,20 @@ func Plugin() { route.Get("info", phpMyAdminController.Info) route.Post("port", phpMyAdminController.SetPort) }) + facades.Route().Prefix("api/plugins/pureftpd").Middleware(middleware.Jwt()).Group(func(route route.Route) { + pureFtpdController := pureftpd.NewPureFtpdController() + route.Get("status", pureFtpdController.Status) + route.Post("reload", pureFtpdController.Reload) + route.Post("start", pureFtpdController.Start) + route.Post("stop", pureFtpdController.Stop) + route.Post("restart", pureFtpdController.Restart) + route.Get("list", pureFtpdController.List) + route.Post("add", pureFtpdController.Add) + route.Post("delete", pureFtpdController.Delete) + route.Post("changePassword", pureFtpdController.ChangePassword) + route.Get("port", pureFtpdController.GetPort) + route.Post("port", pureFtpdController.SetPort) + }) 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/pureftpd/install.sh b/scripts/pureftpd/install.sh new file mode 100644 index 00000000..4fd1f6ad --- /dev/null +++ b/scripts/pureftpd/install.sh @@ -0,0 +1,130 @@ +#!/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="+----------------------------------------------------" +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/pure-ftpd" +setupPath="/www" +pureftpdPath="${setupPath}/server/pure-ftpd" +pureftpdVersion="1.0.50" + +# 准备安装目录 +rm -rf ${pureftpdPath} +mkdir -p ${pureftpdPath} +cd ${pureftpdPath} + +wget -O ${pureftpdPath}/pure-ftpd-${pureftpdVersion}.tar.gz ${downloadUrl}/pure-ftpd-${pureftpdVersion}.tar.gz +if [ "$?" != "0" ]; then + echo -e $HR + echo "错误:Pure-Ftpd-${pureftpdVersion}下载失败,请检查网络是否正常。" + exit 1 +fi + +tar -xvf pure-ftpd-${pureftpdVersion}.tar.gz +rm -f pure-ftpd-${pureftpdVersion}.tar.gz +mv pure-ftpd-${pureftpdVersion} src +cd src + +./configure --prefix=${pureftpdPath} CFLAGS=-O2 --with-puredb --with-quotas --with-cookie --with-virtualhosts --with-diraliases --with-sysquotas --with-ratios --with-altlog --with-paranoidmsg --with-shadow --with-welcomemsg --with-throttling --with-uploadscript --with-language=simplified-chinese --with-rfc2640 --with-ftpwho --with-tls +if [ "$?" != "0" ]; then + echo -e $HR + echo "错误:Pure-Ftpd-${pureftpdVersion}编译配置失败,请截图错误信息寻求帮助。" + exit 1 +fi + +make +if [ "$?" != "0" ]; then + echo -e $HR + echo "错误:Pure-Ftpd-${pureftpdVersion}编译失败,请截图错误信息寻求帮助。" + exit 1 +fi + +make install +if [ ! -f "${pureftpdPath}/bin/pure-pw" ]; then + echo -e $HR + echo "错误:Pure-Ftpd-${pureftpdVersion}安装失败,请截图错误信息寻求帮助。" + exit 1 +fi + +# 修改 pure-ftpd 配置文件 +sed -i "s!# PureDB\s*/etc/pureftpd.pdb!PureDB ${pureftpdPath}/etc/pureftpd.pdb!" ${pureftpdPath}/etc/pure-ftpd.conf +sed -i 's!# ChrootEveryone\s*yes!ChrootEveryone yes!' ${pureftpdPath}/etc/pure-ftpd.conf +sed -i 's!NoAnonymous\s*no!NoAnonymous yes!' ${pureftpdPath}/etc/pure-ftpd.conf +sed -i 's!AnonymousCanCreateDirs\s*yes!AnonymousCanCreateDirs no!' ${pureftpdPath}/etc/pure-ftpd.conf +sed -i 's!AnonymousCantUpload\s*yes!AnonymousCantUpload no!' ${pureftpdPath}/etc/pure-ftpd.conf +sed -i 's!PAMAuthentication\s*yes!PAMAuthentication no!' ${pureftpdPath}/etc/pure-ftpd.conf +sed -i 's!UnixAuthentication\s*yes!UnixAuthentication no!' ${pureftpdPath}/etc/pure-ftpd.conf +sed -i 's!# PassivePortRange\s*30000 50000!PassivePortRange 39000 40000!' ${pureftpdPath}/etc/pure-ftpd.conf +sed -i 's!PassivePortRange\s*30000 50000!PassivePortRange 39000 40000!' ${pureftpdPath}/etc/pure-ftpd.conf +sed -i 's!LimitRecursion\s*10000 8!LimitRecursion 20000 8!' ${pureftpdPath}/etc/pure-ftpd.conf +sed -i 's!# TLS!TLS!' ${pureftpdPath}/etc/pure-ftpd.conf +sed -i "s!# CertFile\s*/etc/ssl/private/pure-ftpd.pem!CertFile ${pureftpdPath}/etc/pure-ftpd.pem!" ${pureftpdPath}/etc/pure-ftpd.conf +sed -i 's!# Bind\s*127.0.0.1,21!Bind 0.0.0.0,21!' ${pureftpdPath}/etc/pure-ftpd.conf +touch ${pureftpdPath}/etc/pureftpd.passwd +touch ${pureftpdPath}/etc/pureftpd.pdb + +openssl dhparam -out ${pureftpdPath}/etc/pure-ftpd-dhparams.pem 2048 +openssl req -x509 -nodes -days 3560 -newkey rsa:2048 -sha256 -keyout ${pureftpdPath}/etc/pure-ftpd.pem -out ${pureftpdPath}/etc/pure-ftpd.pem << EOF +CN +Beijing +Beijing +HaoZi Technology Co., Ltd +HaoZi Panel +github.com/haozi-team/panel +panel@haozi.net +EOF +chmod 600 ${pureftpdPath}/etc/*.pem + +# 添加系统服务 +ln -sf ${pureftpdPath}/bin/pure-pw /usr/bin/pure-pw + +cat > /etc/systemd/system/pure-ftpd.service << EOF +[Unit] +Description=Pure-FTPd FTP server +After=network.target + +[Service] +Type=forking +PIDFile=/var/run/pure-ftpd.pid +ExecStart=${pureftpdPath}/sbin/pure-ftpd ${pureftpdPath}/etc/pure-ftpd.conf +ExecReload=/bin/kill -HUP \$MAINPID +ExecStop=/bin/kill -TERM \$MAINPID + +[Install] +WantedBy=multi-user.target +EOF + +# 添加防火墙规则 +if [ "${OS}" == "centos" ]; then + firewall-cmd --zone=public --add-port=21/tcp --permanent + firewall-cmd --zone=public --add-port=39000-40000/tcp --permanent + firewall-cmd --reload +elif [ "${OS}" == "debian" ]; then + ufw allow 21/tcp + ufw allow 39000:40000/tcp + ufw reload +fi + +systemctl daemon-reload +systemctl enable pure-ftpd.service +systemctl start pure-ftpd.service + +panel writePlugin pureftpd 1.0.50 + +echo -e "${HR}\nPure-Ftpd-${pureftpdVersion} 安装完成\n${HR}" diff --git a/scripts/pureftpd/uninstall.sh b/scripts/pureftpd/uninstall.sh new file mode 100644 index 00000000..7c133477 --- /dev/null +++ b/scripts/pureftpd/uninstall.sh @@ -0,0 +1,31 @@ +#!/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="+----------------------------------------------------" +OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown") + +systemctl stop pure-ftpd +systemctl disable pure-ftpd +rm -f /etc/systemd/system/pure-ftpd.service +systemctl daemon-reload +pkill -9 pure-ftpd +rm -rf /www/server/pure-ftpd +rm -f /usr/bin/pure-pw + +echo -e "${HR}\nPure-Ftpd 卸载完成\n${HR}" diff --git a/scripts/pureftpd/update.sh b/scripts/pureftpd/update.sh new file mode 100644 index 00000000..99c2367d --- /dev/null +++ b/scripts/pureftpd/update.sh @@ -0,0 +1,78 @@ +#!/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="+----------------------------------------------------" +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/pure-ftpd" +setupPath="/www" +pureftpdPath="${setupPath}/server/pure-ftpd" +pureftpdVersion="1.0.50" + +# 准备安装目录 +cp ${pureftpdPath}/etc/pureftpd.passwd /tmp/pureftpd.passwd +cp ${pureftpdPath}/etc/pureftpd.pdb /tmp/pureftpd.pdb +cp ${pureftpdPath}/etc/pureftpd.conf /tmp/pureftpd.conf +systemctl stop pure-ftpd.service +rm -rf ${pureftpdPath} +mkdir -p ${pureftpdPath} +cd ${pureftpdPath} + +wget -O ${pureftpdPath}/pure-ftpd-${pureftpdVersion}.tar.gz ${downloadUrl}/pure-ftpd-${pureftpdVersion}.tar.gz +if [ "$?" != "0" ]; then + echo -e $HR + echo "错误:Pure-Ftpd-${pureftpdVersion}下载失败,请检查网络是否正常。" + exit 1 +fi + +tar -xvf pure-ftpd-${pureftpdVersion}.tar.gz +rm -f pure-ftpd-${pureftpdVersion}.tar.gz +mv pure-ftpd-${pureftpdVersion} src +cd src + +./configure --prefix=${pureftpdPath} CFLAGS=-O2 --with-puredb --with-quotas --with-cookie --with-virtualhosts --with-diraliases --with-sysquotas --with-ratios --with-altlog --with-paranoidmsg --with-shadow --with-welcomemsg --with-throttling --with-uploadscript --with-language=simplified-chinese --with-rfc2640 --with-ftpwho --with-tls +if [ "$?" != "0" ]; then + echo -e $HR + echo "错误:Pure-Ftpd-${pureftpdVersion}编译配置失败,请截图错误信息寻求帮助。" + exit 1 +fi + +make +if [ "$?" != "0" ]; then + echo -e $HR + echo "错误:Pure-Ftpd-${pureftpdVersion}编译失败,请截图错误信息寻求帮助。" + exit 1 +fi + +make install +if [ ! -f "${pureftpdPath}/bin/pure-pw" ]; then + echo -e $HR + echo "错误:Pure-Ftpd-${pureftpdVersion}安装失败,请截图错误信息寻求帮助。" + exit 1 +fi + +# 还原配置 +cp /tmp/pureftpd.passwd ${pureftpdPath}/etc/pureftpd.passwd +cp /tmp/pureftpd.pdb ${pureftpdPath}/etc/pureftpd.pdb +cp /tmp/pureftpd.conf ${pureftpdPath}/etc/pureftpd.conf + +systemctl start pure-ftpd.service + +panel writePlugin pureftpd 1.0.50 + +echo -e "${HR}\nPure-Ftpd-${pureftpdVersion} 升级完成\n${HR}"