mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 16:00:29 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
486cd5d0ad |
@@ -86,17 +86,23 @@ func (b *BaseApi) CheckHasCli(c *gin.Context) {
|
||||
|
||||
// @Tags Database Redis
|
||||
// @Summary Install redis-cli
|
||||
// @Success 200
|
||||
// @Accept json
|
||||
// @Param request body dto.RedisCliInstall true "request"
|
||||
// @Success 200 {string} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /databases/redis/install/cli [post]
|
||||
func (b *BaseApi) InstallCli(c *gin.Context) {
|
||||
if err := redisService.InstallCli(); err != nil {
|
||||
var req dto.RedisCliInstall
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := redisService.InstallCli(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.Success(c)
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Database Redis
|
||||
|
||||
@@ -22,6 +22,10 @@ type DBBaseInfo struct {
|
||||
Port int64 `json:"port"`
|
||||
}
|
||||
|
||||
type RedisCliInstall struct {
|
||||
TaskID string `json:"taskID" validate:"omitempty,uuid"`
|
||||
}
|
||||
|
||||
// mysql
|
||||
type MysqlDBSearch struct {
|
||||
PageInfo
|
||||
|
||||
@@ -8,6 +8,12 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
@@ -17,12 +23,15 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
dockerclient "github.com/docker/docker/client"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
type RedisService struct{}
|
||||
|
||||
// The CLI container is shared by all remote Redis databases on this node.
|
||||
var redisCliInstallMutex sync.Mutex
|
||||
|
||||
type IRedisService interface {
|
||||
UpdateConf(req dto.RedisConfUpdate) error
|
||||
UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error
|
||||
@@ -33,7 +42,7 @@ type IRedisService interface {
|
||||
LoadPersistenceConf(req dto.LoadRedisStatus) (*dto.RedisPersistence, error)
|
||||
|
||||
CheckHasCli() bool
|
||||
InstallCli() error
|
||||
InstallCli(req dto.RedisCliInstall) (string, error)
|
||||
}
|
||||
|
||||
func NewIRedisService() IRedisService {
|
||||
@@ -61,30 +70,65 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
|
||||
}
|
||||
|
||||
func (u *RedisService) CheckHasCli() bool {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer client.Close()
|
||||
containerLists, err := client.ContainerList(context.Background(), container.ListOptions{})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, item := range containerLists {
|
||||
if strings.ReplaceAll(item.Names[0], "/", "") == "1Panel-redis-cli-tools" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
installed, _ := u.checkCliInstalled()
|
||||
return installed
|
||||
}
|
||||
|
||||
func (u *RedisService) InstallCli() error {
|
||||
func (u *RedisService) checkCliInstalled() (bool, error) {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer client.Close()
|
||||
info, err := client.ContainerInspect(context.Background(), "1Panel-redis-cli-tools")
|
||||
if dockerclient.IsErrNotFound(err) {
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return info.ContainerJSONBase != nil && info.State != nil && info.State.Running, nil
|
||||
}
|
||||
|
||||
func (u *RedisService) InstallCli(req dto.RedisCliInstall) (string, error) {
|
||||
if !redisCliInstallMutex.TryLock() {
|
||||
return "", buserr.New("TaskIsExecuting")
|
||||
}
|
||||
defer redisCliInstallMutex.Unlock()
|
||||
item := dto.ContainerOperate{
|
||||
TaskID: req.TaskID,
|
||||
Name: "1Panel-redis-cli-tools",
|
||||
Image: "redis:7.4.4",
|
||||
Networks: []dto.ContainerNetwork{{Network: "1panel-network"}},
|
||||
}
|
||||
return NewIContainerService().ContainerCreate(item, false)
|
||||
running, err := taskRepo.GetFirst(
|
||||
repo.WithByName(task.GetTaskName(item.Name, task.TaskCreate, task.TaskScopeContainer)),
|
||||
repo.WithByType(task.TaskScopeContainer),
|
||||
taskRepo.WithByStatus(constant.StatusExecuting),
|
||||
)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return "", err
|
||||
}
|
||||
if running.ID != "" {
|
||||
return running.ID, nil
|
||||
}
|
||||
installed, err := u.checkCliInstalled()
|
||||
if err != nil || installed {
|
||||
return "", err
|
||||
}
|
||||
if item.TaskID == "" {
|
||||
item.TaskID = uuid.NewString()
|
||||
}
|
||||
if _, err := taskRepo.GetFirst(taskRepo.WithByID(item.TaskID)); !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "", buserr.New("TaskIsExecuting")
|
||||
}
|
||||
if err := NewIContainerService().ContainerCreate(item, true); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return item.TaskID, nil
|
||||
}
|
||||
|
||||
func (u *RedisService) ChangePassword(req dto.ChangeRedisPass) error {
|
||||
|
||||
@@ -15092,9 +15092,26 @@ const docTemplate = `{
|
||||
},
|
||||
"/databases/redis/install/cli": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"in": "body",
|
||||
"name": "request",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.RedisCliInstall"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
@@ -40149,6 +40166,14 @@ const docTemplate = `{
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"dto.RedisCliInstall": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"taskID": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.RedisConf": {
|
||||
"properties": {
|
||||
"containerName": {
|
||||
|
||||
@@ -15088,9 +15088,26 @@
|
||||
},
|
||||
"/databases/redis/install/cli": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"in": "body",
|
||||
"name": "request",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.RedisCliInstall"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
@@ -40145,6 +40162,14 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"dto.RedisCliInstall": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"taskID": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.RedisConf": {
|
||||
"properties": {
|
||||
"containerName": {
|
||||
|
||||
@@ -193,8 +193,10 @@ export const redisPersistenceConf = (type: string, database: string) => {
|
||||
export const checkRedisCli = () => {
|
||||
return http.get<boolean>(`/databases/redis/check`);
|
||||
};
|
||||
export const installRedisCli = () => {
|
||||
return http.post(`/databases/redis/install/cli`, {}, TimeoutEnum.T_5M);
|
||||
export const installRedisCli = (taskID: string, node: string) => {
|
||||
return http.post<string>(`/databases/redis/install/cli?operateNode=${encodeURIComponent(node)}`, {
|
||||
taskID,
|
||||
});
|
||||
};
|
||||
export const changeRedisPassword = (database: string, password: string) => {
|
||||
if (password) {
|
||||
|
||||
@@ -81,7 +81,13 @@
|
||||
:style="{ height: `calc(100vh - ${loadHeight()})`, 'background-color': '#000' }"
|
||||
:description="loadErrMsg()"
|
||||
>
|
||||
<el-button v-if="currentDB.from === 'remote'" v-permission type="primary" @click="installCli">
|
||||
<el-button
|
||||
v-if="currentDB.from === 'remote'"
|
||||
v-permission
|
||||
type="primary"
|
||||
:loading="cliSubmitting"
|
||||
@click="installCli"
|
||||
>
|
||||
{{ $t('commons.button.enable') }}
|
||||
</el-button>
|
||||
</el-empty>
|
||||
@@ -134,6 +140,7 @@
|
||||
</DialogPro>
|
||||
|
||||
<QuickCmd ref="dialogQuickCmdRef" @reload="loadQuickCmd" />
|
||||
<TaskLog ref="cliTaskLogRef" @close="checkCliValid" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -148,12 +155,14 @@ import { checkAppInstalled } from '@/api/modules/app';
|
||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
import { listDatabases, checkRedisCli, installRedisCli } from '@/api/modules/database';
|
||||
import { Database } from '@/api/interface/database';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import i18n from '@/lang';
|
||||
import { getCommandList } from '@/api/modules/command';
|
||||
import { routerToName, routerToNameWithQuery } from '@/utils/router';
|
||||
import { useOperateNodeContext } from '@/composables/useOperateNodeContext';
|
||||
|
||||
import TaskLog from '@/components/log/task/index.vue';
|
||||
import { newUUID } from '@/utils/id';
|
||||
|
||||
const { currentNode, currentRedisDB, openMenuTabs } = useGlobalStore();
|
||||
useOperateNodeContext(currentNode);
|
||||
|
||||
@@ -170,7 +179,11 @@ const appStatusRef = ref();
|
||||
|
||||
const open = ref(false);
|
||||
|
||||
const redisCliExist = ref();
|
||||
const redisCliExist = ref(false);
|
||||
const cliSubmitting = ref(false);
|
||||
const cliTaskLogRef = ref<InstanceType<typeof TaskLog>>();
|
||||
const operateNode = currentNode.value;
|
||||
let disposed = false;
|
||||
|
||||
const appKey = ref('redis');
|
||||
const appName = ref();
|
||||
@@ -250,6 +263,7 @@ const changeDatabase = async () => {
|
||||
const loadDBOptions = async () => {
|
||||
try {
|
||||
const res = await listDatabases('redis,redis-cluster');
|
||||
if (disposed || operateNode !== currentNode.value) return;
|
||||
let datas = res.data || [];
|
||||
dbOptionsLocal.value = [];
|
||||
dbOptionsRemote.value = [];
|
||||
@@ -286,7 +300,7 @@ const loadDBOptions = async () => {
|
||||
reOpenTerminal();
|
||||
}
|
||||
} finally {
|
||||
isLoaded.value = true;
|
||||
if (!disposed && operateNode === currentNode.value) isLoaded.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -301,6 +315,9 @@ const reOpenTerminal = async () => {
|
||||
};
|
||||
|
||||
const initTerminal = async () => {
|
||||
if (disposed || !currentDB.value) return;
|
||||
const database = currentDBName.value;
|
||||
const isCurrent = () => !disposed && operateNode === currentNode.value && database === currentDBName.value;
|
||||
loading.value = true;
|
||||
if (currentDB.value.from === 'remote') {
|
||||
if (!redisCliExist.value) {
|
||||
@@ -311,9 +328,10 @@ const initTerminal = async () => {
|
||||
loading.value = false;
|
||||
redisIsExist.value = true;
|
||||
nextTick(() => {
|
||||
if (!isCurrent()) return;
|
||||
terminalShow.value = true;
|
||||
redisStatus.value = 'Running';
|
||||
terminalRef.value.acceptParams({
|
||||
terminalRef.value?.acceptParams({
|
||||
endpoint: '/api/v2/hosts/terminal/container',
|
||||
args: `source=redis&name=${currentDBName.value}&from=${currentDB.value.from}`,
|
||||
error: '',
|
||||
@@ -325,13 +343,15 @@ const initTerminal = async () => {
|
||||
}
|
||||
await checkAppInstalled(currentDB.value.type, currentDBName.value)
|
||||
.then((res) => {
|
||||
if (!isCurrent()) return;
|
||||
redisIsExist.value = res.data.isExist;
|
||||
redisStatus.value = res.data.status;
|
||||
loading.value = false;
|
||||
nextTick(() => {
|
||||
if (!isCurrent()) return;
|
||||
if (res.data.status === 'Running') {
|
||||
terminalShow.value = true;
|
||||
terminalRef.value.acceptParams({
|
||||
terminalRef.value?.acceptParams({
|
||||
endpoint: '/api/v2/hosts/terminal/container',
|
||||
args: `source=${currentDB.value.type}&name=${currentDBName.value}&from=${currentDB.value.from}`,
|
||||
error: '',
|
||||
@@ -342,6 +362,7 @@ const initTerminal = async () => {
|
||||
isRefresh.value = !isRefresh.value;
|
||||
})
|
||||
.catch(() => {
|
||||
if (!isCurrent()) return;
|
||||
closeTerminal(false);
|
||||
loading.value = false;
|
||||
});
|
||||
@@ -353,27 +374,30 @@ const closeTerminal = async (isKeepShow: boolean) => {
|
||||
};
|
||||
|
||||
const checkCliValid = async () => {
|
||||
await checkRedisCli()
|
||||
.then((res) => {
|
||||
redisCliExist.value = res.data;
|
||||
loadDBOptions();
|
||||
})
|
||||
.catch(() => {
|
||||
loadDBOptions();
|
||||
});
|
||||
if (disposed || operateNode !== currentNode.value) return;
|
||||
try {
|
||||
const res = await checkRedisCli();
|
||||
if (disposed || operateNode !== currentNode.value) return;
|
||||
redisCliExist.value = res.data;
|
||||
} finally {
|
||||
if (!disposed && operateNode === currentNode.value) await loadDBOptions();
|
||||
}
|
||||
};
|
||||
|
||||
const installCli = async () => {
|
||||
loading.value = true;
|
||||
await installRedisCli()
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
redisCliExist.value = true;
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
reOpenTerminal();
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
if (cliSubmitting.value) return;
|
||||
cliSubmitting.value = true;
|
||||
try {
|
||||
const res = await installRedisCli(newUUID(), operateNode);
|
||||
if (disposed || operateNode !== currentNode.value) return;
|
||||
if (res.data) {
|
||||
cliTaskLogRef.value?.openWithTaskID(res.data, true, operateNode);
|
||||
} else {
|
||||
await checkCliValid();
|
||||
}
|
||||
} finally {
|
||||
cliSubmitting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const loadQuickCmd = async () => {
|
||||
@@ -403,6 +427,7 @@ const onAfter = () => {
|
||||
initTerminal();
|
||||
};
|
||||
onBeforeUnmount(() => {
|
||||
disposed = true;
|
||||
closeTerminal(false);
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user