mirror of
https://github.com/tnb-labs/panel.git
synced 2026-08-31 01:12:17 +08:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 266f12d15a | |||
| 3a18b36df2 | |||
| ebcc022418 | |||
| aa0fb2afcc | |||
| bde2c50696 | |||
| 0945949b37 | |||
| 4fcd65da70 | |||
| afb12dd664 | |||
| 8c5d0e905e | |||
| 92e77720f7 | |||
| 124912368e | |||
| 084f0a06ca | |||
| c08fd0af7f | |||
| 3229685729 | |||
| 77ae021a2f | |||
| 69cf5dcb65 | |||
| 9bd9833ca9 | |||
| 5fb58476ba | |||
| da4a04ad87 | |||
| ae4736d86e | |||
| 198c56cb25 | |||
| 4950b48fc9 | |||
| 3f323588c0 | |||
| e651a8ed6b | |||
| bc9274a8b7 | |||
| 1047374c2b | |||
| 1f2f51fb55 | |||
| 872af0b298 | |||
| bc22e23cda | |||
| 4ca2446dc3 | |||
| 840d6824f0 |
@@ -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
|
||||
|
||||
@@ -30,4 +30,5 @@ archives:
|
||||
- storage/*
|
||||
- database/*
|
||||
- scripts/*
|
||||
- resources/*
|
||||
- panel-example.conf
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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`
|
||||
)
|
||||
|
||||
@@ -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`
|
||||
)
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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`
|
||||
)
|
||||
@@ -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
@@ -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",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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("重启完成")
|
||||
|
||||
@@ -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>
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}"
|
||||
@@ -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
|
||||
|
||||
@@ -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}"
|
||||
Reference in New Issue
Block a user