mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 03:07:20 +08:00
feat: 初步完成redis操作方法
This commit is contained in:
@@ -67,6 +67,6 @@ type DatabaseServerRepo interface {
|
||||
List(page, limit uint) ([]*DatabaseServer, int64, error)
|
||||
Get(id uint) (*DatabaseServer, error)
|
||||
Create(req *request.DatabaseServerCreate) error
|
||||
Update(req *request.DatabaseServerCreate) error
|
||||
Update(req *request.DatabaseServerUpdate) error
|
||||
Delete(id uint) error
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ func (d databaseRepo) Update(req *request.DatabaseUpdate) error {
|
||||
Remark: req.Remark,
|
||||
}
|
||||
|
||||
return app.Orm.Model(database).Where("id = ?", req.ID).Updates(database).Error
|
||||
return app.Orm.Model(database).Where("id = ?", req.ID).Omit("ServerID").Updates(database).Error
|
||||
}
|
||||
|
||||
func (d databaseRepo) Delete(id uint) error {
|
||||
|
||||
@@ -2,12 +2,14 @@ package data
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/TheTNB/panel/pkg/db"
|
||||
)
|
||||
|
||||
type databaseServerRepo struct{}
|
||||
@@ -42,6 +44,22 @@ func (d databaseServerRepo) Get(id uint) (*biz.DatabaseServer, error) {
|
||||
}
|
||||
|
||||
func (d databaseServerRepo) Create(req *request.DatabaseServerCreate) error {
|
||||
switch biz.DatabaseType(req.Type) {
|
||||
case biz.DatabaseTypeMysql:
|
||||
if _, err := db.NewMySQL(req.Username, req.Password, fmt.Sprintf("%s:%d", req.Host, req.Port)); err != nil {
|
||||
return err
|
||||
}
|
||||
case biz.DatabaseTypePostgresql:
|
||||
if _, err := db.NewPostgres(req.Username, req.Password, req.Host, req.Port, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
case biz.DatabaseTypeRedis:
|
||||
if _, err := db.NewRedis(req.Username, req.Password, fmt.Sprintf("%s:%d", req.Host, req.Port)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
databaseServer := &biz.DatabaseServer{
|
||||
Name: req.Name,
|
||||
Type: biz.DatabaseType(req.Type),
|
||||
@@ -55,8 +73,24 @@ func (d databaseServerRepo) Create(req *request.DatabaseServerCreate) error {
|
||||
return app.Orm.Create(databaseServer).Error
|
||||
}
|
||||
|
||||
func (d databaseServerRepo) Update(req *request.DatabaseServerCreate) error {
|
||||
databaseServer := &biz.DatabaseServer{
|
||||
func (d databaseServerRepo) Update(req *request.DatabaseServerUpdate) error {
|
||||
switch biz.DatabaseType(req.Type) {
|
||||
case biz.DatabaseTypeMysql:
|
||||
if _, err := db.NewMySQL(req.Username, req.Password, fmt.Sprintf("%s:%d", req.Host, req.Port)); err != nil {
|
||||
return err
|
||||
}
|
||||
case biz.DatabaseTypePostgresql:
|
||||
if _, err := db.NewPostgres(req.Username, req.Password, req.Host, req.Port, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
case biz.DatabaseTypeRedis:
|
||||
if _, err := db.NewRedis(req.Username, req.Password, fmt.Sprintf("%s:%d", req.Host, req.Port)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return app.Orm.Model(&biz.DatabaseServer{}).Where("id = ?", req.ID).Select("*").Updates(&biz.DatabaseServer{
|
||||
Name: req.Name,
|
||||
Type: biz.DatabaseType(req.Type),
|
||||
Host: req.Host,
|
||||
@@ -64,9 +98,7 @@ func (d databaseServerRepo) Update(req *request.DatabaseServerCreate) error {
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Remark: req.Remark,
|
||||
}
|
||||
|
||||
return app.Orm.Save(databaseServer).Error
|
||||
}).Error
|
||||
}
|
||||
|
||||
func (d databaseServerRepo) Delete(id uint) error {
|
||||
|
||||
@@ -64,6 +64,24 @@ func Http(r chi.Router) {
|
||||
r.Post("/{id}/obtainCert", website.ObtainCert)
|
||||
})
|
||||
|
||||
r.Route("/database", func(r chi.Router) {
|
||||
r.Use(middleware.MustLogin)
|
||||
database := service.NewDatabaseService()
|
||||
r.Get("/", database.List)
|
||||
r.Post("/", database.Create)
|
||||
r.Put("/{id}", database.Update)
|
||||
r.Delete("/{id}", database.Delete)
|
||||
})
|
||||
|
||||
r.Route("/databaseServer", func(r chi.Router) {
|
||||
r.Use(middleware.MustLogin)
|
||||
database := service.NewDatabaseService()
|
||||
r.Get("/", database.List)
|
||||
r.Post("/", database.Create)
|
||||
r.Put("/{id}", database.Update)
|
||||
r.Delete("/{id}", database.Delete)
|
||||
})
|
||||
|
||||
r.Route("/backup", func(r chi.Router) {
|
||||
r.Use(middleware.MustLogin)
|
||||
backup := service.NewBackupService()
|
||||
|
||||
@@ -140,7 +140,7 @@ func (s *DashboardService) CountInfo(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
if postgresqlInstalled {
|
||||
postgres, err := db.NewPostgres("postgres", "", "127.0.0.1", fmt.Sprintf("%s/server/postgresql/data/pg_hba.conf", app.Root), 5432)
|
||||
postgres, err := db.NewPostgres("postgres", "", "127.0.0.1", 5432, fmt.Sprintf("%s/server/postgresql/data/pg_hba.conf", app.Root))
|
||||
if err == nil {
|
||||
defer postgres.Close()
|
||||
databases, err := postgres.Databases()
|
||||
|
||||
@@ -1 +1,85 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-rat/chix"
|
||||
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
"github.com/TheTNB/panel/internal/data"
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
)
|
||||
|
||||
type Database struct {
|
||||
databaseRepo biz.DatabaseRepo
|
||||
}
|
||||
|
||||
func NewDatabaseService() *Database {
|
||||
return &Database{
|
||||
databaseRepo: data.NewDatabaseRepo(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Database) List(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := Bind[request.Paginate](r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
certs, total, err := s.databaseRepo.List(req.Page, req.Limit)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
Success(w, chix.M{
|
||||
"total": total,
|
||||
"items": certs,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Database) Create(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := Bind[request.DatabaseCreate](r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.databaseRepo.Create(req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *Database) Update(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := Bind[request.DatabaseUpdate](r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.databaseRepo.Update(req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *Database) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := Bind[request.ID](r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.databaseRepo.Delete(req.ID); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
Success(w, nil)
|
||||
}
|
||||
|
||||
@@ -1 +1,85 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-rat/chix"
|
||||
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
"github.com/TheTNB/panel/internal/data"
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
)
|
||||
|
||||
type DatabaseServer struct {
|
||||
databaseServerRepo biz.DatabaseServerRepo
|
||||
}
|
||||
|
||||
func NewDatabaseServerService() *DatabaseServer {
|
||||
return &DatabaseServer{
|
||||
databaseServerRepo: data.NewDatabaseServerRepo(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DatabaseServer) List(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := Bind[request.Paginate](r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
certs, total, err := s.databaseServerRepo.List(req.Page, req.Limit)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
Success(w, chix.M{
|
||||
"total": total,
|
||||
"items": certs,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DatabaseServer) Create(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := Bind[request.DatabaseServerCreate](r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.databaseServerRepo.Create(req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *DatabaseServer) Update(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := Bind[request.DatabaseServerUpdate](r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.databaseServerRepo.Update(req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *DatabaseServer) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := Bind[request.ID](r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.databaseServerRepo.Delete(req.ID); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
Success(w, nil)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ type Postgres struct {
|
||||
port uint
|
||||
}
|
||||
|
||||
func NewPostgres(username, password, address, hbaFile string, port uint) (*Postgres, error) {
|
||||
func NewPostgres(username, password, address string, port uint, hbaFile string) (*Postgres, error) {
|
||||
dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=postgres sslmode=disable", address, port, username, password)
|
||||
if password == "" {
|
||||
dsn = fmt.Sprintf("host=%s port=%d user=%s dbname=postgres sslmode=disable", address, port, username)
|
||||
|
||||
139
pkg/db/redis.go
139
pkg/db/redis.go
@@ -1,9 +1,23 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gomodule/redigo/redis"
|
||||
)
|
||||
|
||||
type RedisKV struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
Type string `json:"type"`
|
||||
Size int64 `json:"size"`
|
||||
Length int64 `json:"length"`
|
||||
TTL int64 `json:"ttl"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type Redis struct {
|
||||
conn redis.Conn
|
||||
username string
|
||||
@@ -29,6 +43,129 @@ func (r *Redis) Close() error {
|
||||
return r.conn.Close()
|
||||
}
|
||||
|
||||
func (r *Redis) Do(command string, args ...any) (any, error) {
|
||||
func (r *Redis) Exec(command string, args ...any) (any, error) {
|
||||
return r.conn.Do(command, args...)
|
||||
}
|
||||
|
||||
func (r *Redis) Database() (int, error) {
|
||||
return redis.Int(r.conn.Do("CONFIG", "GET", "databases"))
|
||||
}
|
||||
|
||||
func (r *Redis) Select(db int) error {
|
||||
_, err := r.conn.Do("SELECT", db)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Redis) Size() (int, error) {
|
||||
return redis.Int(r.conn.Do("DBSIZE"))
|
||||
}
|
||||
|
||||
func (r *Redis) Data(page, pageSize int) ([]RedisKV, error) {
|
||||
result := make([]RedisKV, 0)
|
||||
cursor := 0
|
||||
var keys []string
|
||||
for {
|
||||
values, err := redis.Values(r.conn.Do("SCAN", cursor, "COUNT", 100))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to SCAN: %v", err)
|
||||
}
|
||||
var batch []string
|
||||
_, err = redis.Scan(values, &cursor, &batch)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse SCAN result: %v", err)
|
||||
}
|
||||
keys = append(keys, batch...)
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
start := (page - 1) * pageSize
|
||||
end := start + pageSize
|
||||
if start >= len(keys) {
|
||||
return []RedisKV{}, nil
|
||||
}
|
||||
if end > len(keys) {
|
||||
end = len(keys)
|
||||
}
|
||||
|
||||
paged := keys[start:end]
|
||||
for _, key := range paged {
|
||||
kv := RedisKV{
|
||||
Key: key,
|
||||
}
|
||||
|
||||
keyType, err := redis.String(r.conn.Do("TYPE", key))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
kv.Type = keyType
|
||||
|
||||
ttl, err := redis.Int64(r.conn.Do("TTL", key))
|
||||
if err == nil {
|
||||
kv.TTL = ttl
|
||||
}
|
||||
idleTime, err := redis.Int64(r.conn.Do("OBJECT", "IDLETIME", key))
|
||||
if err == nil {
|
||||
kv.UpdatedAt = time.Now().Add(-time.Duration(idleTime) * time.Second)
|
||||
} else {
|
||||
kv.UpdatedAt = time.Now()
|
||||
}
|
||||
memory, err := redis.Int64(r.conn.Do("MEMORY", "USAGE", key))
|
||||
if err == nil {
|
||||
kv.Size = memory
|
||||
}
|
||||
|
||||
var value any
|
||||
switch keyType {
|
||||
case "string":
|
||||
if value, err = redis.String(r.conn.Do("GET", key)); err == nil {
|
||||
kv.Length = int64(len(value.(string)))
|
||||
}
|
||||
case "list":
|
||||
if value, err = redis.Strings(r.conn.Do("LRANGE", key, 0, -1)); err == nil {
|
||||
kv.Length, _ = redis.Int64(r.conn.Do("LLEN", key))
|
||||
}
|
||||
case "set":
|
||||
if value, err = redis.Strings(r.conn.Do("SMEMBERS", key)); err == nil {
|
||||
kv.Length, _ = redis.Int64(r.conn.Do("SCARD", key))
|
||||
}
|
||||
case "zset":
|
||||
if members, err := redis.Strings(r.conn.Do("ZRANGE", key, 0, -1, "WITHSCORES")); err == nil {
|
||||
kv.Length, _ = redis.Int64(r.conn.Do("ZCARD", key))
|
||||
zsetMap := make(map[string]string)
|
||||
for i := 0; i < len(members); i += 2 {
|
||||
zsetMap[members[i]] = members[i+1]
|
||||
}
|
||||
value = zsetMap
|
||||
}
|
||||
case "hash":
|
||||
if value, err = redis.StringMap(r.conn.Do("HGETALL", key)); err == nil {
|
||||
kv.Length, _ = redis.Int64(r.conn.Do("HLEN", key))
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if kv.Length > 500 {
|
||||
value = fmt.Sprintf("data is too long, can't display")
|
||||
}
|
||||
|
||||
if str, ok := value.(string); ok {
|
||||
kv.Value = str
|
||||
} else {
|
||||
encoded, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
kv.Value = string(encoded)
|
||||
}
|
||||
|
||||
result = append(result, kv)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user