1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-03-17 03:04:46 +08:00

feat: PostgreSQL 增加权限设置 (#3537)

This commit is contained in:
ssongliu 2024-01-09 12:43:33 +08:00 committed by GitHub
parent 51077b0597
commit 9a905750af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 423 additions and 58 deletions

View File

@ -85,6 +85,28 @@ func (b *BaseApi) UpdatePostgresqlDescription(c *gin.Context) {
helper.SuccessWithData(c, nil) helper.SuccessWithData(c, nil)
} }
// @Tags Database Postgresql
// @Summary Change postgresql privileges
// @Description 修改 postgresql 用户权限
// @Accept json
// @Param request body dto.ChangeDBInfo true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/pg/privileges [post]
// @x-panel-log {"bodyKeys":["database", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新数据库 [database] 用户 [username] 权限","formatEN":"Update [user] privileges of database [database]"}
func (b *BaseApi) ChangePostgresqlPrivileges(c *gin.Context) {
var req dto.PostgresqlPrivileges
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := postgresqlService.ChangePrivileges(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Postgresql // @Tags Database Postgresql
// @Summary Change postgresql password // @Summary Change postgresql password
// @Description 修改 postgresql 密码 // @Description 修改 postgresql 密码

View File

@ -19,7 +19,7 @@ type PostgresqlDBInfo struct {
Format string `json:"format"` Format string `json:"format"`
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
Permission string `json:"permission"` SuperUser bool `json:"superUser"`
BackupCount int `json:"backupCount"` BackupCount int `json:"backupCount"`
Description string `json:"description"` Description string `json:"description"`
} }
@ -39,15 +39,23 @@ type PostgresqlDBCreate struct {
Format string `json:"format"` Format string `json:"format"`
Username string `json:"username" validate:"required"` Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"` Password string `json:"password" validate:"required"`
Permission string `json:"permission" validate:"required"` SuperUser bool `json:"superUser"`
Description string `json:"description"` Description string `json:"description"`
} }
type PostgresqlBindUser struct { type PostgresqlBindUser struct {
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
Database string `json:"database" validate:"required"` Database string `json:"database" validate:"required"`
Username string `json:"username" validate:"required"` Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"` Password string `json:"password" validate:"required"`
SuperUser bool `json:"superUser"`
}
type PostgresqlPrivileges struct {
Name string `json:"name" validate:"required"`
Database string `json:"database" validate:"required"`
Username string `json:"username" validate:"required"`
SuperUser bool `json:"superUser"`
} }
type PostgresqlLoadDB struct { type PostgresqlLoadDB struct {

View File

@ -8,5 +8,6 @@ type DatabasePostgresql struct {
Format string `json:"format" gorm:"type:varchar(64);not null"` Format string `json:"format" gorm:"type:varchar(64);not null"`
Username string `json:"username" gorm:"type:varchar(256);not null"` Username string `json:"username" gorm:"type:varchar(256);not null"`
Password string `json:"password" gorm:"type:varchar(256);not null"` Password string `json:"password" gorm:"type:varchar(256);not null"`
SuperUser bool `json:"superUser" gorm:"type:varchar(64)" `
Description string `json:"description" gorm:"type:varchar(256);"` Description string `json:"description" gorm:"type:varchar(256);"`
} }

View File

@ -4,13 +4,14 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/1Panel-dev/1Panel/backend/buserr"
"io/fs" "io/fs"
"os" "os"
"path" "path"
"strings" "strings"
"time" "time"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/constant"
@ -307,13 +308,13 @@ func reCreatePostgresqlDB(dbID uint, database model.Database, oldEnv string) (*m
oldUser, _ := envMap["PANEL_DB_USER"].(string) oldUser, _ := envMap["PANEL_DB_USER"].(string)
oldPassword, _ := envMap["PANEL_DB_USER_PASSWORD"].(string) oldPassword, _ := envMap["PANEL_DB_USER_PASSWORD"].(string)
createDB, err := postgresqlService.Create(context.Background(), dto.PostgresqlDBCreate{ createDB, err := postgresqlService.Create(context.Background(), dto.PostgresqlDBCreate{
Name: oldName, Name: oldName,
From: database.From, From: database.From,
Database: database.Name, Database: database.Name,
Format: "UTF8", Format: "UTF8",
Username: oldUser, Username: oldUser,
Password: oldPassword, Password: oldPassword,
Permission: "%", SuperUser: true,
}) })
if err != nil { if err != nil {
return nil, envMap, err return nil, envMap, err

View File

@ -29,6 +29,7 @@ type IPostgresqlService interface {
BindUser(req dto.PostgresqlBindUser) error BindUser(req dto.PostgresqlBindUser) error
Create(ctx context.Context, req dto.PostgresqlDBCreate) (*model.DatabasePostgresql, error) Create(ctx context.Context, req dto.PostgresqlDBCreate) (*model.DatabasePostgresql, error)
LoadFromRemote(database string) error LoadFromRemote(database string) error
ChangePrivileges(req dto.PostgresqlPrivileges) error
ChangePassword(info dto.ChangeDBInfo) error ChangePassword(info dto.ChangeDBInfo) error
UpdateDescription(req dto.UpdateDescription) error UpdateDescription(req dto.UpdateDescription) error
DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]string, error) DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]string, error)
@ -84,6 +85,9 @@ func (u *PostgresqlService) ListDBOption() ([]dto.PostgresqlOption, error) {
} }
func (u *PostgresqlService) BindUser(req dto.PostgresqlBindUser) error { func (u *PostgresqlService) BindUser(req dto.PostgresqlBindUser) error {
if cmd.CheckIllegal(req.Name, req.Username, req.Password) {
return buserr.New(constant.ErrCmdIllegal)
}
dbItem, err := postgresqlRepo.Get(postgresqlRepo.WithByPostgresqlName(req.Database), commonRepo.WithByName(req.Name)) dbItem, err := postgresqlRepo.Get(postgresqlRepo.WithByPostgresqlName(req.Database), commonRepo.WithByName(req.Name))
if err != nil { if err != nil {
return err return err
@ -94,10 +98,11 @@ func (u *PostgresqlService) BindUser(req dto.PostgresqlBindUser) error {
return err return err
} }
if err := pgClient.CreateUser(client.CreateInfo{ if err := pgClient.CreateUser(client.CreateInfo{
Name: req.Name, Name: req.Name,
Username: req.Username, Username: req.Username,
Password: req.Password, Password: req.Password,
Timeout: 300, SuperUser: req.SuperUser,
Timeout: 300,
}, false); err != nil { }, false); err != nil {
return err return err
} }
@ -106,8 +111,9 @@ func (u *PostgresqlService) BindUser(req dto.PostgresqlBindUser) error {
return fmt.Errorf("decrypt database db password failed, err: %v", err) return fmt.Errorf("decrypt database db password failed, err: %v", err)
} }
if err := postgresqlRepo.Update(dbItem.ID, map[string]interface{}{ if err := postgresqlRepo.Update(dbItem.ID, map[string]interface{}{
"username": req.Username, "username": req.Username,
"password": pass, "password": pass,
"super_user": req.SuperUser,
}); err != nil { }); err != nil {
return err return err
} }
@ -142,10 +148,11 @@ func (u *PostgresqlService) Create(ctx context.Context, req dto.PostgresqlDBCrea
createItem.PostgresqlName = req.Database createItem.PostgresqlName = req.Database
defer cli.Close() defer cli.Close()
if err := cli.Create(client.CreateInfo{ if err := cli.Create(client.CreateInfo{
Name: req.Name, Name: req.Name,
Username: req.Username, Username: req.Username,
Password: req.Password, Password: req.Password,
Timeout: 300, SuperUser: req.SuperUser,
Timeout: 300,
}); err != nil { }); err != nil {
return nil, err return nil, err
} }
@ -306,6 +313,31 @@ func (u *PostgresqlService) Delete(ctx context.Context, req dto.PostgresqlDBDele
return nil return nil
} }
func (u *PostgresqlService) ChangePrivileges(req dto.PostgresqlPrivileges) error {
if cmd.CheckIllegal(req.Database, req.Username) {
return buserr.New(constant.ErrCmdIllegal)
}
dbItem, err := postgresqlRepo.Get(postgresqlRepo.WithByPostgresqlName(req.Database), commonRepo.WithByName(req.Name))
if err != nil {
return err
}
cli, err := LoadPostgresqlClientByFrom(req.Database)
if err != nil {
return err
}
defer cli.Close()
if err := cli.ChangePrivileges(client.Privileges{Username: req.Username, SuperUser: req.SuperUser, Timeout: 300}); err != nil {
return err
}
if err := postgresqlRepo.Update(dbItem.ID, map[string]interface{}{
"super_user": req.SuperUser,
}); err != nil {
return err
}
return nil
}
func (u *PostgresqlService) ChangePassword(req dto.ChangeDBInfo) error { func (u *PostgresqlService) ChangePassword(req dto.ChangeDBInfo) error {
if cmd.CheckIllegal(req.Value) { if cmd.CheckIllegal(req.Value) {
return buserr.New(constant.ErrCmdIllegal) return buserr.New(constant.ErrCmdIllegal)

View File

@ -64,6 +64,7 @@ func Init() {
migrations.AddTablePHPExtensions, migrations.AddTablePHPExtensions,
migrations.AddTableDatabasePostgresql, migrations.AddTableDatabasePostgresql,
migrations.AddPostgresqlSuperUser,
}) })
if err := m.Migrate(); err != nil { if err := m.Migrate(); err != nil {
global.LOG.Error(err) global.LOG.Error(err)

View File

@ -148,3 +148,13 @@ var AddTableDatabasePostgresql = &gormigrate.Migration{
return nil return nil
}, },
} }
var AddPostgresqlSuperUser = &gormigrate.Migration{
ID: "20231225-add-postgresql-super_user",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.DatabasePostgresql{}); err != nil {
return err
}
return nil
},
}

