Compare commits

..

9 Commits

Author SHA1 Message Date
耗子 6e6342f0a6 feat: release v2.0.27 2023-08-04 22:45:32 +08:00
耗子 cea485133f fix: website ssl and php 2023-08-04 22:44:58 +08:00
耗子 08e14a84a5 fix: lint 2023-08-04 02:38:44 +08:00
耗子 bc179923a0 feat: optimize readme 2023-08-04 02:31:14 +08:00
耗子 67ef7ccd7a feat: optimize readme 2023-08-04 02:23:54 +08:00
耗子 1abb9691f4 fix(safe): null rule 2023-08-04 00:33:27 +08:00
耗子 4dc2895781 feat: release v2.0.26 2023-08-03 23:39:02 +08:00
耗子 e7635d4b36 fix: lint 2023-08-03 23:34:18 +08:00
耗子 e731c025b5 feat: pure-ftpd plugin 2023-08-03 23:33:31 +08:00
21 changed files with 1237 additions and 28 deletions
+3 -5
View File
@@ -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
+69 -10
View File
@@ -6,30 +6,36 @@
[![Codecov](https://codecov.io/gh/haozi-team/panel/branch/main/graph/badge.svg?token=XFT5NGNSRG)](https://codecov.io/gh/haozi-team/panel)
![License](https://img.shields.io/github/license/haozi-team/panel)
这是耗子Linux面板的开源仓库,基于Apache License 2.0协议进行开源,目前处于积极开发状态
这是耗子Linux面板,一种使用GO开发的轻量Linux服务器运维管理面板
#### 交流QQ群:[12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | QQ频道:[耗子](https://pd.qq.com/s/fyol46wfy)
严禁使用耗子Linux面板从事任何非法活动,非法站点请勿向我们请求任何形式的技术支持,如果在技术支持过程中发现非法内容,我们将立即停止技术支持并留存相关证据。
#### 交流QQ群:[12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | QQ频道:[pd.qq.com/s/fyol46wfy](https://pd.qq.com/s/fyol46wfy)
广告: [`WeAvatar` 多端多元化的统一头像服务](https://weavatar.com)
## 运行环境
面板仅支持主流 `amd64` | `arm64` 系统的最新版本,且随着系统版本的更新,面板也会逐步停止对旧版本的支持。
面板仅支持主流 `amd64` | `arm64` 系统的最新版本,且随着系统版本的更新,面板也会逐步停止对旧版本系统的支持。
不支持 `Ubuntu`,因为其发版太频繁,难以维护。
不支持 `Ubuntu`,因为其发版太频繁,难以维护。
低配机器建议使用 `Debian`,资源占用较 `RHEL` 系更低。其他机器建议使用 `RockyLinux` | `AlmaLinux`,维护周期更长。
不在下表中的其他同版本的 `RHEL` 系统(OpenCloudOS、Anolis、CentOS Stream等),可自行尝试安装,但不保证能够正常运行,且不提供技术支持。
不在下表中的其他系统(OpenCloudOS、Anolis、CentOS Stream、Debian等),可自行尝试安装,但不保证能够正常运行,且不提供技术支持。
| 系统 | 版本 |
|------------|----|
| RHEL | 9 |
| RockyLinux | 9 |
| AlmaLinux | 9 |
| Debian | 12 |
## 安装面板
安装面板前,你需要了解LNMP环境的基本知识,以及如何处理常见的LNMP环境问题,我们不建议**0基础**的用户安装和使用耗子Linux面板。
如果你决定继续,请以`root`用户登录服务器,执行以下命令安装面板:
```shell
bash <(curl -sSL https://dl.cdn.haozi.net/panel/install_panel.sh)
@@ -37,18 +43,34 @@ bash <(curl -sSL https://dl.cdn.haozi.net/panel/install_panel.sh)
## 日常维护
使用`panel`命令进行日常维护:
```shell
panel
```
## 问题反馈
使用类问题,可在 [WePublish社区论坛](https://wepublish.cn/forums) 提问寻求帮助。
使用类问题,可在 [WePublish社区论坛](https://wepublish.cn/forums) 提问或QQ群`@汤姆猫`寻求帮助。
对于面板自身问题,可在 GitHub 的`Issues`
面板自身问题,可在 GitHub 的`Issues`
页面提交问题反馈,注意[提问的智慧](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md)。
交流QQ群:[12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | QQ频道:[耗子](https://pd.qq.com/s/fyol46wfy)
## 贡献代码
### 寻找/创建 Issue
您可以在 [Issue 列表](https://github.com/haozi-team/panel/issues) 中寻找或创建一个 Issue,留言表达想要处理该 Issue
的意愿,得到维护者的确认后,即可开始处理。
### 创建 PR
- 如果您对该流程不太了解,可以查看[这篇文章](https://docs.github.com/en/get-started/quickstart/contributing-to-projects)
- 在开发过程中,如果遇到问题可以随时在 Issue 中详尽描述该问题,以进一步沟通,但在此之前请确保自己已通过 Google
等方式尽可能的尝试解决问题;
- 当 PR 开发完毕后,请为其添加 `🚀 Review Ready` 标签,维护者将及时进行评审;
- PR 合并后,如果描述设置正确,Issue 将被自动关闭;
- 我们非常欢迎您的贡献,将在下次发版时将您添加到首页贡献者中;❤️
## 赞助商
@@ -62,12 +84,49 @@ panel
- [无畏云加速](https://su.sctes.com/register?code=8st689ujpmm2p)
- [AnyCast.Ai](https://www.anycast.ai/)
- [迅捷盾](https://console.fastdun.com/)
### DevOps
- [极狐GitLab](https://www.jihulab.com/)
#### 接受云资源和资金赞助,可通过QQ群咨询联系
## Star 趋势
## 行为准则
[![Star 趋势](https://starchart.cc/haozi-team/panel.svg)](https://starchart.cc/haozi-team/panel)
耗子Linux面板遵守业界通用的行为准则。任何违反行为准则的行为都可以报告给我们:
- 参与者将容忍反对意见。
- 参与者必须确保他们的语言和行为没有人身攻击和贬低个人言论。
- 在解释他人的言行时,参与者应始终保持良好的意图。
- 不能容忍可合理视为骚扰的行为。
## 安全说明
安全性是我们最关心的问题之一,我们已在生产环境广泛应用耗子Linux面板。
耗子Linux面板采用业界多种方案尽可能保证面板的安全性,但是我们不能保证面板的绝对安全性,**因此我们不对面板的安全性做任何保证
**
如果您在使用面板的过程中发现任何安全问题,请勿提交 Issue,可通过以下方式直接联系我们:
- 邮箱:i@haozi.net
- QQ826896000
## 贡献者
这个项目的存在要归功于所有做出贡献的人,参与贡献请先查看贡献代码部分。
<a href="https://github.com/DevHaoZi" target="_blank"><img src="https://avatars.githubusercontent.com/u/115467771?v=4" width="48" height="48"></a>
## Star 历史
<a href="https://star-history.com/#haozi-team/panel&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=haozi-team/panel&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=haozi-team/panel&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=haozi-team/panel&type=Date" />
</picture>
</a>
@@ -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 添加用户
@@ -0,0 +1,314 @@
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, 0755)
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)
}
+46 -4
View File
@@ -48,12 +48,17 @@ 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")
match := regexp.MustCompile(`ports: (.*)`).FindStringSubmatch(out)
if len(match) == 0 {
Success(ctx, nil)
Success(ctx, http.Json{
"total": 0,
"items": []map[string]string{},
})
return
}
ports := strings.Split(match[1], " ")
@@ -66,11 +71,31 @@ 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 {
Success(ctx, nil)
Success(ctx, http.Json{
"total": 0,
"items": []map[string]string{},
})
return
}
var rules []map[string]string
@@ -82,7 +107,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
View 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`
)
+12
View File
@@ -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,
+1 -1
View File
@@ -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
//
+1 -1
View File
@@ -8,6 +8,6 @@ func init() {
config := facades.Config()
config.Add("panel", map[string]any{
"name": "耗子Linux面板",
"version": "v2.0.25",
"version": "v2.0.27",
})
}
+18
View File
@@ -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) {
+18
View File
@@ -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
View 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>
@@ -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>
+10 -1
View File
@@ -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;
+9
View File
@@ -316,6 +316,15 @@ Date: 2023-07-24
let domains = $("input[name='domains[]']").map(function () {
return $(this).val();
}).get()
var reg = new RegExp(/\n443.*\n?/);
// 如果开启了https,就自动添加443端口
if ($('input[name="ssl"]').prop('checked') && !reg.test(ports)) {
ports = ports + '\n443 ssl http2';
}
// 如果关闭了https,就自动删除443端口
if (!$('input[name="ssl"]').prop('checked') && reg.test(ports)) {
ports = ports.replace(/443.*\n?/, '');
}
admin.req({
url: '/api/panel/website/config'
+15
View File
@@ -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)
+2 -2
View File
@@ -223,8 +223,8 @@ sed -i 's/display_errors = Off/display_errors = On/g' ${phpPath}/etc/php.ini
sed -i 's/error_reporting =.*/error_reporting = E_ALL \& \~E_NOTICE/g' ${phpPath}/etc/php.ini
# 设置SSL根证书
sed -i "s#;openssl.cafile=#openssl.cafile=/etc/pki/tls/certs/ca-bundle.crt#" ${phpPath}/etc/php.ini
sed -i "s#;curl.cainfo =#curl.cainfo = /etc/pki/tls/certs/ca-bundle.crt#" ${phpPath}/etc/php.ini
#sed -i "s#;openssl.cafile=#openssl.cafile=/etc/pki/tls/certs/ca-bundle.crt#" ${phpPath}/etc/php.ini
#sed -i "s#;curl.cainfo =#curl.cainfo = /etc/pki/tls/certs/ca-bundle.crt#" ${phpPath}/etc/php.ini
# 关闭php外显
sed -i 's/expose_php = On/expose_php = Off/g' ${phpPath}/etc/php.ini
+130
View 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}"
+33
View File
@@ -0,0 +1,33 @@
#!/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
panel deletePlugin pureftpd
echo -e "${HR}\nPure-Ftpd 卸载完成\n${HR}"
+78
View 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}"