mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-09 15:06:37 +08:00
feat: Add installation support for Redis Cluster (#9561)
This commit is contained in:
parent
5d4ab23d95
commit
a74c6dd522
20 changed files with 60 additions and 19 deletions
|
@ -11,13 +11,13 @@ import (
|
||||||
// @Tags Database Redis
|
// @Tags Database Redis
|
||||||
// @Summary Load redis status info
|
// @Summary Load redis status info
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.OperationWithName true "request"
|
// @Param request body dto.LoadRedisStatus true "request"
|
||||||
// @Success 200 {object} dto.RedisStatus
|
// @Success 200 {object} dto.RedisStatus
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Security Timestamp
|
// @Security Timestamp
|
||||||
// @Router /databases/redis/status [post]
|
// @Router /databases/redis/status [post]
|
||||||
func (b *BaseApi) LoadRedisStatus(c *gin.Context) {
|
func (b *BaseApi) LoadRedisStatus(c *gin.Context) {
|
||||||
var req dto.OperationWithName
|
var req dto.LoadRedisStatus
|
||||||
if err := helper.CheckBind(&req, c); err != nil {
|
if err := helper.CheckBind(&req, c); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,8 +89,8 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
|
||||||
var containerID string
|
var containerID string
|
||||||
var initCmd []string
|
var initCmd []string
|
||||||
switch source {
|
switch source {
|
||||||
case "redis":
|
case "redis", "redis-cluster":
|
||||||
containerID, initCmd, err = loadRedisInitCmd(c)
|
containerID, initCmd, err = loadRedisInitCmd(c, source)
|
||||||
case "ollama":
|
case "ollama":
|
||||||
containerID, initCmd, err = loadOllamaInitCmd(c)
|
containerID, initCmd, err = loadOllamaInitCmd(c)
|
||||||
case "container":
|
case "container":
|
||||||
|
@ -127,7 +127,7 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
|
||||||
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
|
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadRedisInitCmd(c *gin.Context) (string, []string, error) {
|
func loadRedisInitCmd(c *gin.Context, redisType string) (string, []string, error) {
|
||||||
name := c.Query("name")
|
name := c.Query("name")
|
||||||
from := c.Query("from")
|
from := c.Query("from")
|
||||||
commands := []string{"exec", "-it"}
|
commands := []string{"exec", "-it"}
|
||||||
|
@ -136,7 +136,7 @@ func loadRedisInitCmd(c *gin.Context) (string, []string, error) {
|
||||||
return "", nil, fmt.Errorf("no such database in db, err: %v", err)
|
return "", nil, fmt.Errorf("no such database in db, err: %v", err)
|
||||||
}
|
}
|
||||||
if from == "local" {
|
if from == "local" {
|
||||||
redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: name, Type: "redis"})
|
redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: name, Type: redisType})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, fmt.Errorf("no such app in db, err: %v", err)
|
return "", nil, fmt.Errorf("no such app in db, err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -319,3 +319,8 @@ type DatabaseDelete struct {
|
||||||
ForceDelete bool `json:"forceDelete"`
|
ForceDelete bool `json:"forceDelete"`
|
||||||
DeleteBackup bool `json:"deleteBackup"`
|
DeleteBackup bool `json:"deleteBackup"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LoadRedisStatus struct {
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
|
Type string `json:"type" validate:"required"`
|
||||||
|
}
|
||||||
|
|
|
@ -482,6 +482,8 @@ func (a *AppInstallService) GetServices(key string) ([]response.AppService, erro
|
||||||
types = []string{constant.AppMysql, constant.AppMysqlCluster}
|
types = []string{constant.AppMysql, constant.AppMysqlCluster}
|
||||||
case constant.AppPostgresql:
|
case constant.AppPostgresql:
|
||||||
types = []string{constant.AppPostgresql, constant.AppPostgresqlCluster}
|
types = []string{constant.AppPostgresql, constant.AppPostgresqlCluster}
|
||||||
|
case constant.AppRedis:
|
||||||
|
types = []string{constant.AppRedis, constant.AppRedisCluster}
|
||||||
}
|
}
|
||||||
|
|
||||||
dbs, _ := databaseRepo.GetList(repo.WithTypes(types))
|
dbs, _ := databaseRepo.GetList(repo.WithTypes(types))
|
||||||
|
|
|
@ -125,6 +125,7 @@ var DatabaseKeys = map[string]uint{
|
||||||
constant.AppMemcached: 11211,
|
constant.AppMemcached: 11211,
|
||||||
constant.AppMysqlCluster: 3306,
|
constant.AppMysqlCluster: 3306,
|
||||||
constant.AppPostgresqlCluster: 5432,
|
constant.AppPostgresqlCluster: 5432,
|
||||||
|
constant.AppRedisCluster: 6379,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ToolKeys = map[string]uint{
|
var ToolKeys = map[string]uint{
|
||||||
|
|
|
@ -54,6 +54,8 @@ func (u *DBCommonService) LoadDatabaseFile(req dto.OperationWithNameAndType) (st
|
||||||
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/postgresql/%s/data/postgresql.conf", req.Name))
|
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/postgresql/%s/data/postgresql.conf", req.Name))
|
||||||
case "redis-conf":
|
case "redis-conf":
|
||||||
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/redis/%s/conf/redis.conf", req.Name))
|
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/redis/%s/conf/redis.conf", req.Name))
|
||||||
|
case "redis-cluster-conf":
|
||||||
|
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/redis-cluster/%s/conf/redis.conf", req.Name))
|
||||||
}
|
}
|
||||||
if _, err := os.Stat(filePath); err != nil {
|
if _, err := os.Stat(filePath); err != nil {
|
||||||
return "", buserr.New("ErrHttpReqNotFound")
|
return "", buserr.New("ErrHttpReqNotFound")
|
||||||
|
|
|
@ -28,7 +28,7 @@ type IRedisService interface {
|
||||||
UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error
|
UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error
|
||||||
ChangePassword(info dto.ChangeRedisPass) error
|
ChangePassword(info dto.ChangeRedisPass) error
|
||||||
|
|
||||||
LoadStatus(req dto.OperationWithName) (*dto.RedisStatus, error)
|
LoadStatus(req dto.LoadRedisStatus) (*dto.RedisStatus, error)
|
||||||
LoadConf(req dto.OperationWithName) (*dto.RedisConf, error)
|
LoadConf(req dto.OperationWithName) (*dto.RedisConf, error)
|
||||||
LoadPersistenceConf(req dto.OperationWithName) (*dto.RedisPersistence, error)
|
LoadPersistenceConf(req dto.OperationWithName) (*dto.RedisPersistence, error)
|
||||||
|
|
||||||
|
@ -129,8 +129,8 @@ func (u *RedisService) UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *RedisService) LoadStatus(req dto.OperationWithName) (*dto.RedisStatus, error) {
|
func (u *RedisService) LoadStatus(req dto.LoadRedisStatus) (*dto.RedisStatus, error) {
|
||||||
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name)
|
redisInfo, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,8 +108,8 @@ export const loadRemoteAccess = (type: string, database: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// redis
|
// redis
|
||||||
export const loadRedisStatus = (database: string) => {
|
export const loadRedisStatus = (type: string, database: string) => {
|
||||||
return http.post<Database.RedisStatus>(`/databases/redis/status`, { name: database });
|
return http.post<Database.RedisStatus>(`/databases/redis/status`, { type: type, name: database });
|
||||||
};
|
};
|
||||||
export const loadRedisConf = (database: string) => {
|
export const loadRedisConf = (database: string) => {
|
||||||
return http.post<Database.RedisConf>(`/databases/redis/conf`, { name: database });
|
return http.post<Database.RedisConf>(`/databases/redis/conf`, { name: database });
|
||||||
|
|
|
@ -3657,6 +3657,8 @@ const message = {
|
||||||
master: 'Master Node',
|
master: 'Master Node',
|
||||||
slave: 'Slave Node',
|
slave: 'Slave Node',
|
||||||
replicaStatus: 'Master-Slave Status',
|
replicaStatus: 'Master-Slave Status',
|
||||||
|
unhealthyDeleteError: 'The installation node status is abnormal, please check the node list and try again!',
|
||||||
|
replicaStatusError: 'Status acquisition is abnormal, please check the master node.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -3520,6 +3520,9 @@ const message = {
|
||||||
master: 'マスターノード',
|
master: 'マスターノード',
|
||||||
slave: 'スレーブノード',
|
slave: 'スレーブノード',
|
||||||
replicaStatus: 'マスタースレーブステータス',
|
replicaStatus: 'マスタースレーブステータス',
|
||||||
|
unhealthyDeleteError:
|
||||||
|
'インストールノードのステータスが異常です。ノードリストを確認してから再試行してください!',
|
||||||
|
replicaStatusError: 'ステータスの取得が異常です。マスターノードを確認してください。',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -3458,6 +3458,8 @@ const message = {
|
||||||
master: '마스터 노드',
|
master: '마스터 노드',
|
||||||
slave: '슬레이브 노드',
|
slave: '슬레이브 노드',
|
||||||
replicaStatus: '마스터-슬레이브 상태',
|
replicaStatus: '마스터-슬레이브 상태',
|
||||||
|
unhealthyDeleteError: '설치 노드 상태가 비정상입니다. 노드 목록을 확인한 후 다시 시도하세요!',
|
||||||
|
replicaStatusError: '상태 획득이 비정상입니다. 마스터 노드를 확인하세요.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -3601,6 +3601,8 @@ const message = {
|
||||||
master: 'Node Utama',
|
master: 'Node Utama',
|
||||||
slave: 'Node Hamba',
|
slave: 'Node Hamba',
|
||||||
replicaStatus: 'Utama-Hamba Status',
|
replicaStatus: 'Utama-Hamba Status',
|
||||||
|
unhealthyDeleteError: 'Status nod pemasangan tidak normal, sila periksa senarai nod dan cuba lagi!',
|
||||||
|
replicaStatusError: 'Pengambilan status tidak normal, sila periksa nod utama.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -3608,6 +3608,9 @@ const message = {
|
||||||
master: 'Nó Mestre',
|
master: 'Nó Mestre',
|
||||||
slave: 'Nó Escravo',
|
slave: 'Nó Escravo',
|
||||||
replicaStatus: 'Status Mestre-Escravo',
|
replicaStatus: 'Status Mestre-Escravo',
|
||||||
|
unhealthyDeleteError:
|
||||||
|
'O status do nó de instalação está anormal, verifique a lista de nós e tente novamente!',
|
||||||
|
replicaStatusError: 'A aquisição do status está anormal, verifique o nó mestre.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -3599,6 +3599,9 @@ const message = {
|
||||||
master: 'Главный узел',
|
master: 'Главный узел',
|
||||||
slave: 'Подчиненный узел',
|
slave: 'Подчиненный узел',
|
||||||
replicaStatus: 'Состояние мастер-слейв',
|
replicaStatus: 'Состояние мастер-слейв',
|
||||||
|
unhealthyDeleteError:
|
||||||
|
'Состояние узла установки аномально, пожалуйста, проверьте список узлов и повторите попытку!',
|
||||||
|
replicaStatusError: 'Получение статуса аномально, пожалуйста, проверьте главный узел.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -3697,6 +3697,9 @@ const message = {
|
||||||
master: 'Главный узел',
|
master: 'Главный узел',
|
||||||
slave: 'Подчиненный узел',
|
slave: 'Подчиненный узел',
|
||||||
replicaStatus: 'Ana-Çalışan Durumu',
|
replicaStatus: 'Ana-Çalışan Durumu',
|
||||||
|
unhealthyDeleteError:
|
||||||
|
'Yükleme düğümü durumu anormal, lütfen düğüm listesini kontrol edin ve tekrar deneyin!',
|
||||||
|
replicaStatusError: 'Durum alımı anormal, lütfen ana düğümü kontrol edin.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -3402,6 +3402,8 @@ const message = {
|
||||||
master: '主節點',
|
master: '主節點',
|
||||||
slave: '從節點',
|
slave: '從節點',
|
||||||
replicaStatus: '主從狀態',
|
replicaStatus: '主從狀態',
|
||||||
|
unhealthyDeleteError: '安裝節點狀態異常,請在節點列表檢查後重試!',
|
||||||
|
replicaStatusError: '狀態獲取異常,請檢查主節點。',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -3382,6 +3382,8 @@ const message = {
|
||||||
master: '主节点',
|
master: '主节点',
|
||||||
slave: '从节点',
|
slave: '从节点',
|
||||||
replicaStatus: '主从状态',
|
replicaStatus: '主从状态',
|
||||||
|
unhealthyDeleteError: '安装节点状态异常,请在节点列表检查后重试!',
|
||||||
|
replicaStatusError: '状态获取异常 请检查主节点',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -188,7 +188,7 @@ const onSetting = async () => {
|
||||||
isOnSetting.value = true;
|
isOnSetting.value = true;
|
||||||
terminalRef.value?.onClose(false);
|
terminalRef.value?.onClose(false);
|
||||||
terminalShow.value = false;
|
terminalShow.value = false;
|
||||||
settingRef.value!.acceptParams({ status: redisStatus.value, database: currentDBName.value });
|
settingRef.value!.acceptParams({ status: redisStatus.value, database: currentDBName.value, type: appKey.value });
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadHeight = () => {
|
const loadHeight = () => {
|
||||||
|
@ -244,7 +244,7 @@ const changeDatabase = async () => {
|
||||||
|
|
||||||
const loadDBOptions = async () => {
|
const loadDBOptions = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await listDatabases('redis');
|
const res = await listDatabases('redis,redis-cluster');
|
||||||
let datas = res.data || [];
|
let datas = res.data || [];
|
||||||
dbOptionsLocal.value = [];
|
dbOptionsLocal.value = [];
|
||||||
dbOptionsRemote.value = [];
|
dbOptionsRemote.value = [];
|
||||||
|
@ -318,8 +318,10 @@ const initTerminal = async () => {
|
||||||
isRefresh.value = !isRefresh.value;
|
isRefresh.value = !isRefresh.value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await checkAppInstalled('redis', currentDBName.value)
|
console.log(currentDBName.value);
|
||||||
|
await checkAppInstalled(currentDB.value.type, currentDBName.value)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
console.log(res.data);
|
||||||
redisIsExist.value = res.data.isExist;
|
redisIsExist.value = res.data.isExist;
|
||||||
redisStatus.value = res.data.status;
|
redisStatus.value = res.data.status;
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
@ -328,7 +330,7 @@ const initTerminal = async () => {
|
||||||
terminalShow.value = true;
|
terminalShow.value = true;
|
||||||
terminalRef.value.acceptParams({
|
terminalRef.value.acceptParams({
|
||||||
endpoint: '/api/v2/containers/exec',
|
endpoint: '/api/v2/containers/exec',
|
||||||
args: `source=redis&name=${currentDBName.value}&from=${currentDB.value.from}`,
|
args: `source=${currentDB.value.type}&name=${currentDBName.value}&from=${currentDB.value.from}`,
|
||||||
error: '',
|
error: '',
|
||||||
initCmd: '',
|
initCmd: '',
|
||||||
});
|
});
|
||||||
|
|
|
@ -153,6 +153,7 @@ const useOld = ref(false);
|
||||||
|
|
||||||
const redisStatus = ref();
|
const redisStatus = ref();
|
||||||
const database = ref();
|
const database = ref();
|
||||||
|
const dbType = ref('redis');
|
||||||
|
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
const redisConf = ref();
|
const redisConf = ref();
|
||||||
|
@ -163,6 +164,7 @@ const settingShow = ref<boolean>(false);
|
||||||
interface DialogProps {
|
interface DialogProps {
|
||||||
database: string;
|
database: string;
|
||||||
status: string;
|
status: string;
|
||||||
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const changeTab = (val: string) => {
|
const changeTab = (val: string) => {
|
||||||
|
@ -179,7 +181,7 @@ const changeTab = (val: string) => {
|
||||||
loadForm();
|
loadForm();
|
||||||
break;
|
break;
|
||||||
case 'status':
|
case 'status':
|
||||||
statusRef.value!.acceptParams({ status: redisStatus.value, database: database.value });
|
statusRef.value!.acceptParams({ status: redisStatus.value, database: database.value, type: dbType.value });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -191,6 +193,7 @@ const changeLoading = (status: boolean) => {
|
||||||
const acceptParams = (prop: DialogProps): void => {
|
const acceptParams = (prop: DialogProps): void => {
|
||||||
redisStatus.value = prop.status;
|
redisStatus.value = prop.status;
|
||||||
database.value = prop.database;
|
database.value = prop.database;
|
||||||
|
dbType.value = prop.type;
|
||||||
settingShow.value = true;
|
settingShow.value = true;
|
||||||
changeTab('status');
|
changeTab('status');
|
||||||
};
|
};
|
||||||
|
@ -274,7 +277,7 @@ const submitForm = async () => {
|
||||||
|
|
||||||
const getDefaultConfig = async () => {
|
const getDefaultConfig = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
await getAppDefaultConfig('redis', '')
|
await getAppDefaultConfig(dbType.value, '')
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
redisConf.value = res.data;
|
redisConf.value = res.data;
|
||||||
useOld.value = true;
|
useOld.value = true;
|
||||||
|
@ -323,7 +326,7 @@ const loadForm = async () => {
|
||||||
const loadConfFile = async () => {
|
const loadConfFile = async () => {
|
||||||
useOld.value = false;
|
useOld.value = false;
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
await loadDBFile('redis-conf', database.value)
|
await loadDBFile(dbType.value + '-conf', database.value)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
redisConf.value = res.data;
|
redisConf.value = res.data;
|
||||||
|
|
|
@ -163,21 +163,25 @@ const redisStatus = reactive({
|
||||||
|
|
||||||
const database = ref();
|
const database = ref();
|
||||||
const statusShow = ref(false);
|
const statusShow = ref(false);
|
||||||
|
const dbType = ref('redis');
|
||||||
|
|
||||||
interface DialogProps {
|
interface DialogProps {
|
||||||
database: string;
|
database: string;
|
||||||
status: string;
|
status: string;
|
||||||
|
type: string;
|
||||||
}
|
}
|
||||||
const acceptParams = (prop: DialogProps): void => {
|
const acceptParams = (prop: DialogProps): void => {
|
||||||
statusShow.value = true;
|
statusShow.value = true;
|
||||||
database.value = prop.database;
|
database.value = prop.database;
|
||||||
|
dbType.value = prop.type;
|
||||||
if (prop.status === 'Running') {
|
if (prop.status === 'Running') {
|
||||||
loadStatus();
|
loadStatus();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadStatus = async () => {
|
const loadStatus = async () => {
|
||||||
const res = await loadRedisStatus(database.value);
|
console.log('loadStatus', database.value);
|
||||||
|
const res = await loadRedisStatus(dbType.value, database.value);
|
||||||
let hit = (
|
let hit = (
|
||||||
(Number(res.data.keyspace_hits) / (Number(res.data.keyspace_hits) + Number(res.data.keyspace_misses))) *
|
(Number(res.data.keyspace_hits) / (Number(res.data.keyspace_hits) + Number(res.data.keyspace_misses))) *
|
||||||
100
|
100
|
||||||
|
|
Loading…
Add table
Reference in a new issue