View File

@ -60,6 +60,7 @@ func (s *DatabaseRouter) InitRouter(Router *gin.RouterGroup) {
cmdRouter.POST("/pg/bind", baseApi.BindPostgresqlUser) cmdRouter.POST("/pg/bind", baseApi.BindPostgresqlUser)
cmdRouter.POST("/pg/del/check", baseApi.DeleteCheckPostgresql) cmdRouter.POST("/pg/del/check", baseApi.DeleteCheckPostgresql)
cmdRouter.POST("/pg/del", baseApi.DeletePostgresql) cmdRouter.POST("/pg/del", baseApi.DeletePostgresql)
cmdRouter.POST("/pg/privileges", baseApi.ChangePostgresqlPrivileges)
cmdRouter.POST("/pg/password", baseApi.ChangePostgresqlPassword) cmdRouter.POST("/pg/password", baseApi.ChangePostgresqlPassword)
cmdRouter.POST("/pg/description", baseApi.UpdatePostgresqlDescription) cmdRouter.POST("/pg/description", baseApi.UpdatePostgresqlDescription)
} }

View File

@ -16,6 +16,7 @@ type PostgresqlClient interface {
Create(info client.CreateInfo) error Create(info client.CreateInfo) error
CreateUser(info client.CreateInfo, withDeleteDB bool) error CreateUser(info client.CreateInfo, withDeleteDB bool) error
Delete(info client.DeleteInfo) error Delete(info client.DeleteInfo) error
ChangePrivileges(info client.Privileges) error
ChangePassword(info client.PasswordChangeInfo) error ChangePassword(info client.PasswordChangeInfo) error
Backup(info client.BackupInfo) error Backup(info client.BackupInfo) error

View File

@ -16,9 +16,17 @@ type DBInfo struct {
} }
type CreateInfo struct { type CreateInfo struct {
Name string `json:"name"` Name string `json:"name"`
Username string `json:"userName"` Username string `json:"userName"`
Password string `json:"password"` Password string `json:"password"`
SuperUser bool `json:"superUser"`
Timeout uint `json:"timeout"` // second
}
type Privileges struct {
Username string `json:"userName"`
SuperUser bool `json:"superUser"`
Timeout uint `json:"timeout"` // second Timeout uint `json:"timeout"` // second
} }

