Compare commits

...

31 Commits

Author SHA1 Message Date
耗子 266f12d15a feat: 发布 v2.0.49 2023-09-29 17:02:12 +08:00
耗子 3a18b36df2 feat: 优化面板更新校验 2023-09-28 20:23:08 +08:00
耗子 ebcc022418 fix: 发布 v2.0.48 2023-09-28 19:50:23 +08:00
耗子 aa0fb2afcc fix: 修复 MySQL 更新 2023-09-28 19:50:03 +08:00
耗子 bde2c50696 fix: 修复 MySQL 版本号未更新 2023-09-28 19:39:26 +08:00
耗子 0945949b37 fix: 多打了一个引号 2023-09-28 17:01:37 +08:00
耗子 4fcd65da70 feat: 发布 v2.0.45 2023-09-28 13:25:09 +08:00
耗子 afb12dd664 feat: 更新 MySQL 版本 2023-09-28 13:20:08 +08:00
耗子 8c5d0e905e feat: 优化 MySQL systemd 判断 2023-09-28 13:06:31 +08:00
耗子 92e77720f7 feat: 优化 MySQL EL9 兼容性 2023-09-28 02:48:24 +08:00
耗子 124912368e feat: 发布 v2.0.44 2023-09-26 17:58:30 +08:00
耗子 084f0a06ca fix: 修复 PostgreSQL 创建数据库未修改所有者 2023-09-26 16:48:36 +08:00
耗子 c08fd0af7f fix: 调整 fail2ban 安装 2023-09-26 01:58:40 +08:00
耗子 3229685729 fix: 修复 logind 的问题 2023-09-26 01:19:05 +08:00
耗子 77ae021a2f feat: 发布 v2.0.43 2023-09-26 01:15:09 +08:00
耗子 69cf5dcb65 fix: PostgreSQL 16 不讲武德,来骗,来偷袭 2023-09-26 01:14:44 +08:00
耗子 9bd9833ca9 feat: 发布 v2.0.42 2023-09-26 00:42:36 +08:00
耗子 5fb58476ba feat: 优化 PostgreSQL 安装更新 2023-09-26 00:41:55 +08:00
耗子 da4a04ad87 fix: 安装 PostgreSQL 后重启一次 2023-09-26 00:32:12 +08:00
耗子 ae4736d86e feat: 发布 v2.0.41 2023-09-26 00:07:10 +08:00
耗子 198c56cb25 feat: 更新 PostgreSQL 16.0 和 15.4 2023-09-25 23:59:24 +08:00
耗子 4950b48fc9 Merge remote-tracking branch 'origin/main' 2023-09-18 10:34:36 +08:00
耗子 3f323588c0 fix: 修复空目录打包 2023-09-18 10:34:29 +08:00
耗子 e651a8ed6b Merge branch 'renovate/goreleaser-goreleaser-action-5.x' into 'main'
chore(deps): Update goreleaser/goreleaser-action action to v5

See merge request haozi-team/panel!15
2023-09-16 11:06:16 +08:00
耗子 bc9274a8b7 Merge branch 'renovate/github.com-imroc-req-v3-3.x' into 'main'
chore(deps): Update module github.com/imroc/req/v3 to v3.42.0

See merge request haozi-team/panel!14
2023-09-16 02:52:17 +08:00
耗子 1047374c2b Merge branch 'renovate/github.com-bytedance-sonic-1.x' into 'main'
chore(deps): Update module github.com/bytedance/sonic to v1.10.1

