mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 13:47:15 +08:00
feat: pure-ftpd plugin
This commit is contained in:
8
.gitignore
vendored
8
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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 添加用户
|
||||
|
||||
@@ -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 添加用户
|
||||
|
||||
313
app/http/controllers/plugins/pureftpd/pureftpd_controller.go
Normal file
313
app/http/controllers/plugins/pureftpd/pureftpd_controller.go
Normal file
@@ -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)
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
13
app/plugins/pureftpd/pureftpd.go
Normal file
13
app/plugins/pureftpd/pureftpd.go
Normal file
@@ -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`
|
||||
)
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
//
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
305
public/panel/views/plugins/pureftpd.html
Normal file
305
public/panel/views/plugins/pureftpd.html
Normal file
@@ -0,0 +1,305 @@
|
||||
<!--
|
||||
Name: Pure-Ftpd管理器
|
||||
Author: 耗子
|
||||
Date: 2023-08-03
|
||||
-->
|
||||
<title>Pure-Ftpd</title>
|
||||
<div class="layui-fluid" id="component-tabs">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-md12">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">Pure-Ftpd 运行状态</div>
|
||||
<div class="layui-card-body">
|
||||
<blockquote id="pureftpd-status" class="layui-elem-quote layui-quote-nm">当前状态:<span
|
||||
class="layui-badge layui-bg-black">获取中</span></blockquote>
|
||||
<div class="layui-btn-container" style="padding-top: 30px;">
|
||||
<button id="pureftpd-start" class="layui-btn">启动</button>
|
||||
<button id="pureftpd-stop" class="layui-btn layui-btn-danger">停止</button>
|
||||
<button id="pureftpd-restart" class="layui-btn layui-btn-warm">重启</button>
|
||||
<button id="pureftpd-reload" class="layui-btn layui-btn-normal">重载</button>
|
||||
</div>
|
||||
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
|
||||
<legend>基本设置</legend>
|
||||
</fieldset>
|
||||
<div class="layui-form" lay-filter="pureftpd_setting">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label" style="font-size: 13px;">端口</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="pureftpd_port" value="获取中ing..." class="layui-input"
|
||||
disabled>
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">设置Pure-Ftpd的访问端口</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn layui-btn-sm" lay-submit lay-filter="pureftpd_setting_submit">
|
||||
确认修改
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">Pure-Ftpd 用户列表</div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-hide" id="pureftpd-user-list" lay-filter="pureftpd-user-list"></table>
|
||||
<!-- 顶部工具栏 -->
|
||||
<script type="text/html" id="pureftpd-user-list-bar">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm" lay-event="add_user">新建用户</button>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 右侧管理 -->
|
||||
<script type="text/html" id="pureftpd-user-list-control">
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="change_password">改密</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
layui.use(['index', 'form', 'table', 'view'], function () {
|
||||
let $ = layui.$
|
||||
, admin = layui.admin
|
||||
, table = layui.table
|
||||
, form = layui.form
|
||||
, view = layui.view;
|
||||
|
||||
// 获取运行状态并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/pureftpd/status"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
if (result.data) {
|
||||
$('#pureftpd-status').html('当前状态:<span class="layui-badge layui-bg-green">运行中</span>');
|
||||
} else {
|
||||
$('#pureftpd-status').html('当前状态:<span class="layui-badge layui-bg-red">已停止</span>');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 获取端口并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/pureftpd/port"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
$('input[name=pureftpd_port]').val(result.data);
|
||||
$('input').attr('disabled', false);
|
||||
form.render();
|
||||
}
|
||||
});
|
||||
|
||||
// 监听提交
|
||||
form.on('submit(pureftpd_setting_submit)', function (data) {
|
||||
data.field.port = $('input[name=pureftpd_port]').val();
|
||||
index = layer.msg('请稍候...', {icon: 16, time: 0, shade: 0.3});
|
||||
admin.req({
|
||||
url: "/api/plugins/pureftpd/port"
|
||||
, type: 'post'
|
||||
, data: data.field
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
layer.msg('设置成功', {icon: 1});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
// 获取用户列表
|
||||
table.render({
|
||||
elem: '#pureftpd-user-list'
|
||||
, url: '/api/plugins/pureftpd/list'
|
||||
, toolbar: '#pureftpd-user-list-bar'
|
||||
, title: 'Pure-Ftpd 用户列表'
|
||||
, cols: [[
|
||||
{field: 'username', title: '用户名', fixed: 'left', unresize: true, sort: true}
|
||||
, {field: 'path', title: '目录', unresize: true, sort: true}
|
||||
, {fixed: 'right', title: '操作', toolbar: '#pureftpd-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(pureftpd-user-list)', function (obj) {
|
||||
if (obj.event === 'add_user') {
|
||||
admin.popup({
|
||||
title: '新建Pure-Ftpd用户'
|
||||
, area: ['600px', '400px']
|
||||
, id: 'LAY-popup-pureftpd-user-add'
|
||||
, success: function (layer, index) {
|
||||
view(this.id).render('plugins/pureftpd/add_user', {}).done(function () {
|
||||
form.render(null, 'LAY-popup-pureftpd-user-add');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// 行工具事件
|
||||
table.on('tool(pureftpd-user-list)', function (obj) {
|
||||
let data = obj.data;
|
||||
if (obj.event === 'del') {
|
||||
layer.confirm('确定要删除用户 <b style="color: red;">' + data.username + '</b> 吗?', function (index) {
|
||||
index = layer.msg('请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
, shade: 0.3
|
||||
});
|
||||
admin.req({
|
||||
url: "/api/plugins/pureftpd/delete"
|
||||
, type: 'post'
|
||||
, data: data
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
layer.msg('Pure-Ftpd用户删除失败,请刷新重试!')
|
||||
return false;
|
||||
}
|
||||
obj.del();
|
||||
layer.alert(data.username + '删除成功!');
|
||||
}
|
||||
});
|
||||
layer.close(index);
|
||||
});
|
||||
} else if (obj.event === 'change_password') {
|
||||
// 弹出输入密码框
|
||||
layer.prompt({
|
||||
formType: 1
|
||||
, title: '请输入新密码(6位以上)'
|
||||
}, function (value, index) {
|
||||
layer.close(index);
|
||||
index = layer.msg('请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
, shade: 0.3
|
||||
});
|
||||
// 发送请求
|
||||
admin.req({
|
||||
url: "/api/plugins/pureftpd/changePassword"
|
||||
, type: 'post'
|
||||
, data: {
|
||||
username: data.username,
|
||||
password: value
|
||||
}
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
layer.msg('密码修改失败,请刷新重试!')
|
||||
return false;
|
||||
}
|
||||
layer.alert('用户' + data.username + '密码修改成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 事件监听
|
||||
$('#pureftpd-start').click(function () {
|
||||
layer.confirm('确定要启动Pure-Ftpd吗?', {
|
||||
btn: ['启动', '取消']
|
||||
}, function () {
|
||||
index = layer.msg('请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
, shade: 0.3
|
||||
});
|
||||
admin.req({
|
||||
url: "/api/plugins/pureftpd/start"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('Pure-Ftpd启动成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#pureftpd-stop').click(function () {
|
||||
layer.confirm('停止Pure-Ftpd将导致FTP无法连接,是否继续停止?', {
|
||||
btn: ['停止', '取消']
|
||||
}, function () {
|
||||
index = layer.msg('请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
, shade: 0.3
|
||||
});
|
||||
admin.req({
|
||||
url: "/api/plugins/pureftpd/stop"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('Pure-Ftpd停止成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#pureftpd-restart').click(function () {
|
||||
layer.confirm('确定要重启Pure-Ftpd吗?', {
|
||||
btn: ['重启', '取消']
|
||||
}, function () {
|
||||
index = layer.msg('请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
, shade: 0.3
|
||||
});
|
||||
admin.req({
|
||||
url: "/api/plugins/pureftpd/restart"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('Pure-Ftpd重启成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#pureftpd-reload').click(function () {
|
||||
index = layer.msg('请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
, shade: 0.3
|
||||
});
|
||||
admin.req({
|
||||
url: "/api/plugins/pureftpd/reload"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
layer.alert('Pure-Ftpd重载成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
80
public/panel/views/plugins/pureftpd/add_user.html
Normal file
80
public/panel/views/plugins/pureftpd/add_user.html
Normal file
@@ -0,0 +1,80 @@
|
||||
<!--
|
||||
Name: Pure-Ftpd管理器 - 新建用户
|
||||
Author: 耗子
|
||||
Date: 2023-08-03
|
||||
-->
|
||||
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
|
||||
<form class="layui-form" action="" lay-filter="add-pureftpd-user-form">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">用户名</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="username" id="add-pureftpd-user-username"
|
||||
lay-verify="required" placeholder="请输入用户名" class="layui-input"
|
||||
value="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">密码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="password" name="password" id="add-pureftpd-user-password"
|
||||
lay-verify="required" placeholder="请输入密码" class="layui-input"
|
||||
value="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">目录</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="path" id="add-pureftpd-user-path"
|
||||
lay-verify="required" placeholder="请输入目录" class="layui-input"
|
||||
value="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<div class="layui-footer">
|
||||
<button class="layui-btn" lay-submit="" lay-filter="add-pureftpd-user-submit">立即提交</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</script>
|
||||
<script>
|
||||
layui.data.sendParams = function (params) {
|
||||
layui.use(['admin', 'form', 'form', 'table'], function () {
|
||||
var admin = layui.admin
|
||||
, layer = layui.layer
|
||||
, form = layui.form
|
||||
, table = layui.table;
|
||||
|
||||
form.render();
|
||||
|
||||
// 提交
|
||||
form.on('submit(add-pureftpd-user-submit)', function (data) {
|
||||
index = layer.msg('正在提交...', {icon: 16, time: 0, shade: 0.3});
|
||||
admin.req({
|
||||
url: "/api/plugins/pureftpd/add"
|
||||
, type: 'post'
|
||||
, data: data.field
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
layer.msg('Pure-Ftpd用户添加失败,请刷新重试!')
|
||||
return false;
|
||||
}
|
||||
table.reload('pureftpd-user-list');
|
||||
layer.alert('Pure-Ftpd用户添加成功!', {
|
||||
icon: 1
|
||||
, title: '提示'
|
||||
, btn: ['确定']
|
||||
, yes: function (index) {
|
||||
layer.closeAll();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
@@ -1,7 +1,7 @@
|
||||
<!--
|
||||
Name: 系统安全
|
||||
Author: 耗子
|
||||
Date: 2023-07-21
|
||||
Date: 2023-08-03
|
||||
-->
|
||||
<title>系统安全</title>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
130
scripts/pureftpd/install.sh
Normal file
130
scripts/pureftpd/install.sh
Normal file
@@ -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}"
|
||||
31
scripts/pureftpd/uninstall.sh
Normal file
31
scripts/pureftpd/uninstall.sh
Normal file
@@ -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}"
|
||||
78
scripts/pureftpd/update.sh
Normal file
78
scripts/pureftpd/update.sh
Normal file
@@ -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}"
|
||||
Reference in New Issue
Block a user