View File

@ -46,6 +46,15 @@ func (r *Local) Create(info CreateInfo) error {
return nil return nil
} }
func (r *Local) ChangePrivileges(info Privileges) error {
super := "SUPERUSER"
if !info.SuperUser {
super = "NOSUPERUSER"
}
changeSql := fmt.Sprintf("ALTER USER \"%s\" WITH %s", info.Username, super)
return r.ExecSQL(changeSql, info.Timeout)
}
func (r *Local) CreateUser(info CreateInfo, withDeleteDB bool) error { func (r *Local) CreateUser(info CreateInfo, withDeleteDB bool) error {
createSql := fmt.Sprintf("CREATE USER \"%s\" WITH PASSWORD '%s'", info.Username, info.Password) createSql := fmt.Sprintf("CREATE USER \"%s\" WITH PASSWORD '%s'", info.Username, info.Password)
if err := r.ExecSQL(createSql, info.Timeout); err != nil { if err := r.ExecSQL(createSql, info.Timeout); err != nil {
@ -61,6 +70,18 @@ func (r *Local) CreateUser(info CreateInfo, withDeleteDB bool) error {
} }
return err return err
} }
if info.SuperUser {
if err := r.ChangePrivileges(Privileges{SuperUser: true, Username: info.Username, Timeout: info.Timeout}); err != nil {
if withDeleteDB {
_ = r.Delete(DeleteInfo{
Name: info.Name,
Username: info.Username,
ForceDelete: true,
Timeout: 300})
}
return err
}
}
grantStr := fmt.Sprintf("GRANT ALL PRIVILEGES ON DATABASE \"%s\" TO \"%s\"", info.Name, info.Username) grantStr := fmt.Sprintf("GRANT ALL PRIVILEGES ON DATABASE \"%s\" TO \"%s\"", info.Name, info.Username)
if err := r.ExecSQL(grantStr, info.Timeout); err != nil { if err := r.ExecSQL(grantStr, info.Timeout); err != nil {
if withDeleteDB { if withDeleteDB {

View File

@ -65,6 +65,18 @@ func (r *Remote) CreateUser(info CreateInfo, withDeleteDB bool) error {
} }
return err return err
} }
if info.SuperUser {
if err := r.ChangePrivileges(Privileges{SuperUser: true, Username: info.Username, Timeout: info.Timeout}); err != nil {
if withDeleteDB {
_ = r.Delete(DeleteInfo{
Name: info.Name,
Username: info.Username,
ForceDelete: true,
Timeout: 300})
}
return err
}
}
grantSql := fmt.Sprintf("GRANT ALL PRIVILEGES ON DATABASE \"%s\" TO \"%s\"", info.Name, info.Username) grantSql := fmt.Sprintf("GRANT ALL PRIVILEGES ON DATABASE \"%s\" TO \"%s\"", info.Name, info.Username)
if err := r.ExecSQL(grantSql, info.Timeout); err != nil { if err := r.ExecSQL(grantSql, info.Timeout); err != nil {
if withDeleteDB { if withDeleteDB {
@ -97,6 +109,14 @@ func (r *Remote) Delete(info DeleteInfo) error {
return nil return nil
} }
func (r *Remote) ChangePrivileges(info Privileges) error {
super := "SUPERUSER"
if !info.SuperUser {
super = "NOSUPERUSER"
}
return r.ExecSQL(fmt.Sprintf("ALTER USER \"%s\" WITH %s", info.Username, super), info.Timeout)
}
func (r *Remote) ChangePassword(info PasswordChangeInfo) error { func (r *Remote) ChangePassword(info PasswordChangeInfo) error {
return r.ExecSQL(fmt.Sprintf("ALTER USER \"%s\" WITH ENCRYPTED PASSWORD '%s'", info.Username, info.Password), info.Timeout) return r.ExecSQL(fmt.Sprintf("ALTER USER \"%s\" WITH ENCRYPTED PASSWORD '%s'", info.Username, info.Password), info.Timeout)
} }

View File

@ -5016,6 +5016,49 @@ const docTemplate = `{
} }
} }
}, },
"/databases/pg/privileges": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "修改 postgresql 用户权限",
"consumes": [
"application/json"
],
"tags": [
"Database Postgresql"
],
"summary": "Change postgresql privileges",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.ChangeDBInfo"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [
"database",
"username"
],
"formatEN": "Update [user] privileges of database [database]",
"formatZH": "更新数据库 [database] 用户 [username] 权限",
"paramKeys": []
}
}
},
"/databases/pg/search": { "/databases/pg/search": {
"post": { "post": {
"security": [ "security": [
@ -16942,6 +16985,9 @@ const docTemplate = `{
"password": { "password": {
"type": "string" "type": "string"
}, },
"superUser": {
"type": "boolean"
},
"username": { "username": {
"type": "string" "type": "string"
} }
@ -16954,7 +17000,6 @@ const docTemplate = `{
"from", "from",
"name", "name",
"password", "password",
"permission",
"username" "username"
], ],
"properties": { "properties": {
@ -16980,8 +17025,8 @@ const docTemplate = `{
"password": { "password": {
"type": "string" "type": "string"
}, },
"permission": { "superUser": {
"type": "string" "type": "boolean"
}, },
"username": { "username": {
"type": "string" "type": "string"

View File

@ -5009,6 +5009,49 @@
} }
} }
}, },
"/databases/pg/privileges": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "修改 postgresql 用户权限",
"consumes": [
"application/json"
],
"tags": [
"Database Postgresql"
],
"summary": "Change postgresql privileges",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.ChangeDBInfo"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [
"database",
"username"
],
"formatEN": "Update [user] privileges of database [database]",
"formatZH": "更新数据库 [database] 用户 [username] 权限",
"paramKeys": []
}
}
},
"/databases/pg/search": { "/databases/pg/search": {
"post": { "post": {
"security": [ "security": [
@ -16935,6 +16978,9 @@
"password": { "password": {
"type": "string" "type": "string"
}, },
"superUser": {
"type": "boolean"
},
"username": { "username": {
"type": "string" "type": "string"
} }
@ -16947,7 +16993,6 @@
"from", "from",
"name", "name",
"password", "password",
"permission",
"username" "username"
], ],
"properties": { "properties": {
@ -16973,8 +17018,8 @@
"password": { "password": {
"type": "string" "type": "string"
}, },
"permission": { "superUser": {
"type": "string" "type": "boolean"
}, },
"username": { "username": {
"type": "string" "type": "string"

View File

@ -2008,6 +2008,8 @@ definitions:
type: string type: string
password: password:
type: string type: string
superUser:
type: boolean
username: username:
type: string type: string
required: required:
@ -2033,8 +2035,8 @@ definitions:
type: string type: string
password: password:
type: string type: string
permission: superUser:
type: string type: boolean
username: username:
type: string type: string
required: required:
@ -2042,7 +2044,6 @@ definitions:
- from - from
- name - name
- password - password
- permission
- username - username
type: object type: object
dto.PostgresqlDBDelete: dto.PostgresqlDBDelete:
@ -8160,6 +8161,34 @@ paths:
formatEN: Update database [name] password formatEN: Update database [name] password
formatZH: 更新数据库 [name] 密码 formatZH: 更新数据库 [name] 密码
paramKeys: [] paramKeys: []
/databases/pg/privileges:
post:
consumes:
- application/json
description: 修改 postgresql 用户权限
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.ChangeDBInfo'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: Change postgresql privileges
tags:
- Database Postgresql
x-panel-log:
BeforeFunctions: []
bodyKeys:
- database
- username
formatEN: Update [user] privileges of database [database]
formatZH: 更新数据库 [database] 用户 [username] 权限
paramKeys: []
/databases/pg/search: /databases/pg/search:
post: post:
consumes: consumes:

View File

@ -151,6 +151,13 @@ export namespace Database {
database: string; database: string;
username: string; username: string;
password: string; password: string;
superUser: boolean;
}
export interface PgChangePrivileges {
name: string;
database: string;
username: string;
superUser: boolean;
} }
export interface PostgresqlDBDelete { export interface PostgresqlDBDelete {
id: number; id: number;
@ -159,18 +166,6 @@ export namespace Database {
forceDelete: boolean; forceDelete: boolean;
deleteBackup: boolean; deleteBackup: boolean;
} }
export interface PostgresqlStatus {
uptime: string;
version: string;
max_connections: string;
autovacuum: string;
current_connections: string;
hit_ratio: string;
shared_buffers: string;
buffers_clean: string;
maxwritten_clean: string;
buffers_backend_fsync: string;
}
export interface PostgresqlDBDeleteCheck { export interface PostgresqlDBDeleteCheck {
id: number; id: number;
type: string; type: string;
@ -199,7 +194,7 @@ export namespace Database {
format: string; format: string;
username: string; username: string;
password: string; password: string;
permission: string; superUser: boolean;
description: string; description: string;
} }
export interface PostgresqlDBInfo { export interface PostgresqlDBInfo {
@ -211,7 +206,7 @@ export namespace Database {
format: string; format: string;
username: string; username: string;
password: string; password: string;
permission: string; superUser: boolean;
description: string; description: string;
} }
export interface ChangeInfo { export interface ChangeInfo {

View File

@ -27,6 +27,9 @@ export const addPostgresqlDB = (params: Database.PostgresqlDBCreate) => {
export const bindPostgresqlUser = (params: Database.PgBind) => { export const bindPostgresqlUser = (params: Database.PgBind) => {
return http.post(`/databases/pg/bind`, params, TimeoutEnum.T_40S); return http.post(`/databases/pg/bind`, params, TimeoutEnum.T_40S);
}; };
export const changePrivileges = (params: Database.PgChangePrivileges) => {
return http.post(`/databases/pg/privileges`, params, TimeoutEnum.T_40S);
};
export const searchPostgresqlDBs = (params: Database.SearchDBWithPage) => { export const searchPostgresqlDBs = (params: Database.SearchDBWithPage) => {
return http.post<ResPage<Database.PostgresqlDBInfo>>(`/databases/pg/search`, params); return http.post<ResPage<Database.PostgresqlDBInfo>>(`/databases/pg/search`, params);
}; };

View File

@ -383,7 +383,9 @@ const message = {
loadFromRemote: 'Load from server', loadFromRemote: 'Load from server',
userBind: 'Bind User', userBind: 'Bind User',
pgBind: 'Bind User (Binding existing users is not currently supported)', pgBindHelper:
'This operation is used to create a new user and bind it to the target database. Currently, selecting users already existing in the database is not supported.',
pgSuperUser: 'Super User',
loadFromRemoteHelper: loadFromRemoteHelper:
'This action will synchronize the database info on the server to 1Panel, do you want to continue?', 'This action will synchronize the database info on the server to 1Panel, do you want to continue?',
passwordHelper: 'Unable to retrieve, please modify', passwordHelper: 'Unable to retrieve, please modify',

View File

@ -376,7 +376,8 @@ const message = {
loadFromRemote: '從服務器獲取', loadFromRemote: '從服務器獲取',
userBind: '綁定使用者', userBind: '綁定使用者',
pgBind: '綁定用戶 (暫不支持綁定已有用戶)', pgBindHelper: '此操作用於創建新使用者並將其綁定到目標資料庫暫不支援選擇已存在於資料庫中的使用者',
pgSuperUser: '超級使用者',
loadFromRemoteHelper: '此操作將同步服務器上數據庫信息到 1Panel是否繼續', loadFromRemoteHelper: '此操作將同步服務器上數據庫信息到 1Panel是否繼續',
passwordHelper: '無法獲取密碼請修改', passwordHelper: '無法獲取密碼請修改',
local: '本地', local: '本地',

View File

@ -376,7 +376,8 @@ const message = {
loadFromRemote: '从服务器获取', loadFromRemote: '从服务器获取',
userBind: '绑定用户', userBind: '绑定用户',
pgBind: '绑定用户 (暂不支持绑定已有用户)', pgBindHelper: '该操作用于创建新用户并将其绑定到目标数据库暂不支持选择已存在于数据库中的用户',
pgSuperUser: '超级用户',
loadFromRemoteHelper: '此操作将同步服务器上数据库信息到 1Panel是否继续', loadFromRemoteHelper: '此操作将同步服务器上数据库信息到 1Panel是否继续',
passwordHelper: '无法获取密码请修改', passwordHelper: '无法获取密码请修改',
local: '本地', local: '本地',

View File

@ -7,12 +7,16 @@
<el-form v-loading="loading" ref="changeFormRef" :model="form" :rules="rules" label-position="top"> <el-form v-loading="loading" ref="changeFormRef" :model="form" :rules="rules" label-position="top">
<el-row type="flex" justify="center"> <el-row type="flex" justify="center">
<el-col :span="22"> <el-col :span="22">
<el-form-item :label="$t('database.pgBind')" prop="username"> <el-alert type="warning" :title="$t('database.pgBindHelper')" :closable="false" />
<el-form-item class="mt-5" :label="$t('database.userBind')" prop="username">
<el-input v-model="form.username" /> <el-input v-model="form.username" />
</el-form-item> </el-form-item>
<el-form-item :label="$t('commons.login.password')" prop="password"> <el-form-item :label="$t('commons.login.password')" prop="password">
<el-input type="password" clearable show-password v-model="form.password" /> <el-input type="password" clearable show-password v-model="form.password" />
</el-form-item> </el-form-item>
<el-form-item :label="$t('database.permission')" prop="superUser">
<el-checkbox v-model="form.superUser">{{ $t('database.pgSuperUser') }}</el-checkbox>
</el-form-item>
</el-col> </el-col>
</el-row> </el-row>
</el-form> </el-form>
@ -49,6 +53,7 @@ const form = reactive({
name: '', name: '',
username: '', username: '',
password: '', password: '',
superUser: true,
}); });
const confirmDialogRef = ref(); const confirmDialogRef = ref();
@ -81,6 +86,7 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
database: form.database, database: form.database,
username: form.username, username: form.username,
password: form.password, password: form.password,
superUser: form.superUser,
}; };
loading.value = true; loading.value = true;
await bindPostgresqlUser(param) await bindPostgresqlUser(param)

View File

@ -20,6 +20,9 @@
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item :label="$t('database.permission')" prop="superUser">
<el-checkbox v-model="form.superUser">{{ $t('database.pgSuperUser') }}</el-checkbox>
</el-form-item>
<el-form-item :label="$t('commons.table.type')" prop="database"> <el-form-item :label="$t('commons.table.type')" prop="database">
<el-tag>{{ form.database + ' [' + form.type + ']' }}</el-tag> <el-tag>{{ form.database + ' [' + form.type + ']' }}</el-tag>
@ -66,8 +69,7 @@ const form = reactive({
format: '', format: '',
username: '', username: '',
password: '', password: '',
permission: '', superUser: true,
permissionIPs: '',
description: '', description: '',
}); });
const rules = reactive({ const rules = reactive({
@ -91,8 +93,7 @@ const acceptParams = (params: DialogProps): void => {
form.database = params.database; form.database = params.database;
form.format = 'UTF8'; form.format = 'UTF8';
form.username = ''; form.username = '';
form.permission = '%'; form.superUser = true;
form.permissionIPs = '';
form.description = ''; form.description = '';
random(); random();
createVisible.value = true; createVisible.value = true;

View File

@ -207,6 +207,7 @@
</template> </template>
</el-dialog> </el-dialog>
<PrivilegesDialog ref="privilegesRef" @search="search" />
<BindDialog ref="bindRef" @search="search" /> <BindDialog ref="bindRef" @search="search" />
<PasswordDialog ref="passwordRef" @search="search" /> <PasswordDialog ref="passwordRef" @search="search" />
<RootPasswordDialog ref="connRef" /> <RootPasswordDialog ref="connRef" />
@ -226,6 +227,7 @@ import BindDialog from '@/views/database/postgresql/bind/index.vue';
import OperateDialog from '@/views/database/postgresql/create/index.vue'; import OperateDialog from '@/views/database/postgresql/create/index.vue';
import DeleteDialog from '@/views/database/postgresql/delete/index.vue'; import DeleteDialog from '@/views/database/postgresql/delete/index.vue';
import PasswordDialog from '@/views/database/postgresql/password/index.vue'; import PasswordDialog from '@/views/database/postgresql/password/index.vue';
import PrivilegesDialog from '@/views/database/postgresql/privileges/index.vue';
import RootPasswordDialog from '@/views/database/postgresql/conn/index.vue'; import RootPasswordDialog from '@/views/database/postgresql/conn/index.vue';
import AppResources from '@/views/database/postgresql/check/index.vue'; import AppResources from '@/views/database/postgresql/check/index.vue';
import AppStatus from '@/components/app-status/index.vue'; import AppStatus from '@/components/app-status/index.vue';
@ -264,6 +266,7 @@ const currentDBName = ref();
const checkRef = ref(); const checkRef = ref();
const deleteRef = ref(); const deleteRef = ref();
const bindRef = ref(); const bindRef = ref();
const privilegesRef = ref();
const pgadminPort = ref(); const pgadminPort = ref();
const dashboardName = ref(); const dashboardName = ref();
@ -508,6 +511,21 @@ const buttons = [
onChangePassword(row); onChangePassword(row);
}, },
}, },
{
label: i18n.global.t('database.permission'),
disabled: (row: Database.PostgresqlDBInfo) => {
return !row.username;
},
click: (row: Database.PostgresqlDBInfo) => {
let param = {
database: currentDBName.value,
name: row.name,
username: row.username,
superUser: row.superUser,
};
privilegesRef.value.acceptParams(param);
},
},
{ {
label: i18n.global.t('database.backupList'), label: i18n.global.t('database.backupList'),
click: (row: Database.PostgresqlDBInfo) => { click: (row: Database.PostgresqlDBInfo) => {

View File

@ -0,0 +1,93 @@
<template>
<div>
<el-drawer v-model="changeVisible" :destroy-on-close="true" :close-on-click-modal="false" width="30%">
<template #header>
<DrawerHeader :header="$t('database.permission')" :resource="form.name" :back="handleClose" />
</template>
<el-form v-loading="loading" :model="form" label-position="top">
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('database.userBind')">
<el-tag>
{{ form.username }}
</el-tag>
</el-form-item>
<el-form-item :label="$t('database.permission')" prop="superUser">
<el-checkbox v-model="form.superUser">{{ $t('database.pgSuperUser') }}</el-checkbox>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="loading" @click="changeVisible = false">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabled="loading" type="primary" @click="onSubmit()">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-drawer>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import i18n from '@/lang';
import { ElForm } from 'element-plus';
import { changePrivileges } from '@/api/modules/database';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { MsgSuccess } from '@/utils/message';
const loading = ref();
const changeVisible = ref(false);
const form = reactive({
database: '',
name: '',
username: '',
superUser: true,
});
interface DialogProps {
database: string;
name: string;
username: string;
superUser: boolean;
}
const acceptParams = (params: DialogProps): void => {
form.database = params.database;
form.name = params.name;
form.username = params.username;
form.superUser = params.superUser;
changeVisible.value = true;
};
const emit = defineEmits<{ (e: 'search'): void }>();
const handleClose = () => {
changeVisible.value = false;
};
const onSubmit = async () => {
let param = {
name: form.name,
database: form.database,
username: form.username,
superUser: form.superUser,
};
loading.value = true;
await changePrivileges(param)
.then(() => {
loading.value = false;
emit('search');
changeVisible.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
})
.catch(() => {
loading.value = false;
});
};
defineExpose({
acceptParams,
});
</script>