diff --git a/internal/biz/database.go b/internal/biz/database.go index 8bd89eae0a..14bfb41a66 100644 --- a/internal/biz/database.go +++ b/internal/biz/database.go @@ -20,10 +20,12 @@ type Database struct { Server string `json:"server"` ServerID uint `json:"server_id"` Encoding string `json:"encoding"` + Comment string `json:"comment"` } type DatabaseRepo interface { List(page, limit uint) ([]*Database, int64, error) Create(req *request.DatabaseCreate) error Delete(serverID uint, name string) error + Comment(req *request.DatabaseComment) error } diff --git a/internal/data/database.go b/internal/data/database.go index dad0c55bc0..0b0503efcf 100644 --- a/internal/data/database.go +++ b/internal/data/database.go @@ -1,6 +1,7 @@ package data import ( + "errors" "fmt" "slices" @@ -53,6 +54,7 @@ func (r databaseRepo) List(page, limit uint) ([]*biz.Database, int64, error) { Server: server.Name, ServerID: server.ID, Encoding: item.Encoding, + Comment: item.Comment, }) } } @@ -134,3 +136,23 @@ func (r databaseRepo) Delete(serverID uint, name string) error { return nil } + +func (r databaseRepo) Comment(req *request.DatabaseComment) error { + server, err := NewDatabaseServerRepo().Get(req.ServerID) + if err != nil { + return err + } + + switch server.Type { + case biz.DatabaseTypeMysql: + return errors.New("mysql not support database comment") + case biz.DatabaseTypePostgresql: + postgres, err := db.NewPostgres(server.Username, server.Password, server.Host, server.Port) + if err != nil { + return err + } + return postgres.DatabaseComment(req.Name, req.Comment) + } + + return nil +} diff --git a/internal/http/request/database.go b/internal/http/request/database.go index 739b1f7251..24be05370b 100644 --- a/internal/http/request/database.go +++ b/internal/http/request/database.go @@ -14,3 +14,9 @@ type DatabaseDelete struct { ServerID uint `form:"server_id" json:"server_id" validate:"required,exists=database_servers id"` Name string `form:"name" json:"name" validate:"required"` } + +type DatabaseComment struct { + ServerID uint `form:"server_id" json:"server_id" validate:"required,exists=database_servers id"` + Name string `form:"name" json:"name" validate:"required"` + Comment string `form:"comment" json:"comment"` +} diff --git a/internal/route/http.go b/internal/route/http.go index 481494a99d..297c16a471 100644 --- a/internal/route/http.go +++ b/internal/route/http.go @@ -67,6 +67,7 @@ func Http(r chi.Router) { r.Get("/", database.List) r.Post("/", database.Create) r.Delete("/", database.Delete) + r.Post("/comment", database.Comment) }) r.Route("/databaseServer", func(r chi.Router) { diff --git a/internal/service/base.go b/internal/service/base.go index 7ab1cc6682..a9d220c3f3 100644 --- a/internal/service/base.go +++ b/internal/service/base.go @@ -39,6 +39,7 @@ func Success(w http.ResponseWriter, data any) { func Error(w http.ResponseWriter, code int, format string, args ...any) { render := chix.NewRender(w) defer render.Release() + render.Header(chix.HeaderContentType, chix.MIMEApplicationJSONCharsetUTF8) // must before Status() render.Status(code) render.JSON(&ErrorResponse{ Message: fmt.Sprintf(format, args...), @@ -49,6 +50,7 @@ func Error(w http.ResponseWriter, code int, format string, args ...any) { func ErrorSystem(w http.ResponseWriter) { render := chix.NewRender(w) defer render.Release() + render.Header(chix.HeaderContentType, chix.MIMEApplicationJSONCharsetUTF8) // must before Status() render.Status(http.StatusInternalServerError) render.JSON(&ErrorResponse{ Message: "系统内部错误", diff --git a/internal/service/database.go b/internal/service/database.go index d12abfc22f..2c32c97805 100644 --- a/internal/service/database.go +++ b/internal/service/database.go @@ -68,3 +68,18 @@ func (s *Database) Delete(w http.ResponseWriter, r *http.Request) { Success(w, nil) } + +func (s *Database) Comment(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.DatabaseComment](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + + if err = s.databaseRepo.Comment(req); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + Success(w, nil) +} diff --git a/pkg/db/postgres.go b/pkg/db/postgres.go index 1ac1a1dcc7..e024809864 100644 --- a/pkg/db/postgres.go +++ b/pkg/db/postgres.go @@ -95,6 +95,11 @@ func (r *Postgres) DatabaseSize(name string) (int64, error) { return size, nil } +func (r *Postgres) DatabaseComment(name, comment string) error { + _, err := r.Exec(fmt.Sprintf("COMMENT ON DATABASE %s IS '%s'", name, comment)) + return err +} + func (r *Postgres) UserCreate(user, password string) error { _, err := r.Exec(fmt.Sprintf("CREATE USER %s WITH PASSWORD '%s'", user, password)) if err != nil { @@ -221,7 +226,11 @@ func (r *Postgres) Users() ([]types.PostgresUser, error) { func (r *Postgres) Databases() ([]types.PostgresDatabase, error) { query := ` - SELECT d.datname, pg_catalog.pg_get_userbyid(d.datdba), pg_catalog.pg_encoding_to_char(d.encoding) + SELECT + d.datname, + pg_catalog.pg_get_userbyid(d.datdba), + pg_catalog.pg_encoding_to_char(d.encoding), + COALESCE(pg_catalog.shobj_description(d.oid, 'pg_database'), '') FROM pg_catalog.pg_database d WHERE datistemplate = false; ` @@ -234,7 +243,7 @@ func (r *Postgres) Databases() ([]types.PostgresDatabase, error) { var databases []types.PostgresDatabase for rows.Next() { var db types.PostgresDatabase - if err := rows.Scan(&db.Name, &db.Owner, &db.Encoding); err != nil { + if err := rows.Scan(&db.Name, &db.Owner, &db.Encoding, &db.Comment); err != nil { return nil, err } if slices.Contains([]string{"template0", "template1", "postgres"}, db.Name) { diff --git a/pkg/types/postgres.go b/pkg/types/postgres.go index d22f5d1ca2..bbd0901f6a 100644 --- a/pkg/types/postgres.go +++ b/pkg/types/postgres.go @@ -9,4 +9,5 @@ type PostgresDatabase struct { Name string `json:"name"` Owner string `json:"owner"` Encoding string `json:"encoding"` + Comment string `json:"comment"` } diff --git a/web/src/api/panel/database/index.ts b/web/src/api/panel/database/index.ts index 67c856ee51..1c153be0c4 100644 --- a/web/src/api/panel/database/index.ts +++ b/web/src/api/panel/database/index.ts @@ -7,6 +7,9 @@ export default { create: (data: any) => http.Post(`/database`, data), // 删除数据库 delete: (server_id: number, name: string) => http.Delete(`/database`, { server_id, name }), + // 更新评论 + comment: (server_id: number, name: string, comment: string) => + http.Post(`/database/comment`, { server_id, name, comment }), // 获取数据库服务器列表 serverList: (page: number, limit: number) => http.Get('/databaseServer', { params: { page, limit } }), diff --git a/web/src/views/database/DatabaseList.vue b/web/src/views/database/DatabaseList.vue index 0f385c9469..b683a1ef79 100644 --- a/web/src/views/database/DatabaseList.vue +++ b/web/src/views/database/DatabaseList.vue @@ -1,6 +1,6 @@ <script setup lang="ts"> import { renderIcon } from '@/utils' -import { NButton, NPopconfirm, NTag } from 'naive-ui' +import { NButton, NInput, NPopconfirm, NTag } from 'naive-ui' import database from '@/api/panel/database' @@ -50,6 +50,23 @@ const columns: any = [ }) } }, + { + title: '备注', + key: 'comment', + minWidth: 250, + resizable: true, + ellipsis: { tooltip: true }, + render(row: any) { + return h(NInput, { + size: 'small', + value: row.comment, + onBlur: () => handleComment(row), + onUpdateValue(v) { + row.comment = v + } + }) + } + }, { title: '操作', key: 'actions', @@ -104,6 +121,12 @@ const handleDelete = async (serverID: number, name: string) => { }) } +const handleComment = (row: any) => { + database.comment(row.server_id, row.name, row.comment).then(() => { + window.$message.success('修改成功') + }) +} + onMounted(() => { window.$bus.on('database:refresh', () => { refresh() @@ -119,7 +142,7 @@ onUnmounted(() => { <n-data-table striped remote - :scroll-x="800" + :scroll-x="1000" :loading="loading" :columns="columns" :data="data" diff --git a/web/src/views/database/ServerList.vue b/web/src/views/database/ServerList.vue index 123466f660..cfc1506440 100644 --- a/web/src/views/database/ServerList.vue +++ b/web/src/views/database/ServerList.vue @@ -215,7 +215,7 @@ onUnmounted(() => { <n-data-table striped remote - :scroll-x="1200" + :scroll-x="1500" :loading="loading" :columns="columns" :data="data" diff --git a/web/src/views/database/UserList.vue b/web/src/views/database/UserList.vue index 9f2e0eb7cb..f8a1dd4982 100644 --- a/web/src/views/database/UserList.vue +++ b/web/src/views/database/UserList.vue @@ -188,7 +188,7 @@ onUnmounted(() => { <n-data-table striped remote - :scroll-x="1400" + :scroll-x="1500" :loading="loading" :columns="columns" :data="data"