See merge request haozi-team/panel!13
2023-09-16 02:40:17 +08:00
Renovate 1f2f51fb55 chore(deps): Update goreleaser/goreleaser-action action to v5 2023-09-15 18:25:32 +00:00
Renovate 872af0b298 chore(deps): Update module github.com/imroc/req/v3 to v3.42.0 2023-09-15 18:25:29 +00:00
Renovate bc22e23cda chore(deps): Update module github.com/bytedance/sonic to v1.10.1 2023-09-15 18:25:19 +00:00
耗子 4ca2446dc3 docs: 更新赞助商信息 2023-09-12 00:13:58 +08:00
耗子 840d6824f0 docs: 更新赞助商信息 2023-09-11 20:44:50 +08:00
27 changed files with 1779 additions and 48 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
with:
go-version: 'stable'
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v4
uses: goreleaser/goreleaser-action@v5
with:
version: latest
args: release --clean
+1
View File
@@ -30,4 +30,5 @@ archives:
- storage/*
- database/*
- scripts/*
- resources/*
- panel-example.conf
+10 -3
View File
@@ -79,12 +79,18 @@ panel
### CDN
- [盾云CDN](http://cdn.ddunyun.com/)
- [无畏云加速](https://su.sctes.com/register?code=8st689ujpmm2p)
- [又拍云](https://www.upyun.com/?utm_source=lianmeng&utm_medium=referral)
- [AnyCast.Ai](https://www.anycast.ai/)
- [盾云CDN](http://cdn.ddunyun.com/)
### 对象存储
- [又拍云](https://www.upyun.com/?utm_source=lianmeng&utm_medium=referral)
### DevOps
- [极狐GitLab](https://www.jihulab.com/)
@@ -104,7 +110,8 @@ panel
安全性是我们最关心的问题之一,我们已在生产环境广泛应用耗子Linux面板。
耗子Linux面板采用业界多种方案尽可能保证面板的安全性,但是我们不能保证面板的绝对安全性,**因此我们不对面板的安全性做任何保证**。
耗子Linux面板采用业界多种方案尽可能保证面板的安全性,但是我们不能保证面板的绝对安全性,**因此我们不对面板的安全性做任何保证
**
如果您在使用面板的过程中发现任何安全问题,请勿提交 Issue,可通过以下方式直接联系我们:
@@ -326,6 +326,7 @@ func (c *Postgresql15Controller) AddDatabase(ctx http.Context) http.Response {
tools.Exec(`echo "CREATE DATABASE ` + database + `;" | su - postgres -c "psql"`)
tools.Exec(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`)
tools.Exec(`echo "ALTER DATABASE ` + database + ` OWNER TO ` + user + `;" | su - postgres -c "psql"`)
tools.Exec(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`)
userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256"
@@ -0,0 +1,612 @@
package postgresql16
import (
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/carbon"
"panel/app/http/controllers"
"panel/app/models"
"panel/app/services"
"panel/pkg/tools"
)
type Postgresql16Controller struct {
setting services.Setting
backup services.Backup
}
type Info struct {
Name string `json:"name"`
Value string `json:"value"`
}
func NewPostgresql16Controller() *Postgresql16Controller {
return &Postgresql16Controller{
setting: services.NewSettingImpl(),
backup: services.NewBackupImpl(),
}
}
// Status 获取运行状态
func (c *Postgresql16Controller) Status(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
}
if status == "active" {
return controllers.Success(ctx, true)
} else {
return controllers.Success(ctx, false)
}
}
// Reload 重载配置
func (c *Postgresql16Controller) Reload(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
tools.Exec("systemctl reload postgresql")
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
}
if status == "active" {
return controllers.Success(ctx, true)
} else {
return controllers.Success(ctx, false)
}
}
// Restart 重启服务
func (c *Postgresql16Controller) Restart(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
tools.Exec("systemctl restart postgresql")
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
}
if status == "active" {
return controllers.Success(ctx, true)
} else {
return controllers.Success(ctx, false)
}
}
// Start 启动服务
func (c *Postgresql16Controller) Start(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
tools.Exec("systemctl start postgresql")
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
}
if status == "active" {
return controllers.Success(ctx, true)
} else {
return controllers.Success(ctx, false)
}
}
// Stop 停止服务
func (c *Postgresql16Controller) Stop(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
tools.Exec("systemctl stop postgresql")
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if len(status) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL状态失败")
}
if status != "active" {
return controllers.Success(ctx, true)
} else {
return controllers.Success(ctx, false)
}
}
// GetConfig 获取配置
func (c *Postgresql16Controller) GetConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
// 获取配置
config := tools.Read("/www/server/postgresql/data/postgresql.conf")
if len(config) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL配置失败")
}
return controllers.Success(ctx, config)
}
// GetUserConfig 获取用户配置
func (c *Postgresql16Controller) GetUserConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
// 获取配置
config := tools.Read("/www/server/postgresql/data/pg_hba.conf")
if len(config) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL配置失败")
}
return controllers.Success(ctx, config)
}
// SaveConfig 保存配置
func (c *Postgresql16Controller) SaveConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
config := ctx.Request().Input("config")
if len(config) == 0 {
return controllers.Error(ctx, http.StatusBadRequest, "配置不能为空")
}
if !tools.Write("/www/server/postgresql/data/postgresql.conf", config, 0644) {
return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败")
}
return c.Restart(ctx)
}
// SaveUserConfig 保存用户配置
func (c *Postgresql16Controller) SaveUserConfig(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
config := ctx.Request().Input("config")
if len(config) == 0 {
return controllers.Error(ctx, http.StatusBadRequest, "配置不能为空")
}
if !tools.Write("/www/server/postgresql/data/pg_hba.conf", config, 0644) {
return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败")
}
return c.Restart(ctx)
}
// Load 获取负载
func (c *Postgresql16Controller) Load(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if status != "active" {
return controllers.Error(ctx, http.StatusInternalServerError, "PostgreSQL 已停止运行")
}
data := []Info{
{"启动时间", carbon.Parse(tools.Exec(`echo "select pg_postmaster_start_time();" | su - postgres -c "psql" | sed -n 3p | cut -d'.' -f1`)).ToDateTimeString()},
{"进程 PID", tools.Exec(`echo "select pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)},
{"进程数", tools.Exec(`ps aux | grep postgres | grep -v grep | wc -l`)},
{"总连接数", tools.Exec(`echo "SELECT count(*) FROM pg_stat_activity WHERE NOT pid=pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)},
{"空间占用", tools.Exec(`echo "select pg_size_pretty(pg_database_size('postgres'));" | su - postgres -c "psql" | sed -n 3p`)},
}
return controllers.Success(ctx, data)
}
// Log 获取日志
func (c *Postgresql16Controller) Log(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
log := tools.Exec("tail -n 100 /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log")
return controllers.Success(ctx, log)
}
// ClearLog 清空日志
func (c *Postgresql16Controller) ClearLog(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
tools.Exec("echo '' > /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log")
return controllers.Success(ctx, nil)
}
// DatabaseList 获取数据库列表
func (c *Postgresql16Controller) DatabaseList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
status := tools.Exec("systemctl status postgresql | grep Active | grep -v grep | awk '{print $2}'")
if status != "active" {
return controllers.Error(ctx, http.StatusInternalServerError, "PostgreSQL 已停止运行")
}
raw := tools.Exec(`echo "\l" | su - postgres -c "psql"`)
databases := strings.Split(raw, "\n")
databases = databases[3 : len(databases)-1]
type database struct {
Name string `json:"name"`
Owner string `json:"owner"`
Encoding string `json:"encoding"`
}
var databaseList []database
for _, db := range databases {
parts := strings.Split(db, "|")
if len(parts) != 9 || len(strings.TrimSpace(parts[0])) == 0 {
continue
}
databaseList = append(databaseList, database{
Name: strings.TrimSpace(parts[0]),
Owner: strings.TrimSpace(parts[1]),
Encoding: strings.TrimSpace(parts[2]),
})
}
page := ctx.Request().QueryInt("page", 1)
limit := ctx.Request().QueryInt("limit", 10)
startIndex := (page - 1) * limit
endIndex := page * limit
if startIndex > len(databaseList) {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []database{},
})
}
if endIndex > len(databaseList) {
endIndex = len(databaseList)
}
pagedDatabases := databaseList[startIndex:endIndex]
return controllers.Success(ctx, http.Json{
"total": len(databaseList),
"items": pagedDatabases,
})
}
// AddDatabase 添加数据库
func (c *Postgresql16Controller) AddDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:255",
})
if err != nil {
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
database := ctx.Request().Input("database")
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
tools.Exec(`echo "CREATE DATABASE ` + database + `;" | su - postgres -c "psql"`)
tools.Exec(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`)
tools.Exec(`echo "ALTER DATABASE ` + database + ` OWNER TO ` + user + `;" | su - postgres -c "psql"`)
tools.Exec(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`)
userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256"
tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`)
return c.Reload(ctx)
}
// DeleteDatabase 删除数据库
func (c *Postgresql16Controller) DeleteDatabase(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$|not_in:postgres,template0,template1",
})
if err != nil {
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
database := ctx.Request().Input("database")
tools.Exec(`echo "DROP DATABASE ` + database + `;" | su - postgres -c "psql"`)
return controllers.Success(ctx, nil)
}
// BackupList 获取备份列表
func (c *Postgresql16Controller) BackupList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
backupList, err := c.backup.PostgresqlList()
if err != nil {
facades.Log().Error("[PostgreSQL] 获取备份列表失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "获取备份列表失败")
}
return controllers.Success(ctx, backupList)
}
// UploadBackup 上传备份
func (c *Postgresql16Controller) UploadBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
file, err := ctx.Request().File("file")
if err != nil {
return controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
if !tools.Exists(backupPath) {
tools.Mkdir(backupPath, 0644)
}
name := file.GetClientOriginalName()
_, err = file.StoreAs(backupPath, name)
if err != nil {
return controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
}
return controllers.Success(ctx, nil)
}
// CreateBackup 创建备份
func (c *Postgresql16Controller) CreateBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$|not_in:information_schema,mysql,performance_schema,sys",
})
if err != nil {
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
database := ctx.Request().Input("database")
err = c.backup.PostgresqlBackup(database)
if err != nil {
facades.Log().Error("[PostgreSQL] 创建备份失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "创建备份失败")
}
return controllers.Success(ctx, nil)
}
// DeleteBackup 删除备份
func (c *Postgresql16Controller) DeleteBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
"name": "required|min_len:1|max_len:255",
})
if err != nil {
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
fileName := ctx.Request().Input("name")
tools.Remove(backupPath + "/" + fileName)
return controllers.Success(ctx, nil)
}
// RestoreBackup 还原备份
func (c *Postgresql16Controller) RestoreBackup(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
"name": "required|min_len:1|max_len:255",
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$|not_in:information_schema,mysql,performance_schema,sys",
})
if err != nil {
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
err = c.backup.PostgresqlRestore(ctx.Request().Input("database"), ctx.Request().Input("name"))
if err != nil {
facades.Log().Error("[PostgreSQL] 还原失败:" + err.Error())
return controllers.Error(ctx, http.StatusInternalServerError, "还原失败: "+err.Error())
}
return controllers.Success(ctx, nil)
}
// UserList 用户列表
func (c *Postgresql16Controller) UserList(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
type user struct {
User string `json:"user"`
Role string `json:"role"`
}
raw := tools.Exec(`echo "\du" | su - postgres -c "psql"`)
users := strings.Split(raw, "\n")
if len(users) < 4 {
return controllers.Error(ctx, http.StatusInternalServerError, "用户列表为空")
}
users = users[3:]
var userList []user
for _, u := range users {
userInfo := strings.Split(u, "|")
if len(userInfo) != 2 {
continue
}
userList = append(userList, user{
User: strings.TrimSpace(userInfo[0]),
Role: strings.TrimSpace(userInfo[1]),
})
}
page := ctx.Request().QueryInt("page", 1)
limit := ctx.Request().QueryInt("limit", 10)
startIndex := (page - 1) * limit
endIndex := page * limit
if startIndex > len(userList) {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []user{},
})
}
if endIndex > len(userList) {
endIndex = len(userList)
}
pagedUsers := userList[startIndex:endIndex]
return controllers.Success(ctx, http.Json{
"total": len(userList),
"items": pagedUsers,
})
}
// AddUser 添加用户
func (c *Postgresql16Controller) AddUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:255",
})
if err != nil {
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
database := ctx.Request().Input("database")
tools.Exec(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`)
tools.Exec(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`)
userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256"
tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`)
return c.Reload(ctx)
}
// DeleteUser 删除用户
func (c *Postgresql16Controller) DeleteUser(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
})
if err != nil {
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
user := ctx.Request().Input("user")
tools.Exec(`echo "DROP USER ` + user + `;" | su - postgres -c "psql"`)
tools.Exec(`sed -i '/` + user + `/d' /www/server/postgresql/data/pg_hba.conf`)
return c.Reload(ctx)
}
// SetUserPassword 设置用户密码
func (c *Postgresql16Controller) SetUserPassword(ctx http.Context) http.Response {
check := controllers.Check(ctx, "postgresql16")
if check != nil {
return check
}
validator, err := ctx.Request().Validate(map[string]string{
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:255",
})
if err != nil {
return controllers.Error(ctx, http.StatusBadRequest, err.Error())
}
if validator.Fails() {
return controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
}
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
tools.Exec(`echo "ALTER USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`)
return controllers.Success(ctx, nil)
}
+2 -2
View File
@@ -4,10 +4,10 @@ var (
Name = "MySQL-5.7"
Description = "MySQL 是最流行的关系型数据库管理系统之一,Oracle 旗下产品。"
Slug = "mysql57"
Version = "5.7.42"
Version = "5.7.43"
Requires = []string{}
Excludes = []string{"mysql80"}
Install = `bash /www/panel/scripts/mysql/install.sh 57`
Uninstall = `bash /www/panel/scripts/mysql/uninstall.sh 57`
Update = `echo "not support now"`
Update = `bash /www/panel/scripts/mysql/update.sh 57`
)
+2 -2
View File
@@ -4,10 +4,10 @@ var (
Name = "MySQL-8.0"
Description = "MySQL 是最流行的关系型数据库管理系统之一,Oracle 旗下产品。(内存 < 4G 无法安装)"
Slug = "mysql80"
Version = "8.0.33"
Version = "8.0.34"
Requires = []string{}
Excludes = []string{"mysql57"}
Install = `bash /www/panel/scripts/mysql/install.sh 80`
Uninstall = `bash /www/panel/scripts/mysql/uninstall.sh 80`
Update = `echo "not support now"`
Update = `bash /www/panel/scripts/mysql/update.sh 80`
)
+2 -2
View File
@@ -4,9 +4,9 @@ var (
Name = "PostgreSQL-15"
Description = "PostgreSQL 是开源的对象 - 关系数据库数据库管理系统,在类似 BSD 许可与 MIT 许可的 PostgreSQL 许可下发行。"
Slug = "postgresql15"
Version = "15.3"
Version = "15.4"
Requires = []string{}
Excludes = []string{}
Excludes = []string{"postgresql16"}
Install = `bash /www/panel/scripts/postgresql/install.sh 15`
Uninstall = `bash /www/panel/scripts/postgresql/uninstall.sh 15`
Update = `bash /www/panel/scripts/postgresql/update.sh 15`
+13
View File
@@ -0,0 +1,13 @@
package postgresql16
var (
Name = "PostgreSQL-16"
Description = "PostgreSQL 是开源的对象 - 关系数据库数据库管理系统,在类似 BSD 许可与 MIT 许可的 PostgreSQL 许可下发行。"
Slug = "postgresql16"
Version = "16.0"
Requires = []string{}
Excludes = []string{"postgresql15"}
Install = `bash /www/panel/scripts/postgresql/install.sh 16`
Uninstall = `bash /www/panel/scripts/postgresql/uninstall.sh 16`
Update = `bash /www/panel/scripts/postgresql/update.sh 16`
)
+12
View File
@@ -15,6 +15,7 @@ import (
"panel/app/plugins/php82"
"panel/app/plugins/phpmyadmin"
"panel/app/plugins/postgresql15"
"panel/app/plugins/postgresql16"
"panel/app/plugins/pureftpd"
"panel/app/plugins/redis"
"panel/app/plugins/s3fs"
@@ -107,6 +108,17 @@ func (r *PluginImpl) All() []PanelPlugin {
Uninstall: postgresql15.Uninstall,
Update: postgresql15.Update,
})
p = append(p, PanelPlugin{
Name: postgresql16.Name,
Description: postgresql16.Description,
Slug: postgresql16.Slug,
Version: postgresql16.Version,
Requires: postgresql16.Requires,
Excludes: postgresql16.Excludes,
Install: postgresql16.Install,
Uninstall: postgresql16.Uninstall,
Update: postgresql16.Update,
})
p = append(p, PanelPlugin{
Name: php74.Name,
Description: php74.Description,
+1 -1
View File
@@ -8,6 +8,6 @@ func init() {
config := facades.Config()
config.Add("panel", map[string]any{
"name": "耗子Linux面板",
"version": "v2.0.40",
"version": "v2.0.49",
})
}
+2 -2
View File
@@ -3,7 +3,7 @@ module panel
go 1.18
require (
github.com/bytedance/sonic v1.10.0
github.com/bytedance/sonic v1.10.1
github.com/gertd/go-pluralize v0.2.1
github.com/gin-contrib/static v0.0.1
github.com/gookit/color v1.5.4
@@ -11,7 +11,7 @@ require (
github.com/goravel/gin v1.1.5
github.com/gorilla/websocket v1.5.0
github.com/iancoleman/strcase v0.3.0
github.com/imroc/req/v3 v3.41.12
github.com/imroc/req/v3 v3.42.0
github.com/mojocn/base64Captcha v1.3.5
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/spf13/cast v1.5.1
+4 -4
View File
@@ -87,8 +87,8 @@ github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQ
github.com/brianvoe/gofakeit/v6 v6.23.2 h1:lVde18uhad5wII/f5RMVFLtdQNE0HaGFuBUXmYKk8i8=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.10.0 h1:qtNZduETEIWJVIyDl01BeNxur2rW9OwTQ/yBqFRkKEk=
github.com/bytedance/sonic v1.10.0/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc=
github.com/bytedance/sonic v1.10.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -384,8 +384,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/imroc/req/v3 v3.41.12 h1:OyPoCpVr8lpWuBwaEnqshA2xYLoxKPnjRIRVaZ+/NoQ=
github.com/imroc/req/v3 v3.41.12/go.mod h1:W7dOrfQORA9nFoj+CafIZ6P5iyk+rWdbp2sffOAvABU=
github.com/imroc/req/v3 v3.42.0 h1:g7wWva3aIJI02mrnqmXe0N5SVkXHQPsYN3Tmw2ZHn3U=
github.com/imroc/req/v3 v3.42.0/go.mod h1:W7dOrfQORA9nFoj+CafIZ6P5iyk+rWdbp2sffOAvABU=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
+13
View File
@@ -144,10 +144,20 @@ func UpdatePanel(proxy bool) error {
} else {
Exec("wget -O /www/panel/panel.zip " + panelInfo.DownloadUrl)
}
if !Exists("/www/panel/panel.zip") {
return errors.New("下载失败")
}
color.Greenln("下载完成")
color.Greenln("更新新版本...")
Exec("cd /www/panel && unzip -o panel.zip && rm -rf panel.zip && chmod 700 panel && bash scripts/update_panel.sh")
if !Exists("/www/panel/panel") {
return errors.New("更新失败,可能是下载过程中出现了问题")
}
color.Greenln("更新完成")
color.Greenln("恢复面板配置...")
@@ -161,6 +171,9 @@ func UpdatePanel(proxy bool) error {
Exec("panel writeSetting version " + panelInfo.Version)
Exec("rm -rf /tmp/panel.db.bak")
Exec("rm -rf /tmp/panel.conf.bak")
color.Greenln("重启面板...")
Exec("systemctl restart panel")
color.Greenln("重启完成")
+2 -2
View File
@@ -165,7 +165,7 @@ Date: 2023-06-22
</div>
<div class="layui-card-body layui-text layadmin-text">
<blockquote class="layui-elem-quote">
<p style="color: red;">开发组祝大家2023端午快乐!永无Bug,永不宕机!</p>
<p style="color: red;">开发组祝大家2023中秋国庆快乐!永无Bug,永不宕机!</p>
</blockquote>
<blockquote class="layui-elem-quote">
<p>欢迎您使用耗子Linux面板。如遇到问题/Bug,可通过 <a
@@ -206,7 +206,6 @@ Date: 2023-06-22
, admin = layui.admin
, element = layui.element
let device = layui.device()
let cpu_info
admin.req({
url: '/api/panel/info/nowMonitor'
@@ -310,6 +309,7 @@ Date: 2023-06-22
index = layer.msg('正在获取版本信息...', {
icon: 16
, time: 0
, shade: 0.3
})
admin.req(
{
@@ -0,0 +1,524 @@
<!--
Name: PostgreSQL管理器
Author: 耗子
Date: 2023-08-13
-->
<title>PostgreSQL</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">PostgreSQL管理</div>
<div class="layui-card-body">
<div class="layui-tab">
<ul class="layui-tab-title">
<li class="layui-this">基本信息</li>
<li>管理</li>
<li>主配置</li>
<li>用户配置</li>
<li>负载状态</li>
<li>日志</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
<legend>运行状态</legend>
</fieldset>
<blockquote id="postgresql-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="postgresql-start" class="layui-btn">启动</button>
<button id="postgresql-stop" class="layui-btn layui-btn-danger">停止</button>
<button id="postgresql-restart" class="layui-btn layui-btn-warm">重启</button>
<button id="postgresql-reload" class="layui-btn layui-btn-normal">重载</button>
</div>
</div>
<div class="layui-tab-item">
<blockquote class="layui-elem-quote">面板仅集成了部分常用功能,如需更多功能,请使用
pgAdmin 客户端。
</blockquote>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
<legend>数据库列表</legend>
</fieldset>
<table class="layui-hide" id="postgresql-database-list"
lay-filter="postgresql-database-list"></table>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
<legend>用户列表</legend>
</fieldset>
<table class="layui-hide" id="postgresql-user-list"
lay-filter="postgresql-user-list"></table>
<!-- 数据库顶部工具栏 -->
<script type="text/html" id="postgresql-database-list-bar">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="add_database">新建数据库
</button>
</div>
</script>
<!-- 用户顶部工具栏 -->
<script type="text/html" id="postgresql-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="postgresql-database-list-control">
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="backup">备份</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
<!-- 用户右侧管理 -->
<script type="text/html" id="postgresql-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 class="layui-tab-item">
<blockquote class="layui-elem-quote">此处修改的是PostgreSQL主配置文件,如果你不了解各参数的含义,请不要随意修改!<br>
提示:Ctrl+F 搜索关键字,Ctrl+S 保存,Ctrl+H 查找替换!
</blockquote>
<div id="postgresql-config-editor"
style="height: 600px;"></div>
<div class="layui-btn-container" style="padding-top: 30px;">
<button id="postgresql-config-save" class="layui-btn">保存</button>
</div>
</div>
<div class="layui-tab-item">
<blockquote class="layui-elem-quote">此处修改的是PostgreSQL用户配置文件,如果你不了解各参数的含义,请不要随意修改!<br>
提示:Ctrl+F 搜索关键字,Ctrl+S 保存,Ctrl+H 查找替换!
</blockquote>
<div id="postgresql-user-config-editor"
style="height: 600px;"></div>
<div class="layui-btn-container" style="padding-top: 30px;">
<button id="postgresql-user-config-save" class="layui-btn">保存</button>
</div>
</div>
<div class="layui-tab-item">
<table class="layui-hide" id="postgresql-load-status"></table>
</div>
<div class="layui-tab-item">
<pre id="postgresql-log" class="layui-code">
获取中...
</pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let postgresql_config_editor;
let postgresql_user_config_editor;
layui.use(['index', 'admin', 'element', 'form', 'view', 'code', 'table'], function () {
let $ = layui.$
, admin = layui.admin
, element = layui.element
, code = layui.code
, table = layui.table
, form = layui.form
, view = layui.view;
form.render();
admin.req({
url: "/api/plugins/postgresql16/status"
, type: 'get'
, success: function (result) {
if (result.code !== 0) {
return false;
}
if (result.data) {
$('#postgresql-status').html('当前状态:<span class="layui-badge layui-bg-green">运行中</span>');
} else {
$('#postgresql-status').html('当前状态:<span class="layui-badge layui-bg-red">已停止</span>');
}
}
});
// 获取数据库列表
table.render({
elem: '#postgresql-database-list'
, url: '/api/plugins/postgresql16/database'
, toolbar: '#postgresql-database-list-bar'
, title: '数据库列表'
, cols: [[
{field: 'name', title: '库名', fixed: 'left', unresize: true, sort: true}
, {field: 'owner', title: '所有者', unresize: true, sort: true}
, {field: 'encoding', title: '编码', unresize: true, sort: true}
, {fixed: 'right', title: '操作', toolbar: '#postgresql-database-list-control', width: 150}
]]
, page: true
, text: {
none: '暂无数据'
}
, parseData: function (res) {
return {
"code": res.code,
"msg": res.message,
"count": res.data.total,
"data": res.data.items
};
}
});
// 头工具栏事件
table.on('toolbar(postgresql-database-list)', function (obj) {
if (obj.event === 'add_database') {
admin.popup({
title: '新建数据库'
, area: ['600px', '300px']
, id: 'LAY-popup-postgresql-database-add'
, success: function (layer, index) {
view(this.id).render('plugins/postgresql16/add_database', {}).done(function () {
form.render(null, 'LAY-popup-postgresql-database-add');
});
}
});
}
});
// 行工具事件
table.on('tool(postgresql-database-list)', function (obj) {
let data = obj.data;
if (obj.event === 'del') {
layer.confirm('高风险操作,确定要删除数据库 <b style="color: red;">' + data.name + '</b> 吗?', function (index) {
index = layer.msg('正在删除数据库 <b style="color: red;">' + data.name + '</b> ...', {
icon: 16
, time: 0
, shade: 0.3
});
admin.req({
url: "/api/plugins/postgresql16/deleteDatabase"
, type: 'post'
, data: {
database: data.name
}
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
obj.del();
layer.alert('数据库' + data.name + '删除成功!');
}
});
layer.close(index);
});
} else if (obj.event === 'backup') {
// 打开备份页面
admin.popup({
title: '备份管理 - ' + data.name
, area: ['70%', '80%']
, id: 'LAY-popup-postgresql-backup'
, success: function (layero, index) {
view(this.id).render('plugins/postgresql16/backup', {
data: data
}).done(function () {
form.render(null, 'LAY-popup-postgresql-backup');
});
}
});
}
});
// 获取数据库用户列表
table.render({
elem: '#postgresql-user-list'
, url: '/api/plugins/postgresql16/user'
, toolbar: '#postgresql-user-list-bar'
, title: '用户列表'
, cols: [[
{field: 'user', title: '用户名', fixed: 'left', width: 300, sort: true}
, {field: 'role', title: '权限'}
, {fixed: 'right', title: '操作', toolbar: '#postgresql-user-list-control', width: 150}
]]
, page: true
, text: {
none: '暂无数据'
}
, parseData: function (res) {
return {
"code": res.code,
"msg": res.message,
"count": res.data.total,
"data": res.data.items
};
}
});
// 头工具栏事件
table.on('toolbar(postgresql-user-list)', function (obj) {
if (obj.event === 'add_user') {
admin.popup({
title: '新建用户'
, area: ['600px', '300px']
, id: 'LAY-popup-postgresql-user-add'
, success: function (layer, index) {
view(this.id).render('plugins/postgresql16/add_user', {}).done(function () {
form.render(null, 'LAY-popup-postgresql-user-add');
});
}
});
}
});
// 行工具事件
table.on('tool(postgresql-user-list)', function (obj) {
let data = obj.data;
if (obj.event === 'del') {
layer.confirm('高风险操作,确定要删除用户 <b style="color: red;">' + data.user + '</b> 吗?', function (index) {
index = layer.msg('正在删除用户 <b style="color: red;">' + data.user + '</b> ...', {
icon: 16
, time: 0
, shade: 0.3
});
admin.req({
url: "/api/plugins/postgresql16/deleteUser"
, type: 'post'
, data: data
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
obj.del();
layer.alert('用户' + data.user + '删除成功!');
}
});
layer.close(index);
});
} else if (obj.event === 'change_password') {
// 弹出输入密码框
layer.prompt({
formType: 1
, title: '请输入新密码(建议8位以上大小写数字特殊符号混合)'
}, function (value, index) {
layer.close(index);
layer.msg('正在修改密码 ...', {
icon: 16
, time: 0
, shade: 0.3
});
// 发送请求
admin.req({
url: "/api/plugins/postgresql16/changePassword"
, type: 'post'
, data: {
user: data.user,
password: value
}
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
layer.alert('用户' + data.user + '密码修改成功!');
}
});
});
}
});
// 获取postgresql日志并渲染
admin.req({
url: "/api/plugins/postgresql16/log"
, type: 'get'
, success: function (result) {
if (result.code !== 0) {
$('#postgresql-log').text('PostgreSQL日志获取失败,请刷新重试!');
code({
elem: '#postgresql-log'
, title: 'error.log'
, encode: true
, about: false
});
return false;
}
$('#postgresql-log').text(result.data);
code({
elem: '#postgresql-log'
, title: 'postgresql.log'
, encode: true
, about: false
});
}
});
// 获取postgresql配置并渲染
admin.req({
url: "/api/plugins/postgresql16/config"
, type: 'get'
, success: function (result) {
if (result.code !== 0) {
return false;
}
$('#postgresql-config-editor').text(result.data);
postgresql_config_editor = ace.edit("postgresql-config-editor", {
mode: "ace/mode/ini",
selectionStyle: "text"
});
}
});
// 获取postgresql用户配置并渲染
admin.req({
url: "/api/plugins/postgresql16/userConfig"
, type: 'get'
, success: function (result) {
if (result.code !== 0) {
return false;
}
$('#postgresql-user-config-editor').text(result.data);
postgresql_user_config_editor = ace.edit("postgresql-user-config-editor", {
mode: "ace/mode/ini",
selectionStyle: "text"
});
}
});
// 获取postgresql负载状态并渲染
table.render({
elem: '#postgresql-load-status'
, url: '/api/plugins/postgresql16/load'
, cols: [[
{field: 'name', width: '80%', title: '属性',}
, {field: 'value', width: '20%', title: '当前值'}
]]
});
element.render();
// 事件监听
$('#postgresql-start').click(function () {
layer.confirm('确定要启动PostgreSQL吗?', {
btn: ['启动', '取消']
}, function () {
index = layer.msg('正在启动PostgreSQL...', {
icon: 16
, time: 0
, shade: 0.3
});
admin.req({
url: "/api/plugins/postgresql16/start"
, type: 'post'
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
admin.events.refresh();
layer.alert('PostgreSQL启动成功!');
}
});
});
});
$('#postgresql-stop').click(function () {
layer.confirm('停止PostgreSQL将导致使用PostgreSQL的网站无法访问,是否继续停止?', {
btn: ['停止', '取消']
}, function () {
index = layer.msg('正在停止PostgreSQL...', {
icon: 16
, time: 0
, shade: 0.3
});
admin.req({
url: "/api/plugins/postgresql16/stop"
, type: 'post'
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
admin.events.refresh();
layer.alert('PostgreSQL停止成功!');
}
});
});
});
$('#postgresql-restart').click(function () {
layer.confirm('重启PostgreSQL将导致使用PostgreSQL的网站短时间无法访问,是否继续重启?', {
btn: ['重启', '取消']
}, function () {
index = layer.msg('正在重启PostgreSQL...', {
icon: 16
, time: 0
, shade: 0.3
});
admin.req({
url: "/api/plugins/postgresql16/restart"
, type: 'post'
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
admin.events.refresh();
layer.alert('PostgreSQL重启成功!');
}
});
});
});
$('#postgresql-reload').click(function () {
index = layer.msg('正在重载PostgreSQL...', {
icon: 16
, time: 0
, shade: 0.3
});
admin.req({
url: "/api/plugins/postgresql16/reload"
, type: 'post'
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
admin.events.refresh();
layer.alert('PostgreSQL重载成功!');
}
});
});
$('#postgresql-config-save').click(function () {
index = layer.msg('正在保存配置...', {
icon: 16
, time: 0
, shade: 0.3
});
admin.req({
url: "/api/plugins/postgresql16/config"
, type: 'post'
, data: {
config: postgresql_config_editor.getValue()
}
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
layer.alert('PostgreSQL配置保存成功!');
}
});
});
$('#postgresql-user-config-save').click(function () {
index = layer.msg('正在保存配置...', {
icon: 16
, time: 0
, shade: 0.3
});
admin.req({
url: "/api/plugins/postgresql16/userConfig"
, type: 'post'
, data: {
config: postgresql_user_config_editor.getValue()
}
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
layer.alert('PostgreSQL用户配置保存成功!');
}
});
});
});
</script>
@@ -0,0 +1,77 @@
<!--
Name: PostgreSQL管理器 - 添加数据库
Author: 耗子
Date: 2023-08-13
-->
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
<form class="layui-form" action="" lay-filter="add-postgresql-database-form">
<div class="layui-form-item">
<label class="layui-form-label">数据库名</label>
<div class="layui-input-block">
<input type="text" name="database" lay-verify="required" placeholder="请输入数据库名"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">用户名</label>
<div class="layui-input-block">
<input type="text" name="user" lay-verify="required" placeholder="请输入用户名"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">密码</label>
<div class="layui-input-block">
<input type="text" name="password" lay-verify="required" placeholder="请输入密码(8位以上大小写数字特殊符号混合)"
autocomplete="off" class="layui-input">
</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-postgresql-database-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', 'table'], function () {
var admin = layui.admin
, layer = layui.layer
, form = layui.form
, table = layui.table
form.render();
// 提交
form.on('submit(add-postgresql-database-submit)', function (data) {
index = layer.msg('正在提交...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/postgresql16/addDatabase"
, type: 'post'
, data: data.field
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
table.reload('postgresql-database-list');
table.reload('postgresql-user-list');
layer.alert('数据库添加成功!', {
icon: 1
, title: '提示'
, btn: ['确定']
, yes: function (index) {
layer.closeAll();
}
});
}
});
return false;
});
});
};
</script>
@@ -0,0 +1,79 @@
<!--
Name: PostgreSQL管理器 - 添加用户
Author: 耗子
Date: 2023-08-13
-->
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
<form class="layui-form" action="" lay-filter="add-postgresql-user-form">
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">用户名</label>
<div class="layui-input-block">
<input type="text" name="user" lay-verify="required" placeholder="请输入用户名"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">密码</label>
<div class="layui-input-block">
<input type="text" name="password" lay-verify="required"
placeholder="请输入密码(8位以上大小写数字特殊符号混合)"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">数据库</label>
<div class="layui-input-block">
<input type="text" name="database" lay-verify="required" placeholder="输入授权给该用户的数据库名"
autocomplete="off" class="layui-input">
</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-postgresql-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', 'table'], function () {
var $ = layui.$
, admin = layui.admin
, layer = layui.layer
, form = layui.form
, table = layui.table
form.render();
// 提交
form.on('submit(add-postgresql-user-submit)', function (data) {
index = layer.msg('正在提交...', {icon: 16, time: 0, shade: 0.3});
admin.req({
url: "/api/plugins/postgresql16/addUser"
, type: 'post'
, data: data.field
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
return false;
}
table.reload('postgresql-database-list');
table.reload('postgresql-user-list');
layer.alert('用户添加成功!', {
icon: 1
, title: '提示'
, btn: ['确定']
, yes: function (index) {
layer.closeAll();
}
});
}
});
return false;
});
});
};
</script>
@@ -0,0 +1,141 @@
<!--
Name: PostgreSQL管理器 - 数据库备份
Author: 耗子
Date: 2023-08-13
-->
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
<div class="layui-row">
<div class="layui-col-xs12 layui-col-sm12 layui-col-md12">
<table class="layui-hide" id="postgresql-backup-list" lay-filter="postgresql-backup-list"></table>
</div>
</div>
</script>
<!-- 备份顶部工具栏 -->
<script type="text/html" id="postgresql-database-backup-bar">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="backup_database">备份数据库</button>
<button class="layui-btn layui-btn-sm" id="upload_postgresql_backup">上传备份</button>
</div>
</script>
<!-- 备份右侧管理 -->
<script type="text/html" id="postgresql-database-backup-control">
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="restore">恢复</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
<script>
layui.data.sendParams = function (params) {
layui.use(['admin', 'form', 'laydate', 'code'], function () {
var admin = layui.admin
, layer = layui.layer
, table = layui.table
, upload = layui.upload;
// 渲染表格
table.render({
elem: '#postgresql-backup-list'
, url: '/api/plugins/postgresql16/backup'
, toolbar: '#postgresql-database-backup-bar'
, title: '备份列表'
, cols: [[
{field: 'name', title: '备份名称', width: 500}
, {field: 'size', title: '文件大小'}
, {field: 'right', title: '操作', width: 150, toolbar: '#postgresql-database-backup-control'}
]]
, text: {
none: '无备份数据'
}
, done: function (res, curr, count) {
upload.render({
elem: '#upload_postgresql_backup'
, url: '/api/plugins/postgresql16/uploadBackup'
, accept: 'file'
, ext: 'sql|zip|rar|tar|gz|bz2'
, before: function (obj) {
index = layer.msg('正在上传备份文件,可能需要较长时间,请勿操作...', {
icon: 16
, time: 0
});
}
, done: function (res) {
layer.close(index);
layer.msg('上传成功!', {icon: 1});
table.reload('postgresql-backup-list');
}
});
}
});
// 头工具栏事件
table.on('toolbar(postgresql-backup-list)', function (obj) {
if (obj.event === 'backup_database') {
index = layer.msg('正在备份数据库,请稍等...', {
icon: 16
, time: 0
});
admin.req({
url: '/api/plugins/postgresql16/createBackup'
, type: 'post'
, data: {
database: params.data.name
}
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
layer.alert('备份失败!');
return false;
}
table.reload('postgresql-backup-list');
layer.msg('备份成功!', {icon: 1});
}
});
}
});
// 行工具事件
table.on('tool(postgresql-backup-list)', function (obj) {
let data = obj.data;
if (obj.event === 'del') {
layer.confirm('确定要删除数据库备份 <b style="color: red;">' + data.name + '</b> 吗?', function (index) {
index = layer.msg('正在删除数据库备份,请稍等...', {
icon: 16
, time: 0
});
admin.req({
url: "/api/plugins/postgresql16/deleteBackup"
, type: 'post'
, data: data
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
layer.msg('数据库备份删除失败,请刷新重试!')
return false;
}
obj.del();
layer.alert('数据库备份' + data.name + '删除成功!');
}
});
});
} else if (obj.event === 'restore') {
layer.confirm('高风险操作,确定要恢复数据库备份 <b style="color: red;">' + data.name + '</b> 吗?', function (index) {
index = layer.msg('正在恢复数据库备份,可能需要较长时间,请勿操作...', {
icon: 16
, time: 0
});
data.database = params.data.name;
admin.req({
url: "/api/plugins/postgresql16/restoreBackup"
, type: 'post'
, data: data
, success: function (result) {
layer.close(index);
if (result.code !== 0) {
layer.msg('数据库备份恢复失败,请刷新重试!')
return false;
}
layer.alert('数据库备份' + data.name + '恢复成功!');
}
});
});
}
});
});
};
</script>
View File
+28
View File
@@ -14,6 +14,7 @@ import (
"panel/app/http/controllers/plugins/php82"
"panel/app/http/controllers/plugins/phpmyadmin"
"panel/app/http/controllers/plugins/postgresql15"
"panel/app/http/controllers/plugins/postgresql16"
"panel/app/http/controllers/plugins/pureftpd"
"panel/app/http/controllers/plugins/redis"
"panel/app/http/controllers/plugins/s3fs"
@@ -124,6 +125,33 @@ func Plugin() {
route.Post("deleteUser", postgresql15Controller.DeleteUser)
route.Post("userPassword", postgresql15Controller.SetUserPassword)
})
facades.Route().Prefix("api/plugins/postgresql16").Middleware(middleware.Jwt()).Group(func(route route.Router) {
postgresql16Controller := postgresql16.NewPostgresql16Controller()
route.Get("status", postgresql16Controller.Status)
route.Post("reload", postgresql16Controller.Reload)
route.Post("start", postgresql16Controller.Start)
route.Post("stop", postgresql16Controller.Stop)
route.Post("restart", postgresql16Controller.Restart)
route.Get("load", postgresql16Controller.Load)
route.Get("config", postgresql16Controller.GetConfig)
route.Post("config", postgresql16Controller.SaveConfig)
route.Get("userConfig", postgresql16Controller.GetUserConfig)
route.Post("userConfig", postgresql16Controller.SaveUserConfig)
route.Get("log", postgresql16Controller.Log)
route.Post("clearLog", postgresql16Controller.ClearLog)
route.Get("database", postgresql16Controller.DatabaseList)
route.Post("addDatabase", postgresql16Controller.AddDatabase)
route.Post("deleteDatabase", postgresql16Controller.DeleteDatabase)
route.Get("backup", postgresql16Controller.BackupList)
route.Post("createBackup", postgresql16Controller.CreateBackup)
route.Post("uploadBackup", postgresql16Controller.UploadBackup)
route.Post("deleteBackup", postgresql16Controller.DeleteBackup)
route.Post("restoreBackup", postgresql16Controller.RestoreBackup)
route.Get("user", postgresql16Controller.UserList)
route.Post("addUser", postgresql16Controller.AddUser)
route.Post("deleteUser", postgresql16Controller.DeleteUser)
route.Post("userPassword", postgresql16Controller.SetUserPassword)
})
facades.Route().Prefix("api/plugins/php74").Middleware(middleware.Jwt()).Group(func(route route.Router) {
php74Controller := php74.NewPhp74Controller()
route.Get("status", php74Controller.Status)
-21
View File
@@ -59,18 +59,6 @@ bantime = 86400
action = %(action_mwl)s
logpath = /var/log/secure
# ssh-END
# pure-ftpd-START
[pure-ftpd]
enabled = true
filter = pure-ftpd
port = 21
maxretry = 5
findtime = 300
bantime = 86400
action = %(action_mwl)s
logpath = /var/log/messages
# pure-ftpd-END
EOF
# 替换端口
sshPort=$(cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}')
@@ -78,15 +66,6 @@ if [ "${sshPort}" == "" ]; then
sshPort="22"
fi
sed -i "s/port = 22/port = ${sshPort}/g" /etc/fail2ban/jail.local
if [ -f "/www/server/pure-ftpd/etc/pure-ftpd.conf" ]; then
ftpPort=$(cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}')
fi
if [ "${ftpPort}" == "" ]; then
ftpPort="21"
sed -i "s/port = 21/port = ${ftpPort}/g" /etc/fail2ban/jail.local
else
sed -i "s/port = 21/port = ${ftpPort}/g" /etc/fail2ban/jail.local
fi
# Debian 的特殊处理
if [ "${OS}" == "debian" ]; then
+6 -5
View File
@@ -29,9 +29,9 @@ mysqlPassword=$(cat /dev/urandom | head -n 16 | md5sum | head -c 16)
cpuCore=$(cat /proc/cpuinfo | grep "processor" | wc -l)
if [[ "${1}" == "80" ]]; then
mysqlVersion="8.0.33"
mysqlVersion="8.0.34"
elif [[ "${1}" == "57" ]]; then
mysqlVersion="5.7.42"
mysqlVersion="5.7.43"
else
echo -e $HR
echo "错误:不支持的 MySQL 版本!"
@@ -49,6 +49,7 @@ if [ "${OS}" == "centos" ]; then
dnf makecache -y
dnf groupinstall "Development Tools" -y
dnf install cmake bison ncurses-devel libtirpc-devel openssl-devel pkg-config openldap-devel libudev-devel cyrus-sasl-devel patchelf rpcgen rpcsvc-proto-devel -y
dnf install gcc-toolset-12-gcc gcc-toolset-12-gcc-c++ gcc-toolset-12-binutils gcc-toolset-12-annobin-annocheck gcc-toolset-12-annobin-plugin-gcc -y
elif [ "${OS}" == "debian" ]; then
apt-get update
apt-get install build-essential cmake bison libncurses5-dev libtirpc-dev libssl-dev pkg-config libldap2-dev libudev-dev libsasl2-dev patchelf -y
@@ -286,11 +287,11 @@ chmod 644 ${mysqlPath}/conf/my.cnf
${mysqlPath}/bin/mysqld --initialize-insecure --user=mysql --basedir=${mysqlPath} --datadir=${mysqlPath}/data
echo "export PATH=${mysqlPath}/bin:\$PATH" >> /etc/profile
echo "export PATH=${mysqlPath}/bin:\$PATH" >> /etc/profile.d/mysql.sh
source /etc/profile
# ARM 环境下,没有 systemd 文件
if [ "${ARCH}" == "aarch64" ]; then
# 检查 systemd 文件是否存在
if [ -f "${mysqlPath}/lib/systemd/system/mysqld.service" ]; then
mkdir -p ${mysqlPath}/lib/systemd/system
cat > ${mysqlPath}/lib/systemd/system/mysqld.service << EOF
# Copyright (c) 2015, 2023, Oracle and/or its affiliates.
+1 -1
View File
@@ -33,7 +33,7 @@ rm -f /usr/lib64/libmysql*
userdel -r mysql
groupdel mysql
sed -i '/export PATH=\/www\/server\/mysql/d' /etc/profile
rm -f /etc/profile.d/mysql.sh
source /etc/profile
panel deletePlugin mysql${1}
+135
View File
@@ -0,0 +1,135 @@
#!/bin/bash
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
: '
Copyright 2022 HaoZi Technology Co., Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
'
HR="+----------------------------------------------------"
ARCH=$(uname -m)
memTotal=$(LC_ALL=C free -m | grep Mem | awk '{print $2}')
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/mysql"
setupPath="/www"
mysqlPath="${setupPath}/server/mysql"
mysqlVersion=""
mysqlPassword=$(cat /dev/urandom | head -n 16 | md5sum | head -c 16)
cpuCore=$(cat /proc/cpuinfo | grep "processor" | wc -l)
if [[ "${1}" == "80" ]]; then
mysqlVersion="8.0.34"
elif [[ "${1}" == "57" ]]; then
mysqlVersion="5.7.43"
else
echo -e $HR
echo "错误:不支持的 MySQL 版本!"
exit 1
fi
if [[ "${memTotal}" -lt "4096" ]] && [[ "${1}" == "80" ]]; then
echo -e $HR
echo "错误:这点内存(${memTotal}M)还想装 MySQL 8.0?洗洗睡吧!"
exit 1
fi
# 安装依赖
if [ "${OS}" == "centos" ]; then
dnf makecache -y
dnf groupinstall "Development Tools" -y
dnf install cmake bison ncurses-devel libtirpc-devel openssl-devel pkg-config openldap-devel libudev-devel cyrus-sasl-devel patchelf rpcgen rpcsvc-proto-devel -y
dnf install gcc-toolset-12-gcc gcc-toolset-12-gcc-c++ gcc-toolset-12-binutils gcc-toolset-12-annobin-annocheck gcc-toolset-12-annobin-plugin-gcc -y
elif [ "${OS}" == "debian" ]; then
apt-get update
apt-get install build-essential cmake bison libncurses5-dev libtirpc-dev libssl-dev pkg-config libldap2-dev libudev-dev libsasl2-dev patchelf -y
else
echo -e $HR
echo "错误:耗子Linux面板不支持该系统"
exit 1
fi
mysqlUserCheck=$(cat /etc/passwd | grep mysql)
if [ "${mysqlUserCheck}" == "" ]; then
groupadd mysql
useradd -s /sbin/nologin -g mysql mysql
fi
# 准备目录
cd ${mysqlPath}
# 下载源码
wget -T 120 -O ${mysqlPath}/mysql-${mysqlVersion}.tar.gz ${downloadUrl}/mysql-boost-${mysqlVersion}.tar.gz
tar -zxvf mysql-${mysqlVersion}.tar.gz
rm -f mysql-${mysqlVersion}.tar.gz
mv mysql-${mysqlVersion} src
# openssl
wget -T 120 -O ${mysqlPath}/openssl-1.1.1u.tar.gz ${downloadUrl}/openssl/openssl-1.1.1u.tar.gz
tar -zxvf openssl-1.1.1u.tar.gz
rm -f openssl-1.1.1u.tar.gz
mv openssl-1.1.1u openssl
cd openssl
./config --prefix=/usr/local/openssl-1.1 --openssldir=/usr/local/openssl-1.1
make -j$(nproc)
make install
echo "/usr/local/openssl-1.1/lib" > /etc/ld.so.conf.d/openssl-1.1.conf
ldconfig
cd ..
rm -rf openssl
# 编译
cd src
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=${mysqlPath} -DMYSQL_DATADIR=${mysqlPath}/data -DSYSCONFDIR=${mysqlPath}/conf -DWITH_MYISAM_STORAGE_ENGINE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_PARTITION_STORAGE_ENGINE=1 -DWITH_ARCHIVE_STORAGE_ENGINE=1 -DWITH_FEDERATED_STORAGE_ENGINE=1 -DWITH_BLACKHOLE_STORAGE_ENGINE=1 -DWITH_EXTRA_CHARSETS=all -DEXTRA_CHARSETS=all -DDEFAULT_CHARSET=utf8mb4 -DDEFAULT_COLLATION=utf8mb4_general_ci -DENABLED_LOCAL_INFILE=1 -DWITH_SYSTEMD=1 -DSYSTEMD_PID_DIR=${mysqlPath} -DWITH_SSL=/usr/local/openssl-1.1 -DWITH_BOOST=../boost
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:MySQL 编译初始化失败,请截图错误信息寻求帮助。"
exit 1
fi
if [[ "${cpuCore}" -gt "1" ]]; then
make -j2
else
make
fi
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:MySQL 编译失败,请截图错误信息寻求帮助。"
exit 1
fi
# 停止已有服务
systemctl stop mysqld
# 安装
make install
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:MySQL 安装失败,请截图错误信息寻求帮助。"
exit 1
fi
# 设置权限
chown -R mysql:mysql ${mysqlPath}
chmod -R 755 ${mysqlPath}
chmod 644 ${mysqlPath}/conf/my.cnf
# 启动服务
systemctl daemon-reload
systemctl enable mysqld
panel writePlugin mysql${1} ${mysqlVersion}
echo -e "${HR}\nMySQL-${1} 升级完成\n${HR}"
+10 -2
View File
@@ -25,11 +25,12 @@ downloadUrl="https://dl.cdn.haozi.net/panel/postgresql"
setupPath="/www"
postgresqlPath="${setupPath}/server/postgresql"
postgresqlVersion=""
postgresqlPassword=$(cat /dev/urandom | head -n 16 | md5sum | head -c 16)
cpuCore=$(cat /proc/cpuinfo | grep "processor" | wc -l)
if [[ "${1}" == "15" ]]; then
postgresqlVersion="15.3"
postgresqlVersion="15.4"
elif [[ "${1}" == "16" ]]; then
postgresqlVersion="16.0"
else
echo -e $HR
echo "错误:不支持的 PostgreSQL 版本!"
@@ -162,6 +163,13 @@ TimeoutSec=infinity
WantedBy=multi-user.target
EOF
# 在 /etc/systemd/logind.conf 设置 RemoveIPC=no,不然会删除 /dev/shm 下的共享内存文件
checkRemoveIPC=$(cat /etc/systemd/logind.conf | grep '^RemoveIPC=no.*$')
if [ "${checkRemoveIPC}" == "" ]; then
echo "RemoveIPC=no" >> /etc/systemd/logind.conf
systemctl restart systemd-logind
fi
# 启动服务
systemctl daemon-reload
systemctl enable postgresql
+100
View File
@@ -0,0 +1,100 @@
#!/bin/bash
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
: '
Copyright 2022 HaoZi Technology Co., Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
'
HR="+----------------------------------------------------"
ARCH=$(uname -m)
memTotal=$(LC_ALL=C free -m | grep Mem | awk '{print $2}')
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/postgresql"
setupPath="/www"
postgresqlPath="${setupPath}/server/postgresql"
postgresqlVersion=""
cpuCore=$(cat /proc/cpuinfo | grep "processor" | wc -l)
if [[ "${1}" == "15" ]]; then
postgresqlVersion="15.4"
elif [[ "${1}" == "16" ]]; then
postgresqlVersion="16.0"
else
echo -e $HR
echo "错误:不支持的 PostgreSQL 版本!"
exit 1
fi
# 安装依赖
if [ "${OS}" == "centos" ]; then
dnf makecache -y
dnf groupinstall "Development Tools" -y
dnf install make gettext zlib-devel readline-devel libicu-devel libxml2-devel libxslt-devel openssl-devel systemd-devel -y
elif [ "${OS}" == "debian" ]; then
apt-get update
apt-get install build-essential make gettext zlib1g-dev libreadline-dev libicu-dev libxml2-dev libxslt-dev libssl-dev libsystemd-dev -y
else
echo -e $HR
echo "错误:耗子Linux面板不支持该系统"
exit 1
fi
# 停止已有服务
systemctl stop postgresql
# 准备目录
rm -rf ${postgresqlPath}/src
cd ${postgresqlPath}
# 下载源码
wget -T 120 -O ${postgresqlPath}/postgresql-${postgresqlVersion}.tar.gz ${downloadUrl}/postgresql-${postgresqlVersion}.tar.gz
tar -zxvf postgresql-${postgresqlVersion}.tar.gz
rm -f postgresql-${postgresqlVersion}.tar.gz
mv postgresql-${postgresqlVersion} src
# 编译
cd src
./configure --prefix=${postgresqlPath} --enable-nls='zh_CN en' --with-icu --with-ssl=openssl --with-systemd --with-libxml --with-libxslt
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:PostgreSQL 编译初始化失败,请截图错误信息寻求帮助。"
exit 1
fi
make -j${cpuCore}
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:PostgreSQL 编译失败,请截图错误信息寻求帮助。"
exit 1
fi
make install
if [ "$?" != "0" ]; then
echo -e $HR
echo "错误:PostgreSQL 安装失败,请截图错误信息寻求帮助。"
exit 1
fi
cd ${postgresqlPath}
rm -rf ${postgresqlPath}/src
# 配置
chown -R postgres:postgres ${postgresqlPath}
chmod -R 700 ${postgresqlPath}
panel writePlugin postgresql${1} ${postgresqlVersion}
systemctl daemon-reload
systemctl restart postgresql
echo -e "${HR}\nPostgreSQL-${1} 升级完成\n${HR}"