mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-10 08:00:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1313462805 | ||
|
|
486cd5d0ad | ||
|
|
00ce5a7b36 | ||
|
|
c17f055e06 | ||
|
|
f22df4b29c | ||
|
|
b9bab90a14 |
@@ -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
|
||||
|
||||
@@ -50,13 +50,12 @@ type FirewallBackendOption struct {
|
||||
}
|
||||
|
||||
type FirewallBackendFamilyStatus struct {
|
||||
Partial bool `json:"partial"`
|
||||
Available bool `json:"available"`
|
||||
Initialized bool `json:"initialized"`
|
||||
Bound bool `json:"bound"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
ForwardPolicy string `json:"forwardPolicy,omitempty"`
|
||||
RAInterfaces []string `json:"raInterfaces,omitempty"`
|
||||
Partial bool `json:"partial"`
|
||||
Available bool `json:"available"`
|
||||
Initialized bool `json:"initialized"`
|
||||
Bound bool `json:"bound"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
RAInterfaces []string `json:"raInterfaces,omitempty"`
|
||||
}
|
||||
|
||||
type FirewallBackendGroup struct {
|
||||
|
||||
@@ -383,11 +383,6 @@ func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
|
||||
}
|
||||
|
||||
func (u *ContainerService) Prune(req dto.ContainerPrune) error {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
name := ""
|
||||
switch req.PruneType {
|
||||
case "container":
|
||||
@@ -408,6 +403,14 @@ func (u *ContainerService) Prune(req dto.ContainerPrune) error {
|
||||
}
|
||||
|
||||
taskItem.AddSubTask(i18n.GetMsgByKey("TaskClean"), func(t *task.Task) error {
|
||||
if err := t.TaskCtx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
pruneFilters := filters.NewArgs()
|
||||
if req.WithTagAll {
|
||||
pruneFilters.Add("dangling", "false")
|
||||
@@ -431,10 +434,7 @@ func (u *ContainerService) Prune(req dto.ContainerPrune) error {
|
||||
}
|
||||
SpaceReclaimed = int(rep.SpaceReclaimed)
|
||||
case "network":
|
||||
_, err := client.NetworksPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cleanUnusedNetworks(t, client)
|
||||
case "volume":
|
||||
versions, err := client.ServerVersion(context.Background())
|
||||
if err != nil {
|
||||
|
||||
@@ -2,16 +2,24 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/errdefs"
|
||||
)
|
||||
|
||||
var networkCleanupSlot = make(chan struct{}, 1)
|
||||
|
||||
func (u *ContainerService) PageNetwork(req dto.SearchWithPage) (int64, interface{}, error) {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
@@ -172,3 +180,114 @@ func (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cleanUnusedNetworks(t *task.Task, cli *client.Client) error {
|
||||
ctx := t.TaskCtx
|
||||
select {
|
||||
case networkCleanupSlot <- struct{}{}:
|
||||
defer func() { <-networkCleanupSlot }()
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
networks, err := cli.NetworkList(ctx, network.ListOptions{})
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
return err
|
||||
}
|
||||
deleted, skipped, failed := 0, 0, 0
|
||||
defer func() {
|
||||
t.Log(i18n.GetMsgWithMap("NetworkCleanupSummary", map[string]interface{}{
|
||||
"deleted": deleted, "skipped": skipped, "failed": failed,
|
||||
}))
|
||||
}()
|
||||
var used map[string]bool
|
||||
sort.Slice(networks, func(i, j int) bool { return networks[i].Name < networks[j].Name })
|
||||
for _, n := range networks {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
switch n.Name {
|
||||
case "none", "host", "bridge", "1panel-network":
|
||||
skipped++
|
||||
continue
|
||||
}
|
||||
if n.Scope != "local" || n.Ingress || n.ConfigOnly {
|
||||
skipped++
|
||||
continue
|
||||
}
|
||||
values := map[string]interface{}{"name": n.Name, "id": n.ID}
|
||||
if used == nil {
|
||||
containers, err := cli.ContainerList(ctx, container.ListOptions{All: true})
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
return err
|
||||
}
|
||||
used = make(map[string]bool)
|
||||
for _, c := range containers {
|
||||
if c.NetworkSettings == nil {
|
||||
continue
|
||||
}
|
||||
for name, endpoint := range c.NetworkSettings.Networks {
|
||||
used[name] = true
|
||||
if endpoint != nil {
|
||||
used[endpoint.NetworkID] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if used[n.Name] || used[n.ID] {
|
||||
skipped++
|
||||
t.Log(i18n.GetMsgWithMap("NetworkCleanupConnected", values))
|
||||
continue
|
||||
}
|
||||
inspected, err := cli.NetworkInspect(ctx, n.ID, network.InspectOptions{})
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
if errdefs.IsNotFound(err) {
|
||||
skipped++
|
||||
} else {
|
||||
failed++
|
||||
t.Logf("Failed to inspect network [%s] (%s): %v", n.Name, n.ID, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if len(inspected.Containers) > 0 {
|
||||
skipped++
|
||||
t.Log(i18n.GetMsgWithMap("NetworkCleanupConnected", values))
|
||||
continue
|
||||
}
|
||||
if err := cli.NetworkRemove(ctx, n.ID); err != nil {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
switch {
|
||||
case errdefs.IsNotFound(err):
|
||||
skipped++
|
||||
case errdefs.IsConflict(err):
|
||||
skipped++
|
||||
t.Log(i18n.GetMsgWithMap("NetworkCleanupConnected", values))
|
||||
default:
|
||||
failed++
|
||||
t.Logf("Failed to remove network [%s] (%s): %v", n.Name, n.ID, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
deleted++
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
if failed > 0 {
|
||||
return errors.New(i18n.GetMsgByKey("NetworkCleanupPartialFailure"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -108,9 +108,7 @@ func (s *FirewallService) LoadBaseInfo(chainGroup string) (dto.FirewallSubsystem
|
||||
status.Reason = constant.FirewallBackendNotInstalled
|
||||
return status, nil
|
||||
}
|
||||
status.IsExist = true
|
||||
status.Message = err.Error()
|
||||
return status, nil
|
||||
return status, err
|
||||
}
|
||||
status.IsExist = true
|
||||
runtimeStatus, err := lifecycle.LoadStatus(client)
|
||||
|
||||
@@ -1185,15 +1185,17 @@ func loadForwardingFirewallOverview(manager forwarding.Adapter, families []strin
|
||||
}
|
||||
ipv6, ipv6Err := loadForwardingFamilyInfo(manager, constant.FirewallFamilyIPv6)
|
||||
if ipv6.Available {
|
||||
interfaces, err := forwarding.IPv6RAInterfaces(os.ReadFile)
|
||||
interfaces, err := forwarding.IPv6RAInterfaces(os.ReadFile, forwarding.LoadDockerIPv4BridgePorts)
|
||||
var pathError *os.PathError
|
||||
switch {
|
||||
case errors.As(err, &pathError) && errors.Is(err, os.ErrNotExist) && pathError.Path == "/proc/net/if_inet6":
|
||||
ipv6.Available, ipv6.Initialized, ipv6.Bound = false, false, false
|
||||
case err != nil:
|
||||
global.LOG.Warnf("IPv6 forwarding RA check failed: %v", err)
|
||||
ipv6.Reason = "ipv6_ra_check_failed"
|
||||
ipv6.Bound = false
|
||||
case len(interfaces) > 0:
|
||||
global.LOG.Warnf("IPv6 forwarding RA risk: interfaces %s may depend on RA/SLAAC with accept_ra=1", strings.Join(interfaces, ", "))
|
||||
ipv6.Reason, ipv6.RAInterfaces = "ipv6_ra_required", interfaces
|
||||
ipv6.Bound = false
|
||||
case !ipv6.Bound:
|
||||
|
||||
@@ -9,13 +9,10 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/firewall"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/firewall/filter"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/firewall/forwarding"
|
||||
@@ -82,23 +79,6 @@ func (s *ForwardingService) LoadBaseInfo(ctx context.Context) (dto.FirewallSubsy
|
||||
baseInfo.PingStatus = firewall.LoadPingStatus()
|
||||
baseInfo.IsInit, baseInfo.IsBind = status.IsInit, status.IsBind
|
||||
baseInfo.IPv4, baseInfo.IPv6 = status.IPv4, status.IPv6
|
||||
for _, family := range []struct {
|
||||
command string
|
||||
status *dto.FirewallBackendFamilyStatus
|
||||
}{
|
||||
{"iptables", &baseInfo.IPv4},
|
||||
{"ip6tables", &baseInfo.IPv6},
|
||||
} {
|
||||
if family.command == "ip6tables" && !baseInfo.IPv6Enabled {
|
||||
continue
|
||||
}
|
||||
policy, err := loadForwardPolicy(ctx, family.command)
|
||||
if err != nil {
|
||||
global.LOG.Warnf("inspect %s FORWARD policy: %v", family.command, err)
|
||||
continue
|
||||
}
|
||||
family.status.ForwardPolicy = policy
|
||||
}
|
||||
return baseInfo, nil
|
||||
}
|
||||
|
||||
@@ -308,26 +288,3 @@ func (s *ForwardingService) Restore(ctx context.Context) error {
|
||||
func NewIForwardingService() IForwardingService {
|
||||
return newForwardingService()
|
||||
}
|
||||
|
||||
func loadForwardPolicy(ctx context.Context, command string) (string, error) {
|
||||
if !cmd.Which(command) {
|
||||
command += "-nft"
|
||||
if !cmd.Which(command) {
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
output, err := cmd.NewCommandMgr(cmd.WithContext(ctx), cmd.WithTimeout(5*time.Second)).RunWithOptionalSudoAndStdout(command, "-t", "filter", "-w", "2", "-S", "FORWARD")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, line := range strings.Split(output, "\n") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) == 3 && fields[0] == "-P" && fields[1] == "FORWARD" {
|
||||
if fields[2] != "ACCEPT" && fields[2] != "DROP" {
|
||||
return "", fmt.Errorf("unexpected FORWARD policy: %s", fields[2])
|
||||
}
|
||||
return fields[2], nil
|
||||
}
|
||||
}
|
||||
return "", errors.New("FORWARD default policy was not found")
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ const (
|
||||
TaskScopeCronjob = "Cronjob"
|
||||
TaskScopeClam = "Clam"
|
||||
TaskScopeSystem = "System"
|
||||
TaskScopeVm = "VirtualMachine"
|
||||
TaskScopeFirewall = "Firewall"
|
||||
TaskScopeAppStore = "AppStore"
|
||||
TaskScopeSnapshot = "Snapshot"
|
||||
@@ -258,7 +259,15 @@ func (s *SubTask) Execute() error {
|
||||
case <-ctx.Done():
|
||||
s.RootTask.Log(i18n.GetWithName("TaskTimeout", subTaskName))
|
||||
err = errors.New("timeout!")
|
||||
if s.RootTask.Task.Type == TaskScopeVm {
|
||||
<-done
|
||||
}
|
||||
case err = <-done:
|
||||
if s.RootTask.Task.Type == TaskScopeVm && ctx.Err() != nil {
|
||||
s.RootTask.Log(i18n.GetWithName("TaskTimeout", subTaskName))
|
||||
err = errors.New("timeout!")
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
s.RootTask.Log(i18n.GetWithNameAndErr("SubTaskFailed", subTaskName, err))
|
||||
if err.Error() == i18n.GetMsgByKey("ErrShutDown") {
|
||||
|
||||
@@ -300,6 +300,9 @@ BuildCache: 'Build Cache'
|
||||
Volume: 'Volume'
|
||||
Network: "Network"
|
||||
PruneStart: 'Cleaning in progress'
|
||||
NetworkCleanupConnected: "Skipped network [{{ .name }}] ({{ .id }}): containers connected or network in use; not deleted"
|
||||
NetworkCleanupSummary: "Network cleanup finished: deleted {{ .deleted }}, skipped {{ .skipped }}, failed {{ .failed }}"
|
||||
NetworkCleanupPartialFailure: "Some networks could not be cleaned; see the task log"
|
||||
ContainerBackupPrepare: 'Prepare container backup workspace'
|
||||
ContainerBackupStop: 'Stop container before backup'
|
||||
ContainerBackupInspect: 'Backup container inspect and network'
|
||||
@@ -718,6 +721,8 @@ ErrDockerIPv4ForwardingDisabled: "IPv4 forwarding is disabled. Set net.ipv4.ip_f
|
||||
ErrFirewallRuleSavedApplyFailed: "The new rule configuration was saved, but applying it to the firewall failed. Retry by synchronizing: {{ .detail }}"
|
||||
ErrFirewallRuleConflict: "A UFW rule with the same match conditions and a different action already exists. Edit the existing rule instead."
|
||||
ErrForwardInterfaceNotFound: "Network interface {{ .name }} specified by the forwarding rule does not exist. Check the interface name and retry."
|
||||
ErrIPv6RARisk: "IPv6 forwarding is blocked to prevent possible network loss. See the documentation and system logs for details."
|
||||
ErrIPv6RACheckFailed: "IPv6 forwarding is blocked because the network configuration could not be checked. See the system logs for details."
|
||||
|
||||
ErrSSHPublicKey: "Enter a valid SSH public key"
|
||||
ErrSSHPrivateKey: "Invalid SSH private key or incorrect passphrase; encrypted keys require the original passphrase"
|
||||
|
||||
@@ -300,6 +300,9 @@ BuildCache: 'Caché de compilación'
|
||||
Volume: 'Volumen de almacenamiento'
|
||||
Network: 'Red'
|
||||
PruneStart: 'Limpiando en progreso espere...'
|
||||
NetworkCleanupConnected: "Se omitió la red [{{ .name }}] ({{ .id }}): hay contenedores conectados o la red está en uso; no se eliminó"
|
||||
NetworkCleanupSummary: "Limpieza de redes finalizada: eliminadas {{ .deleted }}, omitidas {{ .skipped }}, fallidas {{ .failed }}"
|
||||
NetworkCleanupPartialFailure: "No se pudieron limpiar algunas redes; consulte el registro de la tarea"
|
||||
ContainerBackupPrepare: 'Preparar espacio de respaldo del contenedor'
|
||||
ContainerBackupStop: 'Detener contenedor antes del respaldo'
|
||||
ContainerBackupInspect: 'Respaldar inspect y red del contenedor'
|
||||
@@ -718,6 +721,8 @@ ErrDockerIPv4ForwardingDisabled: "El reenvío IPv4 está desactivado. Configure
|
||||
ErrFirewallRuleSavedApplyFailed: "Se guardó la configuración de la nueva regla, pero no se pudo aplicar al cortafuegos. Vuelva a intentarlo mediante la sincronización: {{ .detail }}"
|
||||
ErrFirewallRuleConflict: "Ya existe una regla UFW con las mismas condiciones y una acción diferente. Edite la regla existente."
|
||||
ErrForwardInterfaceNotFound: "La interfaz de red {{ .name }} indicada en la regla de reenvío no existe. Compruebe el nombre e inténtelo de nuevo."
|
||||
ErrIPv6RARisk: "El reenvío IPv6 está bloqueado para evitar una posible pérdida de conectividad. Consulte la documentación y los registros del sistema."
|
||||
ErrIPv6RACheckFailed: "El reenvío IPv6 está bloqueado porque no se pudo comprobar la configuración de red. Consulte los registros del sistema."
|
||||
|
||||
ErrSSHPublicKey: "Introduce una clave pública SSH válida"
|
||||
ErrSSHPrivateKey: "Clave privada SSH no válida o contraseña incorrecta; las claves cifradas requieren la contraseña original"
|
||||
|
||||
@@ -300,6 +300,9 @@ BuildCache: 'کش ساخت'
|
||||
Volume: 'حجم'
|
||||
Network: "شبکه"
|
||||
PruneStart: 'پاکسازی در حال انجام است'
|
||||
NetworkCleanupConnected: "از شبکه [{{ .name }}] ({{ .id }}) صرفنظر شد: کانتینر متصل دارد یا در حال استفاده است؛ حذف نشد"
|
||||
NetworkCleanupSummary: "پاکسازی شبکهها پایان یافت: حذفشده {{ .deleted }}، ردشده {{ .skipped }}، ناموفق {{ .failed }}"
|
||||
NetworkCleanupPartialFailure: "پاکسازی برخی شبکهها ناموفق بود؛ گزارش وظیفه را بررسی کنید"
|
||||
ContainerBackupPrepare: 'آمادهسازی فضای کاری پشتیبان کانتینر'
|
||||
ContainerBackupStop: 'توقف کانتینر قبل از پشتیبانگیری'
|
||||
ContainerBackupInspect: 'بررسی و شبکه کانتینر پشتیبان'
|
||||
@@ -719,6 +722,8 @@ ErrDockerIPv4ForwardingDisabled: "ارسال IPv4 غیرفعال است. پیش
|
||||
ErrFirewallRuleSavedApplyFailed: "پیکربندی قانون جدید ذخیره شد، اما اعمال آن در دیوار آتش ناموفق بود. با همگامسازی دوباره تلاش کنید: {{ .detail }}"
|
||||
ErrFirewallRuleConflict: "یک قانون UFW با شرایط تطبیق یکسان و عملکرد متفاوت از قبل وجود دارد. قانون موجود را ویرایش کنید."
|
||||
ErrForwardInterfaceNotFound: "رابط شبکه {{ .name }} مشخصشده در قانون انتقال وجود ندارد. نام رابط را بررسی کرده و دوباره تلاش کنید."
|
||||
ErrIPv6RARisk: "فعالسازی هدایت IPv6 به دلیل خطر قطع شبکه مسدود شد. مستندات و گزارشهای سیستم را بررسی کنید."
|
||||
ErrIPv6RACheckFailed: "فعالسازی هدایت IPv6 به دلیل عدم امکان بررسی پیکربندی شبکه مسدود شد. جزئیات در گزارشهای سیستم موجود است."
|
||||
|
||||
ErrSSHPublicKey: "یک کلید عمومی SSH معتبر وارد کنید"
|
||||
ErrSSHPrivateKey: "کلید خصوصی SSH نامعتبر یا عبارت عبور نادرست است؛ کلیدهای رمزگذاریشده به عبارت عبور اصلی نیاز دارند"
|
||||
|
||||
@@ -300,6 +300,9 @@ BuildCache: 'ビルドキャッシュ'
|
||||
Volume: 'ストレージボリューム'
|
||||
Network: 'ネットワーク'
|
||||
PruneStart: 'クリーンアップ中、しばらくお待ちください...'
|
||||
NetworkCleanupConnected: "ネットワーク [{{ .name }}] ({{ .id }}) をスキップしました:コンテナが接続されているか使用中のため、削除しませんでした"
|
||||
NetworkCleanupSummary: "ネットワークのクリーンアップが完了しました:削除 {{ .deleted }} 件、スキップ {{ .skipped }} 件、失敗 {{ .failed }} 件"
|
||||
NetworkCleanupPartialFailure: "一部のネットワークをクリーンアップできませんでした。タスクログを確認してください"
|
||||
ContainerBackupPrepare: 'コンテナバックアップ作業領域を準備'
|
||||
ContainerBackupStop: 'バックアップ前にコンテナを停止'
|
||||
ContainerBackupInspect: 'コンテナ inspect とネットワーク情報をバックアップ'
|
||||
@@ -718,6 +721,8 @@ ErrDockerIPv4ForwardingDisabled: "IPv4 転送が無効です。Docker のファ
|
||||
ErrFirewallRuleSavedApplyFailed: "新しいルール設定は保存されましたが、ファイアウォールへの適用に失敗しました。同期で再試行してください:{{ .detail }}"
|
||||
ErrFirewallRuleConflict: "UFW に同じ一致条件でアクションが異なるルールが既に存在します。既存のルールを編集してください。"
|
||||
ErrForwardInterfaceNotFound: "転送ルールで指定されたネットワークインターフェース {{ .name }} は存在しません。名前を確認して再試行してください。"
|
||||
ErrIPv6RARisk: "ネットワーク切断のおそれがあるため、IPv6 転送の有効化をブロックしました。ドキュメントとシステムログをご確認ください。"
|
||||
ErrIPv6RACheckFailed: "ネットワーク設定を確認できないため、IPv6 転送の有効化をブロックしました。詳細はシステムログをご確認ください。"
|
||||
|
||||
ErrSSHPublicKey: "有効な SSH 公開鍵を入力してください"
|
||||
ErrSSHPrivateKey: "SSH 秘密鍵の形式が無効か、パスフレーズが間違っています。暗号化された鍵には元のパスフレーズが必要です"
|
||||
|
||||
@@ -300,6 +300,9 @@ BuildCache: '빌드 캐시'
|
||||
Volume: '스토리지 볼륨'
|
||||
Network: '네트워크'
|
||||
PruneStart: '정리 중입니다. 잠시만 기다려주세요...'
|
||||
NetworkCleanupConnected: "네트워크 [{{ .name }}] ({{ .id }}) 건너뜀: 연결된 컨테이너가 있거나 사용 중이므로 삭제하지 않음"
|
||||
NetworkCleanupSummary: "네트워크 정리 완료: 삭제 {{ .deleted }}개, 건너뜀 {{ .skipped }}개, 실패 {{ .failed }}개"
|
||||
NetworkCleanupPartialFailure: "일부 네트워크를 정리하지 못했습니다. 작업 로그를 확인하세요"
|
||||
ContainerBackupPrepare: '컨테이너 백업 작업 공간 준비'
|
||||
ContainerBackupStop: '백업 전에 컨테이너 중지'
|
||||
ContainerBackupInspect: '컨테이너 inspect 및 네트워크 정보 백업'
|
||||
@@ -718,6 +721,8 @@ ErrDockerIPv4ForwardingDisabled: "IPv4 전달이 비활성화되어 있습니다
|
||||
ErrFirewallRuleSavedApplyFailed: "새 규칙 설정이 저장되었지만 방화벽에 적용하지 못했습니다. 동기화하여 다시 시도하세요: {{ .detail }}"
|
||||
ErrFirewallRuleConflict: "UFW에 일치 조건이 같지만 동작이 다른 규칙이 이미 있습니다. 기존 규칙을 편집하세요."
|
||||
ErrForwardInterfaceNotFound: "전달 규칙에 지정된 네트워크 인터페이스 {{ .name }}이 존재하지 않습니다. 이름을 확인한 후 다시 시도하세요."
|
||||
ErrIPv6RARisk: "네트워크 연결이 끊길 위험이 있어 IPv6 전달 활성화를 차단했습니다. 문서와 시스템 로그를 확인하세요."
|
||||
ErrIPv6RACheckFailed: "네트워크 구성을 확인할 수 없어 IPv6 전달 활성화를 차단했습니다. 자세한 내용은 시스템 로그를 확인하세요."
|
||||
|
||||
ErrSSHPublicKey: "유효한 SSH 공개 키를 입력하세요"
|
||||
ErrSSHPrivateKey: "SSH 개인 키 형식이 잘못되었거나 암호가 틀립니다. 암호화된 키에는 원래 암호가 필요합니다"
|
||||
|
||||
@@ -290,6 +290,9 @@ BuildCache: 'ແຄດການສ້າງ'
|
||||
Volume: 'ວໍລຸມ (Volume)'
|
||||
Network: "ເຄືອຂ່າຍ"
|
||||
PruneStart: 'ກຳລັງດຳເນີນການທຳຄວາມສະອາດ'
|
||||
NetworkCleanupConnected: "ຂ້າມເຄືອຂ່າຍ [{{ .name }}] ({{ .id }}): ມີຄອນເທນເນີເຊື່ອມຕໍ່ຢູ່ ຫຼື ເຄືອຂ່າຍກຳລັງຖືກໃຊ້ງານ; ບໍ່ໄດ້ລຶບ"
|
||||
NetworkCleanupSummary: "ການລ້າງເຄືອຂ່າຍສຳເລັດ: ລຶບແລ້ວ {{ .deleted }}, ຂ້າມ {{ .skipped }}, ລົ້ມເຫຼວ {{ .failed }}"
|
||||
NetworkCleanupPartialFailure: "ບໍ່ສາມາດລ້າງບາງເຄືອຂ່າຍໄດ້; ກະລຸນາເບິ່ງບັນທຶກວຽກ"
|
||||
ContainerBackupPrepare: 'ກຽມພື້ນທີ່ເຮັດວຽກສຳລັບສຳຮອງຂໍ້ມູນຄອນເທນເນີ'
|
||||
ContainerBackupStop: 'ຢຸດຄອນເທນເນີກ່ອນການສຳຮອງຂໍ້ມູນ'
|
||||
ContainerBackupInspect: 'ກວດສອບຄອນເທນເນີ ແລະ ເຄືອຂ່າຍສຳລັບການສຳຮອງຂໍ້ມູນ'
|
||||
@@ -709,6 +712,8 @@ ErrDockerIPv4ForwardingDisabled: "ການສົ່ງຕໍ່ IPv4 ຖືກ
|
||||
ErrFirewallRuleSavedApplyFailed: "ບັນທຶກການຕັ້ງຄ່າກົດໃໝ່ແລ້ວ ແຕ່ນຳໃຊ້ກັບໄຟວໍບໍ່ສຳເລັດ. ລອງອີກຄັ້ງດ້ວຍການຊິງຂໍ້ມູນ: {{ .detail }}"
|
||||
ErrFirewallRuleConflict: "ມີກົດ UFW ທີ່ມີເງື່ອນໄຂກົງກັນແຕ່ການດຳເນີນການຕ່າງກັນແລ້ວ. ກະລຸນາແກ້ໄຂກົດທີ່ມີຢູ່."
|
||||
ErrForwardInterfaceNotFound: "ບໍ່ພົບອິນເຕີເຟດເຄືອຂ່າຍ {{ .name }} ທີ່ລະບຸໃນກົດສົ່ງຕໍ່. ກະລຸນາກວດສອບຊື່ ແລ້ວລອງໃໝ່."
|
||||
ErrIPv6RARisk: "ການສົ່ງຕໍ່ IPv6 ຖືກບລັອກເພື່ອປ້ອງກັນຄວາມສ່ຽງທີ່ເຄືອຂ່າຍຈະຂາດການເຊື່ອມຕໍ່. ເບິ່ງເອກະສານ ແລະ ບັນທຶກລະບົບ."
|
||||
ErrIPv6RACheckFailed: "ການສົ່ງຕໍ່ IPv6 ຖືກບລັອກເນື່ອງຈາກບໍ່ສາມາດກວດສອບການຕັ້ງຄ່າເຄືອຂ່າຍໄດ້. ເບິ່ງລາຍລະອຽດໃນບັນທຶກລະບົບ."
|
||||
|
||||
ErrSSHPublicKey: "ກະລຸນາປ້ອນຄີສາທາລະນະ SSH ທີ່ຖືກຕ້ອງ"
|
||||
ErrSSHPrivateKey: "ຄີສ່ວນຕົວ SSH ບໍ່ຖືກຕ້ອງ ຫຼື ລະຫັດຜ່ານຜິດ; ຄີທີ່ເຂົ້າລະຫັດຕ້ອງໃຊ້ລະຫັດຜ່ານເດີມ"
|
||||
|
||||
@@ -300,6 +300,9 @@ BuildCache: 'Cache binaan'
|
||||
Volume: 'Jilid storan'
|
||||
Network: 'Rangkaian'
|
||||
PruneStart: 'Pembersihan sedang dijalankan, sila tunggu...'
|
||||
NetworkCleanupConnected: "Rangkaian [{{ .name }}] ({{ .id }}) dilangkau: terdapat kontena yang disambungkan atau rangkaian sedang digunakan; tidak dipadamkan"
|
||||
NetworkCleanupSummary: "Pembersihan rangkaian selesai: dipadamkan {{ .deleted }}, dilangkau {{ .skipped }}, gagal {{ .failed }}"
|
||||
NetworkCleanupPartialFailure: "Sesetengah rangkaian tidak dapat dibersihkan; semak log tugas"
|
||||
ContainerBackupPrepare: 'Sediakan ruang kerja sandaran kontena'
|
||||
ContainerBackupStop: 'Hentikan kontena sebelum sandaran'
|
||||
ContainerBackupInspect: 'Sandarkan inspect dan rangkaian kontena'
|
||||
@@ -718,6 +721,8 @@ ErrDockerIPv4ForwardingDisabled: "Pemajuan IPv4 dilumpuhkan. Tetapkan net.ipv4.i
|
||||
ErrFirewallRuleSavedApplyFailed: "Konfigurasi peraturan baharu telah disimpan, tetapi gagal digunakan pada tembok api. Cuba lagi melalui penyegerakan: {{ .detail }}"
|
||||
ErrFirewallRuleConflict: "Peraturan UFW dengan syarat padanan yang sama tetapi tindakan berbeza sudah wujud. Sila edit peraturan sedia ada."
|
||||
ErrForwardInterfaceNotFound: "Antara muka rangkaian {{ .name }} yang dinyatakan dalam peraturan pemajuan tidak wujud. Semak namanya dan cuba lagi."
|
||||
ErrIPv6RARisk: "Pemajuan IPv6 disekat bagi mengelakkan risiko kehilangan sambungan rangkaian. Rujuk dokumentasi dan log sistem."
|
||||
ErrIPv6RACheckFailed: "Pemajuan IPv6 disekat kerana konfigurasi rangkaian tidak dapat diperiksa. Lihat log sistem untuk butiran."
|
||||
|
||||
ErrSSHPublicKey: "Masukkan kunci awam SSH yang sah"
|
||||
ErrSSHPrivateKey: "Kunci peribadi SSH tidak sah atau frasa laluan salah; kunci yang disulitkan memerlukan frasa laluan asal"
|
||||
|
||||
@@ -300,6 +300,9 @@ BuildCache: 'Cache de construção'
|
||||
Volume: 'Volume de armazenamento'
|
||||
Network: 'Rede'
|
||||
PruneStart: 'Limpando em andamento aguarde...'
|
||||
NetworkCleanupConnected: "Rede [{{ .name }}] ({{ .id }}) ignorada: há contêineres conectados ou a rede está em uso; não foi excluída"
|
||||
NetworkCleanupSummary: "Limpeza de redes concluída: excluídas {{ .deleted }}, ignoradas {{ .skipped }}, falhas {{ .failed }}"
|
||||
NetworkCleanupPartialFailure: "Não foi possível limpar algumas redes; consulte o log da tarefa"
|
||||
ContainerBackupPrepare: 'Preparar área de backup do contêiner'
|
||||
ContainerBackupStop: 'Parar contêiner antes do backup'
|
||||
ContainerBackupInspect: 'Fazer backup do inspect e rede do contêiner'
|
||||
@@ -718,6 +721,8 @@ ErrDockerIPv4ForwardingDisabled: "O encaminhamento IPv4 está desativado. Defina
|
||||
ErrFirewallRuleSavedApplyFailed: "A configuração da nova regra foi salva, mas não pôde ser aplicada ao firewall. Tente novamente por meio da sincronização: {{ .detail }}"
|
||||
ErrFirewallRuleConflict: "Já existe uma regra UFW com as mesmas condições e uma ação diferente. Edite a regra existente."
|
||||
ErrForwardInterfaceNotFound: "A interface de rede {{ .name }} especificada na regra de encaminhamento não existe. Verifique o nome e tente novamente."
|
||||
ErrIPv6RARisk: "O encaminhamento IPv6 foi bloqueado para evitar uma possível perda de conexão. Consulte a documentação e os logs do sistema."
|
||||
ErrIPv6RACheckFailed: "O encaminhamento IPv6 foi bloqueado porque não foi possível verificar a configuração de rede. Consulte os logs do sistema."
|
||||
|
||||
ErrSSHPublicKey: "Informe uma chave pública SSH válida"
|
||||
ErrSSHPrivateKey: "Chave privada SSH inválida ou senha incorreta; chaves criptografadas exigem a senha original"
|
||||
|
||||
@@ -300,6 +300,9 @@ BuildCache: 'Кэш сборки'
|
||||
Volume: 'Том хранилища'
|
||||
Network: 'Сеть'
|
||||
PruneStart: 'Очистка выполняется, пожалуйста, подождите...'
|
||||
NetworkCleanupConnected: "Сеть [{{ .name }}] ({{ .id }}) пропущена: подключены контейнеры или сеть используется; не удалена"
|
||||
NetworkCleanupSummary: "Очистка сетей завершена: удалено {{ .deleted }}, пропущено {{ .skipped }}, ошибок {{ .failed }}"
|
||||
NetworkCleanupPartialFailure: "Не удалось очистить некоторые сети; проверьте журнал задачи"
|
||||
ContainerBackupPrepare: 'Подготовить рабочую область резервной копии контейнера'
|
||||
ContainerBackupStop: 'Остановить контейнер перед резервным копированием'
|
||||
ContainerBackupInspect: 'Сделать резервную копию inspect и сети контейнера'
|
||||
@@ -718,6 +721,8 @@ ErrDockerIPv4ForwardingDisabled: "Пересылка IPv4 отключена. П
|
||||
ErrFirewallRuleSavedApplyFailed: "Настройки нового правила сохранены, но применить их к межсетевому экрану не удалось. Повторите попытку с помощью синхронизации: {{ .detail }}"
|
||||
ErrFirewallRuleConflict: "В UFW уже есть правило с такими же условиями и другим действием. Отредактируйте существующее правило."
|
||||
ErrForwardInterfaceNotFound: "Сетевой интерфейс {{ .name }}, указанный в правиле перенаправления, не существует. Проверьте имя и повторите попытку."
|
||||
ErrIPv6RARisk: "Включение переадресации IPv6 заблокировано из-за риска потери связи. См. документацию и системные журналы."
|
||||
ErrIPv6RACheckFailed: "Включение переадресации IPv6 заблокировано: не удалось проверить настройки сети. Подробности в системных журналах."
|
||||
|
||||
ErrSSHPublicKey: "Введите действительный открытый ключ SSH"
|
||||
ErrSSHPrivateKey: "Недопустимый закрытый ключ SSH или неверная парольная фраза; для зашифрованных ключей требуется исходная парольная фраза"
|
||||
|
||||
@@ -300,6 +300,9 @@ BuildCache: 'Derleme önbelleği'
|
||||
Volume: 'Depolama hacmi'
|
||||
Network: 'Ağ'
|
||||
PruneStart: 'Temizlik devam ediyor, lütfen bekleyin...'
|
||||
NetworkCleanupConnected: "Ağ [{{ .name }}] ({{ .id }}) atlandı: bağlı konteynerler var veya ağ kullanımda; silinmedi"
|
||||
NetworkCleanupSummary: "Ağ temizliği tamamlandı: silinen {{ .deleted }}, atlanan {{ .skipped }}, başarısız {{ .failed }}"
|
||||
NetworkCleanupPartialFailure: "Bazı ağlar temizlenemedi; görev günlüğünü kontrol edin"
|
||||
ContainerBackupPrepare: 'Konteyner yedekleme çalışma alanını hazırla'
|
||||
ContainerBackupStop: 'Yedeklemeden önce konteyneri durdur'
|
||||
ContainerBackupInspect: 'Konteyner inspect ve ağ bilgisini yedekle'
|
||||
@@ -718,6 +721,8 @@ ErrDockerIPv4ForwardingDisabled: "IPv4 yönlendirmesi devre dışı. Docker güv
|
||||
ErrFirewallRuleSavedApplyFailed: "Yeni kural yapılandırması kaydedildi, ancak güvenlik duvarına uygulanamadı. Eşitleme yaparak yeniden deneyin: {{ .detail }}"
|
||||
ErrFirewallRuleConflict: "UFW içinde aynı eşleşme koşullarına sahip, farklı bir eylem uygulayan bir kural zaten var. Mevcut kuralı düzenleyin."
|
||||
ErrForwardInterfaceNotFound: "Yönlendirme kuralında belirtilen {{ .name }} ağ arayüzü mevcut değil. Arayüz adını kontrol edip yeniden deneyin."
|
||||
ErrIPv6RARisk: "Ağ bağlantısının kesilme riski nedeniyle IPv6 yönlendirmesi engellendi. Belgeleri ve sistem günlüklerini inceleyin."
|
||||
ErrIPv6RACheckFailed: "Ağ yapılandırması denetlenemediği için IPv6 yönlendirmesi engellendi. Ayrıntılar için sistem günlüklerini inceleyin."
|
||||
|
||||
ErrSSHPublicKey: "Geçerli bir SSH açık anahtarı girin"
|
||||
ErrSSHPrivateKey: "SSH özel anahtarı geçersiz veya parola yanlış; şifreli anahtarlar mevcut parolayı gerektirir"
|
||||
|
||||
@@ -300,6 +300,9 @@ BuildCache: '構建快取'
|
||||
Volume: '磁碟區'
|
||||
Network: '網路'
|
||||
PruneStart: '清理中,請稍候...'
|
||||
NetworkCleanupConnected: "跳過網路 [{{ .name }}] ({{ .id }}):仍有容器連接或正在使用,未刪除"
|
||||
NetworkCleanupSummary: "網路清理完成:已刪除 {{ .deleted }},已跳過 {{ .skipped }},失敗 {{ .failed }}"
|
||||
NetworkCleanupPartialFailure: "部分網路清理失敗,請查看任務日誌"
|
||||
ContainerBackupPrepare: '準備容器備份工作目錄'
|
||||
ContainerBackupStop: '備份前停止容器'
|
||||
ContainerBackupInspect: '備份容器 Inspect 與網路資訊'
|
||||
@@ -718,6 +721,8 @@ ErrDockerIPv4ForwardingDisabled: "IPv4 轉送尚未啟用,請先設定 net.ipv
|
||||
ErrFirewallRuleSavedApplyFailed: "新規則設定已儲存,但套用至防火牆失敗。可透過同步重試:{{ .detail }}"
|
||||
ErrFirewallRuleConflict: "UFW 中已存在比對條件相同但動作不同的規則,請編輯既有規則。"
|
||||
ErrForwardInterfaceNotFound: "轉發規則指定的網卡 {{ .name }} 不存在,請檢查網卡名稱後重試。"
|
||||
ErrIPv6RARisk: "IPv6 轉送可能導致網路中斷,已阻止啟用。請參閱文件,詳情見系統日誌。"
|
||||
ErrIPv6RACheckFailed: "無法檢查 IPv6 網路設定,已阻止啟用轉送。詳情見系統日誌。"
|
||||
|
||||
ErrSSHPublicKey: "請輸入有效的 SSH 公鑰"
|
||||
ErrSSHPrivateKey: "SSH 私鑰格式無效或密碼錯誤;加密私鑰需要填寫原密碼"
|
||||
|
||||
@@ -300,6 +300,9 @@ BuildCache: "构建缓存"
|
||||
Volume: "存储卷"
|
||||
Network: "网络"
|
||||
PruneStart: "清理中,请稍候..."
|
||||
NetworkCleanupConnected: "跳过网络 [{{ .name }}] ({{ .id }}):仍有容器连接或正在使用,未删除"
|
||||
NetworkCleanupSummary: "网络清理完成:已删除 {{ .deleted }},已跳过 {{ .skipped }},失败 {{ .failed }}"
|
||||
NetworkCleanupPartialFailure: "部分网络清理失败,请查看任务日志"
|
||||
ContainerBackupPrepare: "准备容器备份工作目录"
|
||||
ContainerBackupStop: "备份前停止容器"
|
||||
ContainerBackupInspect: "备份容器 Inspect 与网络信息"
|
||||
@@ -718,6 +721,8 @@ ErrDockerIPv4ForwardingDisabled: "IPv4 转发未开启,请先设置 net.ipv4.i
|
||||
ErrFirewallRuleSavedApplyFailed: "新规则配置已保存,但应用到防火墙失败。可通过同步重试:{{ .detail }}"
|
||||
ErrFirewallRuleConflict: "UFW 中已存在匹配条件相同但动作不同的规则,请编辑已有规则。"
|
||||
ErrForwardInterfaceNotFound: "转发规则指定的网卡 {{ .name }} 不存在,请检查网卡名称后重试。"
|
||||
ErrIPv6RARisk: "IPv6 转发存在断网风险,已阻止启用。请参阅文档,详情见系统日志。"
|
||||
ErrIPv6RACheckFailed: "无法检查 IPv6 网络配置,已阻止启用转发。详情见系统日志。"
|
||||
|
||||
ErrSSHPublicKey: "请输入有效的 SSH 公钥"
|
||||
ErrSSHPrivateKey: "SSH 私钥格式无效或密码错误;加密私钥需要填写原密码"
|
||||
|
||||
@@ -251,6 +251,10 @@ func (c *CommandHelper) buildPipeCommands(ctx context.Context, commands []PipeCo
|
||||
cmdItem.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
cmdItem.Cancel = func() error {
|
||||
_ = syscall.Kill(-cmdItem.Process.Pid, syscall.SIGKILL)
|
||||
return cmdItem.Process.Kill()
|
||||
}
|
||||
cmds = append(cmds, cmdItem)
|
||||
}
|
||||
return cmds
|
||||
@@ -335,6 +339,12 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
if newContext != nil {
|
||||
cmd.Cancel = func() error {
|
||||
_ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
|
||||
return cmd.Process.Kill()
|
||||
}
|
||||
}
|
||||
|
||||
customWriter := &CustomWriter{taskItem: c.taskItem}
|
||||
var stdout, stderr bytes.Buffer
|
||||
@@ -398,6 +408,9 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
|
||||
}()
|
||||
select {
|
||||
case err := <-done:
|
||||
if newContext != nil && newContext.Err() != nil {
|
||||
return "", c.pipeResultErr(newContext, err)
|
||||
}
|
||||
if err != nil {
|
||||
return handleErr(&stdout, &stderr, c.IgnoreExist1, err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package forwarding
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -8,19 +10,27 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
)
|
||||
|
||||
func ensureFamilyForwardingSysctls(system forwardingSystem, family string) error {
|
||||
withIPv6 := family == FamilyIPv6
|
||||
if withIPv6 {
|
||||
interfaces, err := IPv6RAInterfaces(system.ReadFile)
|
||||
interfaces, err := IPv6RAInterfaces(system.ReadFile, LoadDockerIPv4BridgePorts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("check IPv6 Router Advertisement: %w", err)
|
||||
global.LOG.Warnf("IPv6 forwarding blocked: failed to check Router Advertisement: %v", err)
|
||||
return buserr.New("ErrIPv6RACheckFailed")
|
||||
}
|
||||
if len(interfaces) > 0 {
|
||||
return fmt.Errorf("IPv6 forwarding blocked: interfaces %s may depend on RA/SLAAC with accept_ra=1; persist accept_ra=2 on interfaces that require RA before retrying", strings.Join(interfaces, ", "))
|
||||
global.LOG.Warnf("IPv6 forwarding blocked: interfaces %s may depend on RA/SLAAC with accept_ra=1; review the network configuration and persist accept_ra=2 on interfaces that require RA before retrying", strings.Join(interfaces, ", "))
|
||||
return buserr.New("ErrIPv6RARisk")
|
||||
}
|
||||
}
|
||||
paths := []string{"/proc/sys/net/ipv4/ip_forward"}
|
||||
@@ -43,7 +53,7 @@ func ensureFamilyForwardingSysctls(system forwardingSystem, family string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func IPv6RAInterfaces(readFile func(string) ([]byte, error)) ([]string, error) {
|
||||
func IPv6RAInterfaces(readFile func(string) ([]byte, error), loadDockerPorts func() (map[string]bool, error)) ([]string, error) {
|
||||
addresses, err := readFile("/proc/net/if_inet6")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -67,21 +77,16 @@ func IPv6RAInterfaces(readFile func(string) ([]byte, error)) ([]string, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, exists := globalAddresses[name]; !exists {
|
||||
globalAddresses[name] = false
|
||||
}
|
||||
if fields[3] == "00" {
|
||||
globalAddresses[name] = true
|
||||
if flags&permanentAddress == 0 {
|
||||
raInterfaces[name] = true
|
||||
}
|
||||
globalAddresses[name] = globalAddresses[name] || fields[3] == "00"
|
||||
if fields[3] == "00" && flags&permanentAddress == 0 {
|
||||
raInterfaces[name] = true
|
||||
}
|
||||
}
|
||||
routes, err := readFile("/proc/net/ipv6_route")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
const raRouteFlags = 0x00010000 | 0x00800000
|
||||
const raRouteFlags = 0x00010000 | 0x00080000 | 0x00800000
|
||||
for _, line := range strings.Split(string(routes), "\n") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) == 0 {
|
||||
@@ -98,12 +103,15 @@ func IPv6RAInterfaces(readFile func(string) ([]byte, error)) ([]string, error) {
|
||||
raInterfaces[fields[9]] = true
|
||||
}
|
||||
}
|
||||
uncertainInterfaces := make(map[string]bool)
|
||||
for name, hasGlobalAddress := range globalAddresses {
|
||||
if !hasGlobalAddress {
|
||||
if !hasGlobalAddress && !raInterfaces[name] {
|
||||
raInterfaces[name] = true
|
||||
uncertainInterfaces[name] = true
|
||||
}
|
||||
}
|
||||
var interfaces []string
|
||||
checkDockerPorts := false
|
||||
for name := range raInterfaces {
|
||||
if name == "lo" {
|
||||
continue
|
||||
@@ -118,15 +126,104 @@ func IPv6RAInterfaces(readFile func(string) ([]byte, error)) ([]string, error) {
|
||||
switch strings.TrimSpace(string(value)) {
|
||||
case "1":
|
||||
interfaces = append(interfaces, name)
|
||||
checkDockerPorts = checkDockerPorts || uncertainInterfaces[name]
|
||||
case "0", "2":
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid accept_ra value for %s", name)
|
||||
}
|
||||
}
|
||||
if checkDockerPorts {
|
||||
ports, err := loadDockerPorts()
|
||||
if err != nil {
|
||||
global.LOG.Warnf("IPv6 forwarding RA check: cannot exclude Docker bridge ports: %v", err)
|
||||
} else {
|
||||
kept := interfaces[:0]
|
||||
for _, name := range interfaces {
|
||||
if !uncertainInterfaces[name] || !ports[name] {
|
||||
kept = append(kept, name)
|
||||
}
|
||||
}
|
||||
interfaces = kept
|
||||
}
|
||||
}
|
||||
sort.Strings(interfaces)
|
||||
return interfaces, nil
|
||||
}
|
||||
|
||||
func LoadDockerIPv4BridgePorts() (map[string]bool, error) {
|
||||
cli, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cli.Close()
|
||||
if !strings.HasPrefix(cli.DaemonHost(), "unix://") {
|
||||
return nil, errors.New("cannot verify local bridge ports using a remote Docker daemon")
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
info, err := cli.Info(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, option := range info.SecurityOptions {
|
||||
if option == "name=rootless" {
|
||||
return nil, errors.New("cannot verify host bridge ports using a rootless Docker daemon")
|
||||
}
|
||||
}
|
||||
networks, err := cli.NetworkList(ctx, network.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
output, err := cmd.NewCommandMgr(cmd.WithContext(ctx), cmd.WithTimeout(5*time.Second)).RunWithOptionalSudoAndStdout("ip", "-details", "-json", "link", "show")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dockerIPv4BridgePorts(networks, output)
|
||||
}
|
||||
|
||||
func dockerIPv4BridgePorts(networks []network.Summary, linkJSON string) (map[string]bool, error) {
|
||||
bridges := make(map[string]bool)
|
||||
for _, item := range networks {
|
||||
if item.Driver != "bridge" || item.EnableIPv6 || item.ConfigOnly {
|
||||
continue
|
||||
}
|
||||
name := item.Options["com.docker.network.bridge.name"]
|
||||
if name == "" {
|
||||
if item.Options["com.docker.network.bridge.default_bridge"] == "true" {
|
||||
name = "docker0"
|
||||
} else if len(item.ID) >= 12 {
|
||||
name = "br-" + item.ID[:12]
|
||||
}
|
||||
}
|
||||
if name != "" {
|
||||
bridges[name] = true
|
||||
}
|
||||
}
|
||||
var links []struct {
|
||||
Name string `json:"ifname"`
|
||||
Master string `json:"master"`
|
||||
LinkInfo struct {
|
||||
Kind string `json:"info_kind"`
|
||||
} `json:"linkinfo"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(linkJSON), &links); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
confirmedBridges := make(map[string]bool)
|
||||
for _, link := range links {
|
||||
if link.LinkInfo.Kind == "bridge" && bridges[link.Name] {
|
||||
confirmedBridges[link.Name] = true
|
||||
}
|
||||
}
|
||||
ports := make(map[string]bool)
|
||||
for _, link := range links {
|
||||
if link.LinkInfo.Kind == "veth" && confirmedBridges[link.Master] {
|
||||
ports[link.Name] = true
|
||||
}
|
||||
}
|
||||
return ports, nil
|
||||
}
|
||||
|
||||
func enableFamilyForwardingSysctls(content, family string) string {
|
||||
lines := strings.Split(strings.TrimRight(content, "\n"), "\n")
|
||||
wanted := map[string]string{"net.ipv4.ip_forward": "net.ipv4.ip_forward = 1"}
|
||||
|
||||
@@ -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": [
|
||||
@@ -36372,9 +36389,6 @@ const docTemplate = `{
|
||||
"bound": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"forwardPolicy": {
|
||||
"type": "string"
|
||||
},
|
||||
"initialized": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -40152,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": [
|
||||
@@ -36368,9 +36385,6 @@
|
||||
"bound": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"forwardPolicy": {
|
||||
"type": "string"
|
||||
},
|
||||
"initialized": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -40148,6 +40162,14 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"dto.RedisCliInstall": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"taskID": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.RedisConf": {
|
||||
"properties": {
|
||||
"containerName": {
|
||||
|
||||
+15
-4
@@ -230,6 +230,10 @@ func (c *CommandHelper) buildPipeCommands(ctx context.Context, commands []PipeCo
|
||||
cmdItem.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
cmdItem.Cancel = func() error {
|
||||
_ = syscall.Kill(-cmdItem.Process.Pid, syscall.SIGKILL)
|
||||
return cmdItem.Process.Kill()
|
||||
}
|
||||
cmds = append(cmds, cmdItem)
|
||||
}
|
||||
return cmds
|
||||
@@ -305,6 +309,12 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
if newContext != nil {
|
||||
cmd.Cancel = func() error {
|
||||
_ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
|
||||
return cmd.Process.Kill()
|
||||
}
|
||||
}
|
||||
|
||||
customWriter := &CustomWriter{taskItem: c.taskItem}
|
||||
var stdout, stderr bytes.Buffer
|
||||
@@ -357,11 +367,12 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
|
||||
}()
|
||||
select {
|
||||
case err := <-done:
|
||||
if c.preserveErrorCause && newContext != nil && newContext.Err() != nil {
|
||||
if cmd.Process != nil && cmd.Process.Pid > 0 {
|
||||
_ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
|
||||
if newContext != nil && newContext.Err() != nil {
|
||||
resultErr := c.pipeResultErr(newContext, err)
|
||||
if c.preserveErrorCause {
|
||||
resultErr = &commandError{message: resultErr.Error(), cause: newContext.Err()}
|
||||
}
|
||||
return "", newContext.Err()
|
||||
return "", resultErr
|
||||
}
|
||||
if err != nil {
|
||||
out, resultErr := handleErr(&stdout, &stderr, c.IgnoreExist1, err)
|
||||
|
||||
@@ -23,7 +23,6 @@ export namespace Firewall {
|
||||
initialized: boolean;
|
||||
bound: boolean;
|
||||
reason?: string;
|
||||
forwardPolicy?: 'ACCEPT' | 'DROP';
|
||||
raInterfaces?: string[];
|
||||
}
|
||||
export interface BackendGroup {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onActivated, ref, watch } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
defineOptions({ name: 'TableSearch' });
|
||||
|
||||
const emit = defineEmits(['search', 'update:searchName']);
|
||||
@@ -42,10 +42,6 @@ watch(
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
onActivated(() => {
|
||||
searchInfo.value = props.searchName;
|
||||
});
|
||||
|
||||
const search = () => {
|
||||
emit('update:searchName', searchInfo.value);
|
||||
emit('search');
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, onDeactivated, onMounted, ref, useAttrs } from 'vue';
|
||||
import { computed, onMounted, ref, useAttrs } from 'vue';
|
||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
import { flattenVNodes, type FuTableOperationButton } from '@/components/table/shared';
|
||||
import { useCardColumns } from './useCardColumns';
|
||||
@@ -459,8 +459,6 @@ watch([currentViewMode, tableData, () => props.syncCardContentHeight], scheduleC
|
||||
flush: 'post',
|
||||
});
|
||||
|
||||
onDeactivated(clearSelects);
|
||||
|
||||
watch([() => props.paginationConfig?.currentPage, () => props.paginationConfig?.pageSize], clearSelects, {
|
||||
flush: 'sync',
|
||||
});
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { reactive, type UnwrapNestedRefs } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
import { getPageState } from '@/utils/page-state-cache';
|
||||
|
||||
/** A new page visit starts from its defaults instead of restoring the previous page number. */
|
||||
export const usePageState = <T extends object>(factory: () => T): UnwrapNestedRefs<T> => {
|
||||
return reactive(factory());
|
||||
const route = useRoute();
|
||||
const { currentNode } = useGlobalStore();
|
||||
const key = `${currentNode.value}:${String(route.name || route.path)}`;
|
||||
return reactive(getPageState(key, factory));
|
||||
};
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { watch, type Ref } from 'vue';
|
||||
import GlobalStore from '@/store/modules/global';
|
||||
|
||||
type SearchValue = string | number | undefined;
|
||||
type SearchFields = Record<string, Ref<SearchValue>>;
|
||||
|
||||
export const useSearchPersistence = (page: string, fields: SearchFields, scope?: () => SearchValue) => {
|
||||
const { currentNode } = storeToRefs(GlobalStore());
|
||||
const defaults = Object.fromEntries(Object.entries(fields).map(([name, field]) => [name, field.value]));
|
||||
let initialized = false;
|
||||
let restoring = false;
|
||||
let activeKey: string | undefined;
|
||||
|
||||
watch(
|
||||
() => {
|
||||
const context = scope?.();
|
||||
if (scope && context === undefined) return undefined;
|
||||
return `1panel:search:v1:${JSON.stringify([currentNode.value, page, context ?? ''])}`;
|
||||
},
|
||||
(key) => {
|
||||
activeKey = key;
|
||||
let saved: Record<string, unknown> = {};
|
||||
try {
|
||||
const parsed = activeKey ? JSON.parse(localStorage.getItem(activeKey) || '{}') : {};
|
||||
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) saved = parsed;
|
||||
} catch {}
|
||||
restoring = true;
|
||||
for (const [name, field] of Object.entries(fields)) {
|
||||
const value = saved[name];
|
||||
const initial = defaults[name];
|
||||
let fallback = initial;
|
||||
if (initialized) {
|
||||
fallback = typeof initial === 'string' ? '' : undefined;
|
||||
}
|
||||
const valid =
|
||||
(typeof value === 'string' || (typeof value === 'number' && Number.isFinite(value))) &&
|
||||
(fallback === undefined || typeof value === typeof fallback);
|
||||
if (value === null) {
|
||||
field.value = typeof initial === 'string' ? '' : undefined;
|
||||
} else if (valid) {
|
||||
field.value = value as SearchValue;
|
||||
} else {
|
||||
field.value = fallback;
|
||||
}
|
||||
}
|
||||
restoring = false;
|
||||
initialized = true;
|
||||
},
|
||||
{ immediate: true, flush: 'sync' },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => Object.fromEntries(Object.entries(fields).map(([name, field]) => [name, field.value ?? null])),
|
||||
(value) => {
|
||||
if (restoring || !activeKey) return;
|
||||
try {
|
||||
localStorage.setItem(activeKey, JSON.stringify(value));
|
||||
} catch {}
|
||||
},
|
||||
{ flush: 'sync' },
|
||||
);
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { onActivated, onDeactivated } from 'vue';
|
||||
import GlobalStore from '@/store/modules/global';
|
||||
import { bindSearchReset, type SearchFields } from '@/utils/search-reset';
|
||||
|
||||
interface SearchResetOptions {
|
||||
scope?: () => string | number | undefined;
|
||||
onRevisit: () => unknown;
|
||||
}
|
||||
|
||||
export const useSearchReset = (fields: SearchFields, options: SearchResetOptions) => {
|
||||
const { currentNode } = storeToRefs(GlobalStore());
|
||||
const reset = bindSearchReset(options.scope ? [currentNode, options.scope] : [currentNode], fields);
|
||||
let deactivated = false;
|
||||
onDeactivated(() => {
|
||||
deactivated = true;
|
||||
});
|
||||
onActivated(() => {
|
||||
if (!deactivated) return;
|
||||
deactivated = false;
|
||||
reset();
|
||||
return options.onRevisit();
|
||||
});
|
||||
};
|
||||
@@ -570,6 +570,8 @@ const message = {
|
||||
cancelKeepAlive: 'Stop keeping page session',
|
||||
},
|
||||
header: {
|
||||
userCenter: 'User Center',
|
||||
exitSystem: 'Log Out',
|
||||
logout: 'Logout',
|
||||
},
|
||||
database: {
|
||||
@@ -4306,8 +4308,6 @@ const message = {
|
||||
dockerGuard: 'Container Port Guard',
|
||||
systemFirewall: 'Host firewall',
|
||||
systemFirewallHelper: 'Controls host port access and inbound firewall rules.',
|
||||
forwardPolicyDropWarning:
|
||||
'This server restricts network forwarding. Port forwarding rules may not take effect.',
|
||||
forwardingHelper: 'Manages port-forwarding rules.',
|
||||
dockerFirewallHelper: 'Selects how 1Panel manages container port protection.',
|
||||
dockerNftablesRequirement: 'Docker ≥ 29.0.0, experimental',
|
||||
@@ -4390,9 +4390,9 @@ const message = {
|
||||
'Host INPUT rules do not directly protect this Docker published port. Click to open Container Port Guard.',
|
||||
notInitialized: 'Not initialized',
|
||||
ipv6RARisk:
|
||||
'IPv6 forwarding is blocked: interfaces {0} have accept_ra=1 and may rely on RA/SLAAC. Review the network configuration, persist accept_ra=2 on interfaces that need RA, then retry.',
|
||||
'IPv6 forwarding is blocked to prevent possible network loss. See the documentation and system logs for details.',
|
||||
ipv6RACheckFailed:
|
||||
'Cannot check IPv6 RA configuration. Enabling IPv6 forwarding is blocked. Check the system network configuration and retry.',
|
||||
'IPv6 forwarding is blocked because the network configuration could not be checked. See the system logs for details.',
|
||||
ipv6ForwardingOnDemand:
|
||||
'IPv6 forwarding is not enabled. It will be checked and enabled when IPv6 rules are added or restored.',
|
||||
familyUnsupported: 'The system does not support {0}',
|
||||
|
||||
@@ -570,6 +570,8 @@ const message = {
|
||||
cancelKeepAlive: 'Dejar de mantener la sesión de la página',
|
||||
},
|
||||
header: {
|
||||
userCenter: 'Centro de usuario',
|
||||
exitSystem: 'Cerrar sesión',
|
||||
logout: 'Cerrar sesión',
|
||||
},
|
||||
database: {
|
||||
@@ -4357,8 +4359,6 @@ const message = {
|
||||
dockerGuard: 'Protección de puertos de contenedores',
|
||||
systemFirewall: 'Firewall del host',
|
||||
systemFirewallHelper: 'Controla el acceso a los puertos del host y las reglas de entrada.',
|
||||
forwardPolicyDropWarning:
|
||||
'Este servidor restringe el reenvío de red. Es posible que las reglas de reenvío de puertos no surtan efecto.',
|
||||
forwardingHelper: 'Gestiona las reglas de reenvío de puertos.',
|
||||
dockerFirewallHelper: 'Selecciona cómo gestiona 1Panel la protección de puertos de contenedores.',
|
||||
dockerNftablesRequirement: 'Docker ≥ 29.0.0, experimental',
|
||||
@@ -4443,9 +4443,9 @@ const message = {
|
||||
'Las reglas INPUT del host no protegen directamente este puerto publicado por Docker. Haz clic para abrir la protección de puertos de contenedores.',
|
||||
notInitialized: 'No inicializado',
|
||||
ipv6RARisk:
|
||||
'El reenvío IPv6 está bloqueado: las interfaces {0} tienen accept_ra=1 y pueden depender de RA/SLAAC. Revise la configuración de red, configure accept_ra=2 de forma persistente en las interfaces que necesitan RA y vuelva a intentarlo.',
|
||||
'El reenvío IPv6 está bloqueado para evitar una posible pérdida de conectividad. Consulte la documentación y los registros del sistema.',
|
||||
ipv6RACheckFailed:
|
||||
'No se puede comprobar la configuración RA de IPv6. Se ha bloqueado la activación del reenvío IPv6. Revise la configuración de red y vuelva a intentarlo.',
|
||||
'El reenvío IPv6 está bloqueado porque no se pudo comprobar la configuración de red. Consulte los registros del sistema.',
|
||||
ipv6ForwardingOnDemand:
|
||||
'El reenvío IPv6 no está habilitado. Se comprobará y habilitará al añadir o restaurar reglas IPv6.',
|
||||
familyUnsupported: 'El sistema no admite {0}',
|
||||
|
||||
@@ -565,6 +565,8 @@ const message = {
|
||||
cancelKeepAlive: 'لغو حفظ نشست صفحه',
|
||||
},
|
||||
header: {
|
||||
userCenter: 'مرکز کاربری',
|
||||
exitSystem: 'خروج از سیستم',
|
||||
logout: 'خروج',
|
||||
},
|
||||
database: {
|
||||
@@ -4271,7 +4273,6 @@ const message = {
|
||||
dockerGuard: 'محافظت از پورت کانتینر',
|
||||
systemFirewall: 'فایروال میزبان',
|
||||
systemFirewallHelper: 'دسترسی پورتهای میزبان و قوانین ورودی را کنترل میکند.',
|
||||
forwardPolicyDropWarning: 'این سرور هدایت ترافیک شبکه را محدود کرده است. ممکن است قوانین هدایت پورت کار نکنند.',
|
||||
forwardingHelper: 'قوانین انتقال پورت را مدیریت میکند.',
|
||||
dockerFirewallHelper: 'روش مدیریت محافظت از پورت کانتینر در 1Panel را انتخاب میکند.',
|
||||
dockerNftablesRequirement: 'Docker ≥ 29.0.0، آزمایشی',
|
||||
@@ -4355,10 +4356,9 @@ const message = {
|
||||
dockerInputNotProtected:
|
||||
'قوانین INPUT میزبان مستقیماً از این پورت منتشرشده Docker محافظت نمیکنند. برای باز کردن محافظت از پورت کانتینر کلیک کنید.',
|
||||
notInitialized: 'راهاندازی نشده',
|
||||
ipv6RARisk:
|
||||
'هدایت IPv6 مسدود شد: رابطهای {0} دارای accept_ra=1 هستند و ممکن است به RA/SLAAC وابسته باشند. تنظیمات شبکه را بررسی کنید، برای رابطهای نیازمند RA مقدار accept_ra=2 را بهصورت پایدار تنظیم کنید و دوباره تلاش کنید.',
|
||||
ipv6RARisk: 'فعالسازی هدایت IPv6 به دلیل خطر قطع شبکه مسدود شد. مستندات و گزارشهای سیستم را بررسی کنید.',
|
||||
ipv6RACheckFailed:
|
||||
'بررسی تنظیمات RA در IPv6 ممکن نیست. فعالسازی هدایت IPv6 مسدود شده است. تنظیمات شبکه سیستم را بررسی کرده و دوباره تلاش کنید.',
|
||||
'فعالسازی هدایت IPv6 به دلیل عدم امکان بررسی پیکربندی شبکه مسدود شد. جزئیات در گزارشهای سیستم موجود است.',
|
||||
ipv6ForwardingOnDemand: 'هدایت IPv6 فعال نیست. هنگام افزودن یا بازیابی قوانین IPv6 بررسی و فعال خواهد شد.',
|
||||
familyUnsupported: 'سیستم از {0} پشتیبانی نمیکند',
|
||||
familyChainIssue: '{0} · زنجیره {1} · {2}',
|
||||
|
||||
@@ -568,6 +568,8 @@ const message = {
|
||||
cancelKeepAlive: 'ページセッションの保持を解除',
|
||||
},
|
||||
header: {
|
||||
userCenter: 'ユーザーセンター',
|
||||
exitSystem: 'ログアウト',
|
||||
logout: 'ログアウト',
|
||||
},
|
||||
database: {
|
||||
@@ -4294,8 +4296,6 @@ const message = {
|
||||
dockerGuard: 'コンテナポート保護',
|
||||
systemFirewall: 'ホストファイアウォール',
|
||||
systemFirewallHelper: 'ホストのポートアクセスと受信ルールを管理します。',
|
||||
forwardPolicyDropWarning:
|
||||
'このサーバーではネットワーク転送が制限されているため、ポート転送ルールが機能しない可能性があります。',
|
||||
forwardingHelper: 'ポート転送ルールを管理します。',
|
||||
dockerFirewallHelper: '1Panel のコンテナポート保護の管理方法を選択します。',
|
||||
dockerNftablesRequirement: 'Docker ≥ 29.0.0、実験的',
|
||||
@@ -4377,9 +4377,9 @@ const message = {
|
||||
'ホストの INPUT ルールでは、この Docker 公開ポートを直接保護できません。クリックしてコンテナポート保護を開きます。',
|
||||
notInitialized: '未初期化',
|
||||
ipv6RARisk:
|
||||
'IPv6 転送がブロックされています。インターフェース {0} は accept_ra=1 で、RA/SLAAC に依存している可能性があります。ネットワーク設定を確認し、RA が必要なインターフェースで accept_ra=2 を永続的に設定してから再試行してください。',
|
||||
'ネットワーク切断のおそれがあるため、IPv6 転送の有効化をブロックしました。ドキュメントとシステムログをご確認ください。',
|
||||
ipv6RACheckFailed:
|
||||
'IPv6 RA 設定を確認できないため、IPv6 転送の有効化をブロックしました。システムのネットワーク設定を確認して再試行してください。',
|
||||
'ネットワーク設定を確認できないため、IPv6 転送の有効化をブロックしました。詳細はシステムログをご確認ください。',
|
||||
ipv6ForwardingOnDemand: 'IPv6 転送は無効です。IPv6 ルールの追加または復元時に確認して有効化します。',
|
||||
familyUnsupported: 'システムは {0} をサポートしていません',
|
||||
familyChainIssue: '{0} · {1} チェーン · {2}',
|
||||
|
||||
@@ -562,6 +562,8 @@ const message = {
|
||||
cancelKeepAlive: '페이지 세션 유지 해제',
|
||||
},
|
||||
header: {
|
||||
userCenter: '사용자 센터',
|
||||
exitSystem: '로그아웃',
|
||||
logout: '로그아웃',
|
||||
},
|
||||
database: {
|
||||
@@ -4215,7 +4217,6 @@ const message = {
|
||||
dockerGuard: '컨테이너 포트 보호',
|
||||
systemFirewall: '호스트 방화벽',
|
||||
systemFirewallHelper: '호스트 포트 접근과 인바운드 규칙을 관리합니다.',
|
||||
forwardPolicyDropWarning: '현재 서버에서 네트워크 전달이 제한되어 포트 전달 규칙이 작동하지 않을 수 있습니다.',
|
||||
forwardingHelper: '포트 포워딩 규칙을 관리합니다.',
|
||||
dockerFirewallHelper: '1Panel 컨테이너 포트 보호 관리 방식을 선택합니다.',
|
||||
dockerNftablesRequirement: 'Docker ≥ 29.0.0, 실험적',
|
||||
@@ -4296,9 +4297,9 @@ const message = {
|
||||
'호스트 INPUT 규칙은 이 Docker 게시 포트를 직접 보호하지 않습니다. 클릭하여 컨테이너 포트 보호를 여세요.',
|
||||
notInitialized: '초기화되지 않음',
|
||||
ipv6RARisk:
|
||||
'IPv6 전달이 차단되었습니다. 인터페이스 {0}의 accept_ra=1 설정으로 인해 RA/SLAAC에 의존할 수 있습니다. 네트워크 설정을 확인하고 RA가 필요한 인터페이스에 accept_ra=2를 영구 설정한 후 다시 시도하세요.',
|
||||
'네트워크 연결이 끊길 위험이 있어 IPv6 전달 활성화를 차단했습니다. 문서와 시스템 로그를 확인하세요.',
|
||||
ipv6RACheckFailed:
|
||||
'IPv6 RA 설정을 확인할 수 없어 IPv6 전달 활성화를 차단했습니다. 시스템 네트워크 설정을 확인한 후 다시 시도하세요.',
|
||||
'네트워크 구성을 확인할 수 없어 IPv6 전달 활성화를 차단했습니다. 자세한 내용은 시스템 로그를 확인하세요.',
|
||||
ipv6ForwardingOnDemand:
|
||||
'IPv6 전달이 활성화되지 않았습니다. IPv6 규칙을 추가하거나 복원할 때 확인 후 활성화됩니다.',
|
||||
familyUnsupported: '시스템에서 {0}을 지원하지 않습니다',
|
||||
|
||||
@@ -563,6 +563,8 @@ const message = {
|
||||
cancelKeepAlive: 'ຍົກເລີກການຮັກສາເຊັດຊັນໜ້າ',
|
||||
},
|
||||
header: {
|
||||
userCenter: 'ສູນຜູ້ໃຊ້',
|
||||
exitSystem: 'ອອກຈາກລະບົບ',
|
||||
logout: 'ອອກຈາກລະບົບ',
|
||||
},
|
||||
database: {
|
||||
@@ -4182,7 +4184,6 @@ const message = {
|
||||
dockerGuard: 'ການປ້ອງກັນພອດຄອນເທນເນີ',
|
||||
systemFirewall: 'ໄຟວໍໂຮສ',
|
||||
systemFirewallHelper: 'ຄວບຄຸມການເຂົ້າເຖິງພອດໂຮສ ແລະ ກົດຂາເຂົ້າ.',
|
||||
forwardPolicyDropWarning: 'ເຊີບເວີນີ້ຈຳກັດການສົ່ງຕໍ່ເຄືອຂ່າຍ. ກົດການສົ່ງຕໍ່ພອດອາດບໍ່ເຮັດວຽກ.',
|
||||
forwardingHelper: 'ຈັດການກົດການສົ່ງຕໍ່ພອດ.',
|
||||
dockerFirewallHelper: 'ເລືອກວິທີທີ່ 1Panel ຈັດການການປ້ອງກັນພອດຄອນເທນເນີ.',
|
||||
dockerNftablesRequirement: 'Docker ≥ 29.0.0, ທົດລອງ',
|
||||
@@ -4260,9 +4261,9 @@ const message = {
|
||||
'ກົດ INPUT ຂອງໂຮສບໍ່ໄດ້ປ້ອງກັນພອດ Docker ນີ້ໂດຍກົງ. ຄລິກເພື່ອເປີດການປ້ອງກັນພອດຄອນເທນເນີ.',
|
||||
notInitialized: 'ຍັງບໍ່ໄດ້ເລີ່ມຕົ້ນ',
|
||||
ipv6RARisk:
|
||||
'ການສົ່ງຕໍ່ IPv6 ຖືກບລັອກ: ອິນເຕີເຟດ {0} ມີ accept_ra=1 ແລະອາດອາໄສ RA/SLAAC. ກວດສອບການຕັ້ງຄ່າເຄືອຂ່າຍ, ຕັ້ງ accept_ra=2 ແບບຖາວອນໃຫ້ອິນເຕີເຟດທີ່ຕ້ອງການ RA ແລ້ວລອງໃໝ່.',
|
||||
'ການສົ່ງຕໍ່ IPv6 ຖືກບລັອກເພື່ອປ້ອງກັນຄວາມສ່ຽງທີ່ເຄືອຂ່າຍຈະຂາດການເຊື່ອມຕໍ່. ເບິ່ງເອກະສານ ແລະ ບັນທຶກລະບົບ.',
|
||||
ipv6RACheckFailed:
|
||||
'ບໍ່ສາມາດກວດສອບການຕັ້ງຄ່າ IPv6 RA ໄດ້. ການເປີດໃຊ້ການສົ່ງຕໍ່ IPv6 ຖືກບລັອກ. ກວດສອບການຕັ້ງຄ່າເຄືອຂ່າຍແລ້ວລອງໃໝ່.',
|
||||
'ການສົ່ງຕໍ່ IPv6 ຖືກບລັອກເນື່ອງຈາກບໍ່ສາມາດກວດສອບການຕັ້ງຄ່າເຄືອຂ່າຍໄດ້. ເບິ່ງລາຍລະອຽດໃນບັນທຶກລະບົບ.',
|
||||
ipv6ForwardingOnDemand: 'ຍັງບໍ່ໄດ້ເປີດໃຊ້ການສົ່ງຕໍ່ IPv6. ລະບົບຈະກວດສອບແລະເປີດໃຊ້ເມື່ອເພີ່ມຫຼືກູ້ຄືນກົດ IPv6.',
|
||||
familyUnsupported: 'ລະບົບບໍ່ຮອງຮັບ {0}',
|
||||
familyChainIssue: '{0} · chain {1} · {2}',
|
||||
|
||||
@@ -575,6 +575,8 @@ const message = {
|
||||
cancelKeepAlive: 'Hentikan pengekalan sesi halaman',
|
||||
},
|
||||
header: {
|
||||
userCenter: 'Pusat Pengguna',
|
||||
exitSystem: 'Log keluar',
|
||||
logout: 'Log keluar',
|
||||
},
|
||||
database: {
|
||||
@@ -4374,8 +4376,6 @@ const message = {
|
||||
dockerGuard: 'Perlindungan port bekas',
|
||||
systemFirewall: 'Tembok api hos',
|
||||
systemFirewallHelper: 'Mengawal akses port hos dan peraturan masuk.',
|
||||
forwardPolicyDropWarning:
|
||||
'Pelayan ini mengehadkan pemajuan rangkaian. Peraturan pemajuan port mungkin tidak berfungsi.',
|
||||
forwardingHelper: 'Mengurus peraturan pemajuan port.',
|
||||
dockerFirewallHelper: 'Memilih cara 1Panel mengurus perlindungan port bekas.',
|
||||
dockerNftablesRequirement: 'Docker ≥ 29.0.0, percubaan',
|
||||
@@ -4459,9 +4459,9 @@ const message = {
|
||||
'Peraturan INPUT hos tidak melindungi port terbitan Docker ini secara langsung. Klik untuk membuka perlindungan port bekas.',
|
||||
notInitialized: 'Belum dimulakan',
|
||||
ipv6RARisk:
|
||||
'Pemajuan IPv6 disekat: antara muka {0} mempunyai accept_ra=1 dan mungkin bergantung pada RA/SLAAC. Semak konfigurasi rangkaian, tetapkan accept_ra=2 secara kekal pada antara muka yang memerlukan RA, kemudian cuba lagi.',
|
||||
'Pemajuan IPv6 disekat bagi mengelakkan risiko kehilangan sambungan rangkaian. Rujuk dokumentasi dan log sistem.',
|
||||
ipv6RACheckFailed:
|
||||
'Konfigurasi RA IPv6 tidak dapat diperiksa. Pengaktifan pemajuan IPv6 disekat. Semak konfigurasi rangkaian sistem dan cuba lagi.',
|
||||
'Pemajuan IPv6 disekat kerana konfigurasi rangkaian tidak dapat diperiksa. Lihat log sistem untuk butiran.',
|
||||
ipv6ForwardingOnDemand:
|
||||
'Pemajuan IPv6 belum diaktifkan. Ia akan diperiksa dan diaktifkan apabila peraturan IPv6 ditambah atau dipulihkan.',
|
||||
familyUnsupported: 'Sistem tidak menyokong {0}',
|
||||
|
||||
@@ -568,6 +568,8 @@ const message = {
|
||||
cancelKeepAlive: 'Parar de manter a sessão da página',
|
||||
},
|
||||
header: {
|
||||
userCenter: 'Central do usuário',
|
||||
exitSystem: 'Sair',
|
||||
logout: 'Logout',
|
||||
},
|
||||
database: {
|
||||
@@ -4395,8 +4397,6 @@ const message = {
|
||||
dockerGuard: 'Proteção de portas de contêineres',
|
||||
systemFirewall: 'Firewall do host',
|
||||
systemFirewallHelper: 'Controla o acesso às portas do host e as regras de entrada.',
|
||||
forwardPolicyDropWarning:
|
||||
'Este servidor restringe o encaminhamento de rede. As regras de encaminhamento de portas podem não funcionar.',
|
||||
forwardingHelper: 'Gerencia regras de encaminhamento de portas.',
|
||||
dockerFirewallHelper: 'Seleciona como o 1Panel gerencia a proteção de portas de contêineres.',
|
||||
dockerNftablesRequirement: 'Docker ≥ 29.0.0, experimental',
|
||||
@@ -4482,9 +4482,9 @@ const message = {
|
||||
'As regras INPUT do host não protegem diretamente esta porta publicada pelo Docker. Clique para abrir a proteção de portas de contêineres.',
|
||||
notInitialized: 'Não inicializado',
|
||||
ipv6RARisk:
|
||||
'O encaminhamento IPv6 foi bloqueado: as interfaces {0} têm accept_ra=1 e podem depender de RA/SLAAC. Verifique a configuração de rede, configure accept_ra=2 de forma persistente nas interfaces que precisam de RA e tente novamente.',
|
||||
'O encaminhamento IPv6 foi bloqueado para evitar uma possível perda de conexão. Consulte a documentação e os logs do sistema.',
|
||||
ipv6RACheckFailed:
|
||||
'Não foi possível verificar a configuração RA do IPv6. A ativação do encaminhamento IPv6 foi bloqueada. Verifique a configuração de rede e tente novamente.',
|
||||
'O encaminhamento IPv6 foi bloqueado porque não foi possível verificar a configuração de rede. Consulte os logs do sistema.',
|
||||
ipv6ForwardingOnDemand:
|
||||
'O encaminhamento IPv6 não está ativado. Ele será verificado e ativado ao adicionar ou restaurar regras IPv6.',
|
||||
familyUnsupported: 'O sistema não oferece suporte a {0}',
|
||||
|
||||
@@ -566,6 +566,8 @@ const message = {
|
||||
cancelKeepAlive: 'Не сохранять сеанс страницы',
|
||||
},
|
||||
header: {
|
||||
userCenter: 'Центр пользователя',
|
||||
exitSystem: 'Выйти из системы',
|
||||
logout: 'Выход',
|
||||
},
|
||||
database: {
|
||||
@@ -4361,8 +4363,6 @@ const message = {
|
||||
dockerGuard: 'Защита портов контейнеров',
|
||||
systemFirewall: 'Брандмауэр хоста',
|
||||
systemFirewallHelper: 'Управляет доступом к портам хоста и входящими правилами.',
|
||||
forwardPolicyDropWarning:
|
||||
'На этом сервере ограничена пересылка сетевого трафика. Правила перенаправления портов могут не работать.',
|
||||
forwardingHelper: 'Управляет правилами перенаправления портов.',
|
||||
dockerFirewallHelper: 'Выбирает способ управления защитой портов контейнеров в 1Panel.',
|
||||
dockerNftablesRequirement: 'Docker ≥ 29.0.0, экспериментально',
|
||||
@@ -4447,9 +4447,9 @@ const message = {
|
||||
'Правила INPUT хоста не защищают этот опубликованный Docker-порт напрямую. Нажмите, чтобы открыть защиту портов контейнеров.',
|
||||
notInitialized: 'Не инициализировано',
|
||||
ipv6RARisk:
|
||||
'Пересылка IPv6 заблокирована: интерфейсы {0} имеют accept_ra=1 и могут зависеть от RA/SLAAC. Проверьте настройки сети, сохраните accept_ra=2 для интерфейсов, которым нужен RA, и повторите попытку.',
|
||||
'Включение переадресации IPv6 заблокировано из-за риска потери связи. См. документацию и системные журналы.',
|
||||
ipv6RACheckFailed:
|
||||
'Не удалось проверить настройки RA для IPv6. Включение пересылки IPv6 заблокировано. Проверьте настройки сети и повторите попытку.',
|
||||
'Включение переадресации IPv6 заблокировано: не удалось проверить настройки сети. Подробности в системных журналах.',
|
||||
ipv6ForwardingOnDemand:
|
||||
'Пересылка IPv6 не включена. Проверка и включение выполняются при добавлении или восстановлении правил IPv6.',
|
||||
familyUnsupported: 'Система не поддерживает {0}',
|
||||
|
||||
@@ -570,6 +570,8 @@ const message = {
|
||||
cancelKeepAlive: 'Sayfa oturumunu korumayı bırak',
|
||||
},
|
||||
header: {
|
||||
userCenter: 'Kullanıcı Merkezi',
|
||||
exitSystem: 'Çıkış yap',
|
||||
logout: 'Çıkış',
|
||||
},
|
||||
database: {
|
||||
@@ -4379,7 +4381,6 @@ const message = {
|
||||
dockerGuard: 'Konteyner portu koruması',
|
||||
systemFirewall: 'Ana makine güvenlik duvarı',
|
||||
systemFirewallHelper: 'Ana makine port erişimini ve gelen kuralları yönetir.',
|
||||
forwardPolicyDropWarning: 'Bu sunucu ağ yönlendirmesini kısıtlıyor. Port yönlendirme kuralları çalışmayabilir.',
|
||||
forwardingHelper: 'Port yönlendirme kurallarını yönetir.',
|
||||
dockerFirewallHelper: '1Panel konteyner portu korumasının nasıl yönetileceğini seçer.',
|
||||
dockerNftablesRequirement: 'Docker ≥ 29.0.0, deneysel',
|
||||
@@ -4466,9 +4467,9 @@ const message = {
|
||||
'Ana makine INPUT kuralları bu Docker yayımlanmış portunu doğrudan korumaz. Konteyner portu korumasını açmak için tıklayın.',
|
||||
notInitialized: 'Başlatılmadı',
|
||||
ipv6RARisk:
|
||||
'IPv6 yönlendirme engellendi: {0} arayüzlerinde accept_ra=1 ayarlı ve bu arayüzler RA/SLAAC kullanıyor olabilir. Ağ yapılandırmasını kontrol edin, RA gereken arayüzlerde accept_ra=2 ayarını kalıcı olarak uygulayın ve yeniden deneyin.',
|
||||
'Ağ bağlantısının kesilme riski nedeniyle IPv6 yönlendirmesi engellendi. Belgeleri ve sistem günlüklerini inceleyin.',
|
||||
ipv6RACheckFailed:
|
||||
'IPv6 RA yapılandırması kontrol edilemedi. IPv6 yönlendirmeyi etkinleştirme engellendi. Sistem ağ yapılandırmasını kontrol edip yeniden deneyin.',
|
||||
'Ağ yapılandırması denetlenemediği için IPv6 yönlendirmesi engellendi. Ayrıntılar için sistem günlüklerini inceleyin.',
|
||||
ipv6ForwardingOnDemand:
|
||||
'IPv6 yönlendirme etkin değil. IPv6 kuralları eklenirken veya geri yüklenirken kontrol edilip etkinleştirilecektir.',
|
||||
familyUnsupported: 'Sistem {0} desteğini sunmuyor',
|
||||
|
||||
@@ -548,6 +548,8 @@ const message = {
|
||||
cancelKeepAlive: '取消保持頁面工作階段',
|
||||
},
|
||||
header: {
|
||||
userCenter: '使用者中心',
|
||||
exitSystem: '登出系統',
|
||||
logout: '登出',
|
||||
},
|
||||
database: {
|
||||
@@ -4020,7 +4022,6 @@ const message = {
|
||||
dockerGuard: '容器連接埠防護',
|
||||
systemFirewall: '主機防火牆',
|
||||
systemFirewallHelper: '用於管理主機連接埠存取和入站規則。',
|
||||
forwardPolicyDropWarning: '目前伺服器限制了網路轉送,轉送規則可能無法生效。',
|
||||
forwardingHelper: '用於管理連接埠轉發規則。',
|
||||
dockerFirewallHelper: '用於選擇 1Panel 容器連接埠防護的管理方式。',
|
||||
dockerNftablesRequirement: 'Docker ≥ 29.0.0,實驗性',
|
||||
@@ -4092,9 +4093,8 @@ const message = {
|
||||
dockerInputUseHostFirewall: '此連接埠需要透過主機防火牆設定存取規則,請前往主機防火牆進行設定。',
|
||||
dockerInputNotProtected: '主機 INPUT 規則無法直接保護此 Docker 發佈連接埠,點擊前往容器連接埠防護。',
|
||||
notInitialized: '未初始化',
|
||||
ipv6RARisk:
|
||||
'IPv6 轉送已阻止:網路介面 {0} 的 accept_ra=1,可能依賴 RA/SLAAC。請確認網路設定,為需要接收 RA 的介面持久化設定 accept_ra=2 後重試。',
|
||||
ipv6RACheckFailed: '無法檢查 IPv6 RA 設定,已阻止啟用 IPv6 轉送。請檢查系統網路設定後重試。',
|
||||
ipv6RARisk: 'IPv6 轉送可能導致網路中斷,已阻止啟用。請參閱文件,詳情見系統日誌。',
|
||||
ipv6RACheckFailed: '無法檢查 IPv6 網路設定,已阻止啟用轉送。詳情見系統日誌。',
|
||||
ipv6ForwardingOnDemand: 'IPv6 轉送尚未啟用,將在新增或還原 IPv6 規則時檢查並啟用。',
|
||||
familyUnsupported: '系統不支援 {0}',
|
||||
familyChainIssue: '{0} · {1} 鏈 · {2}',
|
||||
|
||||
@@ -552,6 +552,8 @@ const message = {
|
||||
cancelKeepAlive: '取消保持页面会话',
|
||||
},
|
||||
header: {
|
||||
userCenter: '用户中心',
|
||||
exitSystem: '退出系统',
|
||||
logout: '退出登录',
|
||||
},
|
||||
database: {
|
||||
@@ -4066,7 +4068,6 @@ const message = {
|
||||
dockerGuard: '容器端口防护',
|
||||
systemFirewall: '主机防火墙',
|
||||
systemFirewallHelper: '用于管理主机端口访问和入站规则。',
|
||||
forwardPolicyDropWarning: '当前服务器限制了网络转发,转发规则可能无法生效。',
|
||||
forwardingHelper: '用于管理端口转发规则。',
|
||||
dockerFirewallHelper: '用于选择 1Panel 容器端口防护的管理方式。',
|
||||
dockerNftablesRequirement: 'Docker ≥ 29.0.0,实验性',
|
||||
@@ -4138,9 +4139,8 @@ const message = {
|
||||
dockerInputUseHostFirewall: '该端口需要通过主机防火墙设置访问规则,请前往主机防火墙进行配置。',
|
||||
dockerInputNotProtected: '宿主机 INPUT 规则不能直接保护此 Docker 发布端口,点击前往容器端口防护。',
|
||||
notInitialized: '未初始化',
|
||||
ipv6RARisk:
|
||||
'IPv6 转发已阻止:网卡 {0} 的 accept_ra=1,可能依赖 RA/SLAAC。请确认网络配置,为需要接收 RA 的网卡持久化设置 accept_ra=2 后重试。',
|
||||
ipv6RACheckFailed: '无法检查 IPv6 RA 配置,已阻止启用 IPv6 转发。请检查系统网络配置后重试。',
|
||||
ipv6RARisk: 'IPv6 转发存在断网风险,已阻止启用。请参阅文档,详情见系统日志。',
|
||||
ipv6RACheckFailed: '无法检查 IPv6 网络配置,已阻止启用转发。详情见系统日志。',
|
||||
ipv6ForwardingOnDemand: 'IPv6 转发尚未启用,将在添加或恢复 IPv6 规则时检查并启用。',
|
||||
familyUnsupported: '系统不支持 {0}',
|
||||
familyChainIssue: '{0} · {1} 链 · {2}',
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<el-popover
|
||||
v-model:visible="popoverVisible"
|
||||
placement="right-end"
|
||||
placement="right-start"
|
||||
:show-arrow="false"
|
||||
:offset="0"
|
||||
:width="200"
|
||||
@@ -26,12 +26,6 @@
|
||||
</div>
|
||||
</template>
|
||||
<div class="dropdown-menu" v-loading="loading || switchingNode">
|
||||
<div class="dropdown-item" v-if="currentUser" @click="changeUserInfo">
|
||||
<SvgIcon class="icon" iconName="p-gerenzhongxin1" />
|
||||
{{ currentUser.name }}
|
||||
</div>
|
||||
<el-divider class="divider" />
|
||||
|
||||
<div class="dropdown-item" @click="openTask">
|
||||
<SvgIcon class="icon" iconName="p-renwuzhongxin1" />
|
||||
{{ $t('menu.msgCenter') }}
|
||||
@@ -74,11 +68,6 @@
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<el-divider class="divider" />
|
||||
<div class="dropdown-item" @click="logout">
|
||||
<SvgIcon class="icon" iconName="p-tuichudenglu3" />
|
||||
{{ $t('commons.login.logout') }}
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
<NodeDrawer
|
||||
@@ -129,7 +118,7 @@ const props = defineProps({
|
||||
|
||||
const defaultNodeLimit = 8;
|
||||
|
||||
const emit = defineEmits(['openTask', 'refresh']);
|
||||
const emit = defineEmits(['openTask', 'refresh', 'userChange']);
|
||||
bus.on('refreshTask', () => {
|
||||
checkTask();
|
||||
});
|
||||
@@ -272,6 +261,7 @@ const openNodeDashboard = () => {
|
||||
};
|
||||
|
||||
const logout = () => {
|
||||
popoverVisible.value = false;
|
||||
ElMessageBox.confirm(i18n.global.t('commons.msg.sureLogOut'), i18n.global.t('commons.msg.infoTitle'), {
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
@@ -297,13 +287,17 @@ const loadCurrentUser = async (currentNode?: string) => {
|
||||
const authInfo = await syncAuthInfo(currentNode);
|
||||
if (authInfo) {
|
||||
currentUser.value = authInfo;
|
||||
emit('userChange', authInfo);
|
||||
}
|
||||
};
|
||||
const changeUserInfo = () => {
|
||||
popoverVisible.value = false;
|
||||
loadCurrentUser();
|
||||
userInfoRef.value?.openDrawer();
|
||||
};
|
||||
|
||||
defineExpose({ openUserInfo: changeUserInfo, logout });
|
||||
|
||||
onMounted(() => {
|
||||
loadNodes();
|
||||
checkTask();
|
||||
@@ -318,7 +312,7 @@ onMounted(() => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid var(--panel-footer-border);
|
||||
border-bottom: 1px solid var(--panel-footer-border);
|
||||
height: 48px;
|
||||
.icon {
|
||||
margin-left: 25px;
|
||||
|
||||
@@ -10,6 +10,14 @@
|
||||
<PrimaryMenu />
|
||||
</div>
|
||||
<Logo :isCollapse="isCollapse" />
|
||||
<Collapse
|
||||
ref="nodeSelectorRef"
|
||||
class="node-selector"
|
||||
:version="version"
|
||||
@open-task="openTask"
|
||||
@refresh="search"
|
||||
@user-change="currentUser = $event"
|
||||
/>
|
||||
<el-scrollbar>
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
@@ -23,7 +31,40 @@
|
||||
<SubItem :menuList="routerMenus" :level="0" />
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
<Collapse :version="version" @open-task="openTask" @refresh="search" />
|
||||
<el-dropdown
|
||||
v-if="currentUser"
|
||||
class="user-dropdown"
|
||||
trigger="click"
|
||||
placement="right-end"
|
||||
:popper-options="{ modifiers: [{ name: 'flip', enabled: false }] }"
|
||||
:show-arrow="false"
|
||||
:hide-on-click="true"
|
||||
@visible-change="userMenuVisible = $event"
|
||||
@command="handleUserCommand"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="user-entry"
|
||||
:class="{ 'is-active': userMenuVisible }"
|
||||
:aria-label="currentUser.name"
|
||||
:title="currentUser.name"
|
||||
>
|
||||
<SvgIcon class="icon" iconName="p-gerenzhongxin1" />
|
||||
<span v-if="!isCollapse" class="user-name">{{ currentUser.name }}</span>
|
||||
</button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="user-menu">
|
||||
<el-dropdown-item command="userCenter">
|
||||
<SvgIcon class="user-menu-icon" iconName="p-gerenzhongxin1" />
|
||||
<span>{{ $t('header.userCenter') }}</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="logout">
|
||||
<SvgIcon class="user-menu-icon" iconName="p-tuichudenglu3" />
|
||||
<span>{{ $t('header.exitSystem') }}</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -40,12 +81,24 @@ import { getSettingBaseInfo } from '@/api/modules/setting';
|
||||
import PrimaryMenu from '@/assets/images/menu-bg.svg?component';
|
||||
import { hasPermissionMetaAccess, hasRouteRoleAccess } from '@/utils/rbac';
|
||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
import type { Login } from '@/api/interface/auth';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const menuStore = MenuStore();
|
||||
const { currentNode, isAdmin, menuAccordion, permissions } = useGlobalStore();
|
||||
const version = ref();
|
||||
const nodeSelectorRef = ref<InstanceType<typeof Collapse>>();
|
||||
const currentUser = ref<Login.AuthInfo>();
|
||||
const userMenuVisible = ref(false);
|
||||
|
||||
const handleUserCommand = (command: string) => {
|
||||
if (command === 'userCenter') {
|
||||
nodeSelectorRef.value?.openUserInfo();
|
||||
} else if (command === 'logout') {
|
||||
nodeSelectorRef.value?.logout();
|
||||
}
|
||||
};
|
||||
|
||||
const activeMenu = computed(() => {
|
||||
const { meta, path } = route;
|
||||
@@ -324,8 +377,14 @@ watch(
|
||||
height: 100%;
|
||||
background: var(--panel-menu-bg-color) no-repeat top;
|
||||
|
||||
.logo,
|
||||
.node-selector {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.el-scrollbar {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
.el-menu {
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
@@ -337,4 +396,62 @@ watch(
|
||||
.ico {
|
||||
height: 20px !important;
|
||||
}
|
||||
|
||||
.user-dropdown {
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.user-menu {
|
||||
min-width: 160px;
|
||||
|
||||
:deep(.el-dropdown-menu__item) {
|
||||
gap: 8px;
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
.user-menu-icon {
|
||||
flex-shrink: 0;
|
||||
font-size: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.user-entry {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
padding: 0 25px;
|
||||
border: 0;
|
||||
border-top: 1px solid var(--panel-footer-border);
|
||||
background: transparent;
|
||||
color: var(--panel-main-bg-color-1);
|
||||
font-family: inherit;
|
||||
font-size: var(--el-font-size-base);
|
||||
line-height: 1.5;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
|
||||
.icon {
|
||||
flex-shrink: 0;
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus-visible,
|
||||
&.is-active {
|
||||
color: var(--el-color-primary);
|
||||
background: var(--el-menu-item-bg-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { bindSearchReset } from '@/utils/search-reset';
|
||||
import router from '@/routers/router';
|
||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref, reactive, toRef } from 'vue';
|
||||
import { MsgError } from '@/utils/message';
|
||||
@@ -49,12 +47,12 @@ export const ProcessStore = defineStore('ProcessStore', () => {
|
||||
port: undefined,
|
||||
});
|
||||
|
||||
const { currentNode } = useGlobalStore();
|
||||
bindSearchReset([() => router.currentRoute.value.path, currentNode], {
|
||||
useSearchPersistence('host/process/process', {
|
||||
pid: toRef(psSearch, 'pid'),
|
||||
name: toRef(psSearch, 'name'),
|
||||
username: toRef(psSearch, 'username'),
|
||||
|
||||
});
|
||||
useSearchPersistence('host/process/network', {
|
||||
processID: toRef(netSearch, 'processID'),
|
||||
processName: toRef(netSearch, 'processName'),
|
||||
port: toRef(netSearch, 'port'),
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { watch, type Ref, type WatchSource } from 'vue';
|
||||
|
||||
export type SearchFields = Record<string, Ref<string | number | undefined>>;
|
||||
|
||||
export const bindSearchReset = (sources: WatchSource[], fields: SearchFields) => {
|
||||
const defaults = Object.values(fields).map((field) => ({ field, value: field.value }));
|
||||
const reset = () => {
|
||||
for (const { field, value } of defaults) field.value = value;
|
||||
};
|
||||
watch(sources, reset, { flush: 'sync' });
|
||||
return reset;
|
||||
};
|
||||
@@ -172,7 +172,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Refresh, Search } from '@element-plus/icons-vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
@@ -193,16 +193,10 @@ const searching = ref(false);
|
||||
const operating = ref('');
|
||||
const keyword = ref('');
|
||||
const marketKeyword = ref('');
|
||||
useSearchReset(
|
||||
useSearchPersistence(
|
||||
'ai/agents/agent/config/tabs/plugins',
|
||||
{ keyword, marketKeyword },
|
||||
{
|
||||
scope: () => agentId.value || undefined,
|
||||
onRevisit: () => {
|
||||
page.value = 1;
|
||||
marketResults.value = [];
|
||||
marketSearched.value = false;
|
||||
},
|
||||
},
|
||||
() => agentId.value || undefined,
|
||||
);
|
||||
const origin = ref('');
|
||||
const status = ref('');
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Refresh, Search } from '@element-plus/icons-vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
@@ -169,15 +169,10 @@ const getDefaultMarketSource = (): HermesSkillMarketSource => {
|
||||
const marketSource = ref<HermesSkillMarketSource>(getDefaultMarketSource());
|
||||
const marketSearched = ref(false);
|
||||
const agentId = ref(0);
|
||||
useSearchReset(
|
||||
useSearchPersistence(
|
||||
'ai/agents/agent/config/tabs/skills/hermes',
|
||||
{ installedKeyword, marketKeyword },
|
||||
{
|
||||
scope: () => agentId.value || undefined,
|
||||
onRevisit: () => {
|
||||
marketResults.value = [];
|
||||
marketSearched.value = false;
|
||||
},
|
||||
},
|
||||
() => agentId.value || undefined,
|
||||
);
|
||||
const installedSkills = ref<AI.AgentSkillItem[]>([]);
|
||||
const marketResults = ref<AI.AgentSkillSearchItem[]>([]);
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Refresh, Search } from '@element-plus/icons-vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
@@ -181,15 +181,10 @@ const getDefaultMarketSource = (): SkillMarketSource => {
|
||||
const marketSource = ref<SkillMarketSource>(getDefaultMarketSource());
|
||||
const marketSearched = ref(false);
|
||||
const agentId = ref(0);
|
||||
useSearchReset(
|
||||
useSearchPersistence(
|
||||
'ai/agents/agent/config/tabs/skills/openclaw',
|
||||
{ installedKeyword, marketKeyword },
|
||||
{
|
||||
scope: () => agentId.value || undefined,
|
||||
onRevisit: () => {
|
||||
marketResults.value = [];
|
||||
marketSearched.value = false;
|
||||
},
|
||||
},
|
||||
() => agentId.value || undefined,
|
||||
);
|
||||
const skills = ref<AI.AgentSkillItem[]>([]);
|
||||
const marketResults = ref<AI.AgentSkillSearchItem[]>([]);
|
||||
|
||||
@@ -272,8 +272,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { toRef, defineAsyncComponent, onMounted, reactive, ref } from 'vue';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { deleteAgentCheck, pageAgents, resetAgentToken, unbindAgentWebsite, updateAgentRemark } from '@/api/modules/ai';
|
||||
import { checkAppInstalled, installedOp, searchApp, searchAppInstalled } from '@/api/modules/app';
|
||||
@@ -341,6 +341,7 @@ const isActive = ref(false);
|
||||
const isExist = ref(false);
|
||||
const noApp = ref(false);
|
||||
const searchName = ref('');
|
||||
useSearchPersistence('ai/agents/agent/index', { search: searchName });
|
||||
const defaultHttpsPort = ref(443);
|
||||
const openrestyPortLoaded = ref(false);
|
||||
const websiteDomainsMap = ref<Record<number, Website.Domain[]>>({});
|
||||
@@ -400,7 +401,6 @@ const paginationConfig = reactive({
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
|
||||
const checkNoApp = async () => {
|
||||
try {
|
||||
|
||||
@@ -69,8 +69,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { deleteAgentAccount, getAgentProviders, pageAgentAccounts } from '@/api/modules/ai';
|
||||
import { AI } from '@/api/interface/ai';
|
||||
import AddDialog from '@/views/ai/agents/model/add/index.vue';
|
||||
@@ -86,6 +86,7 @@ const items = ref<AI.AgentAccountItem[]>([]);
|
||||
const addRef = ref();
|
||||
const modelPoolRef = ref();
|
||||
const searchName = ref('');
|
||||
useSearchPersistence('ai/agents/model/index', { search: searchName });
|
||||
const apiType = ref('');
|
||||
const apiTypeOptions = ref<string[]>([]);
|
||||
|
||||
@@ -111,7 +112,6 @@ const paginationConfig = reactive({
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
|
||||
const search = async () => {
|
||||
const req: AI.AgentAccountSearch = {
|
||||
|
||||
@@ -186,7 +186,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import AppStatus from '@/components/app-status/index.vue';
|
||||
import AddDialog from '@/views/ai/model/ollama/add/index.vue';
|
||||
import Conn from '@/views/ai/model/ollama/conn/index.vue';
|
||||
@@ -195,7 +195,7 @@ import Terminal from '@/views/ai/model/ollama/terminal/index.vue';
|
||||
import Del from '@/views/ai/model/ollama/del/index.vue';
|
||||
import PortJumpDialog from '@/components/port-jump/index.vue';
|
||||
import CodemirrorDrawer from '@/components/codemirror-pro/drawer.vue';
|
||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
import { App } from '@/api/interface/app';
|
||||
import {
|
||||
@@ -237,7 +237,7 @@ const paginationConfig = reactive({
|
||||
total: 0,
|
||||
});
|
||||
const searchName = ref();
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('ai/model/ollama/index', { search: searchName });
|
||||
const appInstallID = ref(0);
|
||||
|
||||
const opRef = ref();
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { App } from '@/api/interface/app';
|
||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||
import { searchApp, syncApp, syncCutomAppStore, syncLocalApp, getCurrentNodeCustomAppConfig } from '@/api/modules/app';
|
||||
@@ -105,10 +105,7 @@ const req = reactive({
|
||||
resource: 'all',
|
||||
showCurrentArch: false,
|
||||
});
|
||||
useSearchReset(
|
||||
{ search: toRef(req, 'name'), page: toRef(paginationConfig, 'currentPage') },
|
||||
{ onRevisit: () => search(req) },
|
||||
);
|
||||
useSearchPersistence('app-store/apps/index', { search: toRef(req, 'name') });
|
||||
|
||||
const apps = ref<App.AppItem[]>([]);
|
||||
const loading = ref(false);
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import AppCard from '@/views/app-store/installed/app/card.vue';
|
||||
import Backups from '@/components/backup/index.vue';
|
||||
import Uploads from '@/components/upload/index.vue';
|
||||
@@ -211,10 +211,7 @@ const searchReq = reactive({
|
||||
update: false,
|
||||
sync: false,
|
||||
});
|
||||
useSearchReset(
|
||||
{ search: toRef(searchReq, 'name'), page: toRef(paginationConfig, 'currentPage') },
|
||||
{ onRevisit: () => search() },
|
||||
);
|
||||
useSearchPersistence('app-store/installed/index', { search: toRef(searchReq, 'name') });
|
||||
const router = useRouter();
|
||||
const activeName = ref(i18n.global.t('app.installed'));
|
||||
const mode = ref('installed');
|
||||
|
||||
@@ -521,7 +521,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { computed, h, ref } from 'vue';
|
||||
import CodemirrorPro from '@/components/codemirror-pro/index.vue';
|
||||
import ContainerLog from '@/components/log/container/index.vue';
|
||||
@@ -576,7 +576,7 @@ const dialogBackupRef = ref();
|
||||
const uploadRef = ref();
|
||||
|
||||
const searchName = ref('');
|
||||
useSearchReset({ search: searchName }, { onRevisit: () => search() });
|
||||
useSearchPersistence('container/compose/index', { search: searchName });
|
||||
const includeAppStore = ref(localStorage.getItem('includeAppStore') !== 'false');
|
||||
const showType = ref('compose');
|
||||
const containerStats = ref<any[]>([]);
|
||||
|
||||
@@ -474,7 +474,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import PruneDialog from '@/views/container/container/prune/index.vue';
|
||||
import RenameDialog from '@/views/container/container/rename/index.vue';
|
||||
import UpgradeDialog from '@/views/container/container/upgrade/index.vue';
|
||||
@@ -490,7 +490,7 @@ import Uploads from '@/components/upload/index.vue';
|
||||
import DockerStatus from '@/views/container/docker-status/index.vue';
|
||||
import ContainerLogDialog from '@/components/log/container-drawer/index.vue';
|
||||
import Status from '@/components/status/index.vue';
|
||||
import { toRef, computed, reactive, ref } from 'vue';
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import {
|
||||
containerItemStats,
|
||||
containerListStats,
|
||||
@@ -536,7 +536,7 @@ const paginationConfig = reactive({
|
||||
order: 'ascending',
|
||||
});
|
||||
const searchName = ref();
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('container/container/index', { search: searchName });
|
||||
const dialogUpgradeRef = ref();
|
||||
const dialogCommitRef = ref();
|
||||
const dialogPortJumpRef = ref();
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { toRef, reactive, ref, computed } from 'vue';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
import { newUUID } from '@/utils/id';
|
||||
@@ -200,10 +200,7 @@ const paginationConfig = reactive({
|
||||
orderBy: 'createdAt',
|
||||
order: 'null',
|
||||
});
|
||||
useSearchReset(
|
||||
{ search: toRef(paginationConfig, 'name'), page: toRef(paginationConfig, 'currentPage') },
|
||||
{ onRevisit: () => search() },
|
||||
);
|
||||
useSearchPersistence('container/image/index', { search: toRef(paginationConfig, 'name') });
|
||||
const columns = ref([]);
|
||||
|
||||
const isActive = ref(false);
|
||||
|
||||
@@ -95,10 +95,10 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import CreateDialog from '@/views/container/network/create/index.vue';
|
||||
import DetailDrawer from '@/views/container/network/detail/index.vue';
|
||||
import { toRef, reactive, ref } from 'vue';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
import { newUUID } from '@/utils/id';
|
||||
import { deleteNetwork, searchNetwork, inspect, containerPrune } from '@/api/modules/container';
|
||||
@@ -121,7 +121,7 @@ const paginationConfig = reactive({
|
||||
total: 0,
|
||||
});
|
||||
const searchName = ref();
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('container/network/index', { search: searchName });
|
||||
|
||||
const opRef = ref();
|
||||
const isActive = ref(false);
|
||||
|
||||
@@ -59,9 +59,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import OperatorDialog from '@/views/container/repo/operator/index.vue';
|
||||
import { toRef, reactive, ref } from 'vue';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
import { Container } from '@/api/interface/container';
|
||||
import { checkRepoStatus, deleteImageRepo, searchImageRepo } from '@/api/modules/container';
|
||||
@@ -78,7 +78,7 @@ const paginationConfig = reactive({
|
||||
total: 0,
|
||||
});
|
||||
const searchName = ref();
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('container/repo/index', { search: searchName });
|
||||
|
||||
const opRef = ref();
|
||||
const confirmDialog = ref();
|
||||
|
||||
@@ -81,8 +81,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { toRef, reactive, ref } from 'vue';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { dateFormat, getCurrentDateFormatted } from '@/utils/date';
|
||||
import { downloadWithContent } from '@/utils/file';
|
||||
import { Container } from '@/api/interface/container';
|
||||
@@ -111,7 +111,7 @@ const paginationConfig = reactive({
|
||||
total: 0,
|
||||
});
|
||||
const searchName = ref();
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('container/template/index', { search: searchName });
|
||||
|
||||
const search = async () => {
|
||||
if (!isActive.value || !isExist.value) {
|
||||
|
||||
@@ -124,11 +124,11 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import CreateDialog from '@/views/container/volume/create/index.vue';
|
||||
import CodemirrorDrawer from '@/components/codemirror-pro/drawer.vue';
|
||||
import DockerStatus from '@/views/container/docker-status/index.vue';
|
||||
import { toRef, reactive, ref } from 'vue';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
import { newUUID } from '@/utils/id';
|
||||
import { deleteVolume, searchVolume, inspect, containerPrune } from '@/api/modules/container';
|
||||
@@ -158,7 +158,7 @@ const paginationConfig = reactive({
|
||||
total: 0,
|
||||
});
|
||||
const searchName = ref();
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('container/volume/index', { search: searchName });
|
||||
const isActive = ref(false);
|
||||
const isExist = ref(false);
|
||||
|
||||
|
||||
@@ -272,11 +272,11 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import Records from '@/views/cronjob/cronjob/record/index.vue';
|
||||
import Backups from '@/views/cronjob/cronjob/backup/index.vue';
|
||||
import Import from '@/views/cronjob/cronjob/import/index.vue';
|
||||
import { toRef, onMounted, ref, toRefs } from 'vue';
|
||||
import { onMounted, ref, toRefs } from 'vue';
|
||||
import {
|
||||
deleteCronjob,
|
||||
editCronjobGroup,
|
||||
@@ -332,7 +332,7 @@ const pageState = usePageState(() => ({
|
||||
}));
|
||||
const paginationConfig = pageState.paginationConfig;
|
||||
const { defaultGroupID, searchName, searchGroupID } = toRefs(pageState);
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('cronjob/cronjob/index', { search: searchName });
|
||||
|
||||
const search = async (column?: any) => {
|
||||
if (column) {
|
||||
|
||||
@@ -132,7 +132,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { dateFormat, getCurrentDateFormatted } from '@/utils/date';
|
||||
import { deepCopy } from '@/utils/misc';
|
||||
import { newUUID } from '@/utils/id';
|
||||
@@ -141,7 +141,7 @@ import TaskLog from '@/components/log/task/index.vue';
|
||||
import OperateDialog from '@/views/cronjob/library/operate/index.vue';
|
||||
import TerminalDialog from '@/views/cronjob/library/run/index.vue';
|
||||
import { deleteScript, searchScript, syncScript } from '@/api/modules/cronjob';
|
||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { Cronjob } from '@/api/interface/cronjob';
|
||||
import i18n from '@/lang';
|
||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
@@ -170,7 +170,7 @@ const paginationConfig = reactive({
|
||||
total: 0,
|
||||
});
|
||||
const searchInfo = ref<string>('');
|
||||
useSearchReset({ search: searchInfo, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('cronjob/library/index', { search: searchInfo });
|
||||
const group = ref<string>('');
|
||||
const groupOptions = ref();
|
||||
|
||||
|
||||
@@ -323,8 +323,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { toRef, nextTick, onMounted, reactive, ref } from 'vue';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { nextTick, onMounted, reactive, ref } from 'vue';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
import { getRandomStr } from '@/utils/id';
|
||||
import { Position } from '@element-plus/icons-vue';
|
||||
@@ -366,6 +366,7 @@ const loading = ref(false);
|
||||
const maskShow = ref(true);
|
||||
const submitLoading = ref(false);
|
||||
const searchName = ref('');
|
||||
useSearchPersistence('database/mongodb/index', { search: searchName });
|
||||
const createVisible = ref(false);
|
||||
const appStatusRef = ref();
|
||||
const bindRef = ref();
|
||||
@@ -399,7 +400,6 @@ const paginationConfig = reactive({
|
||||
orderBy: 'createdAt',
|
||||
order: 'null',
|
||||
});
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
@@ -75,9 +75,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { deleteCheckDatabase, searchDatabases } from '@/api/modules/database';
|
||||
import AppResources from '@/views/database/postgresql/check/index.vue';
|
||||
import OperateDialog from '@/views/database/mongodb/remote/operate/index.vue';
|
||||
@@ -101,7 +101,7 @@ const paginationConfig = reactive({
|
||||
order: 'null',
|
||||
});
|
||||
const searchName = ref();
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('database/mongodb/remote/index', { search: searchName });
|
||||
|
||||
const search = async (column?: any) => {
|
||||
paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;
|
||||
|
||||
@@ -226,7 +226,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import BindDialog from '@/views/database/mysql/bind/index.vue';
|
||||
import UserDialog from '@/views/database/mysql/user/index.vue';
|
||||
import OperateDialog from '@/views/database/mysql/create/index.vue';
|
||||
@@ -241,7 +241,7 @@ import PortJumpDialog from '@/components/port-jump/index.vue';
|
||||
import Tooltip from '@/components/tooltip/index.vue';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import {
|
||||
deleteCheckMysqlDB,
|
||||
listDatabases,
|
||||
@@ -301,7 +301,7 @@ const paginationConfig = reactive({
|
||||
order: 'null',
|
||||
});
|
||||
const searchName = ref();
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('database/mysql/index', { search: searchName });
|
||||
|
||||
const mysqlContainer = ref();
|
||||
const mysqlStatus = ref();
|
||||
|
||||
@@ -74,9 +74,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { deleteCheckDatabase, searchDatabases } from '@/api/modules/database';
|
||||
import AppResources from '@/views/database/mysql/check/index.vue';
|
||||
import OperateDialog from '@/views/database/mysql/remote/operate/index.vue';
|
||||
@@ -100,7 +100,7 @@ const paginationConfig = reactive({
|
||||
order: 'null',
|
||||
});
|
||||
const searchName = ref();
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('database/mysql/remote/index', { search: searchName });
|
||||
|
||||
const search = async (column?: any) => {
|
||||
paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;
|
||||
|
||||
@@ -241,7 +241,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import BindDialog from '@/views/database/postgresql/bind/index.vue';
|
||||
import OperateDialog from '@/views/database/postgresql/create/index.vue';
|
||||
import DeleteDialog from '@/views/database/postgresql/delete/index.vue';
|
||||
@@ -256,7 +256,7 @@ import UploadDialog from '@/components/upload/index.vue';
|
||||
import PortJumpDialog from '@/components/port-jump/index.vue';
|
||||
import Tooltip from '@/components/tooltip/index.vue';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import {
|
||||
deleteCheckPostgresqlDB,
|
||||
listDatabases,
|
||||
@@ -313,7 +313,7 @@ const paginationConfig = reactive({
|
||||
order: 'null',
|
||||
});
|
||||
const searchName = ref();
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('database/postgresql/index', { search: searchName });
|
||||
|
||||
const postgresqlContainer = ref();
|
||||
const postgresqlStatus = ref();
|
||||
|
||||
@@ -74,9 +74,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { deleteCheckDatabase, searchDatabases } from '@/api/modules/database';
|
||||
import AppResources from '@/views/database/postgresql/check/index.vue';
|
||||
import OperateDialog from '@/views/database/postgresql/remote/operate/index.vue';
|
||||
@@ -100,7 +100,7 @@ const paginationConfig = reactive({
|
||||
order: 'null',
|
||||
});
|
||||
const searchName = ref();
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('database/postgresql/remote/index', { search: searchName });
|
||||
|
||||
const search = async (column?: any) => {
|
||||
paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -73,9 +73,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { deleteCheckDatabase, searchDatabases } from '@/api/modules/database';
|
||||
import AppResources from '@/views/database/redis/check/index.vue';
|
||||
import OperateDialog from '@/views/database/redis/remote/operate/index.vue';
|
||||
@@ -99,7 +99,7 @@ const paginationConfig = reactive({
|
||||
order: 'null',
|
||||
});
|
||||
const searchName = ref();
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('database/redis/remote/index', { search: searchName });
|
||||
|
||||
const search = async (column?: any) => {
|
||||
paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;
|
||||
|
||||
@@ -739,7 +739,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { toRef, computed, nextTick, onBeforeUnmount, onMounted, reactive, ref } from 'vue';
|
||||
import {
|
||||
addFavorite,
|
||||
@@ -853,6 +853,7 @@ const initData = () => ({
|
||||
sortOrder: 'ascending',
|
||||
});
|
||||
let req = reactive(initData());
|
||||
useSearchPersistence('host/file-management/index', { search: toRef(req, 'search') });
|
||||
let loading = ref(false);
|
||||
const paths = ref<FilePaths[]>([]);
|
||||
const breadcrumbVisiblePaths = ref<FilePaths[]>([]);
|
||||
@@ -985,10 +986,6 @@ const paginationConfig = reactive({
|
||||
pageSize: Number(localStorage.getItem('file-page-size')) || 100,
|
||||
total: 0,
|
||||
});
|
||||
useSearchReset(
|
||||
{ search: toRef(req, 'search'), page: toRef(paginationConfig, 'currentPage') },
|
||||
{ onRevisit: () => search() },
|
||||
);
|
||||
|
||||
const btnWrapperRefs = ref<Record<string, any>>({});
|
||||
|
||||
@@ -2854,6 +2851,36 @@ onBeforeUnmount(() => {
|
||||
border-bottom: 1px solid var(--el-border-color-light) !important;
|
||||
}
|
||||
|
||||
/* Keep the closable tab width stable so switching tabs does not reflow the tab bar */
|
||||
:deep(.file-tabs .el-tabs__header .el-tabs__item.is-closable) {
|
||||
padding: 0 20px;
|
||||
|
||||
.is-icon-close {
|
||||
width: 14px;
|
||||
margin-left: 5px;
|
||||
right: 0;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity var(--el-transition-duration);
|
||||
}
|
||||
|
||||
&.is-active,
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
.is-icon-close {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: none), (pointer: coarse) {
|
||||
.is-icon-close {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.file-tabs .el-tabs--card > .el-tabs__header .el-tabs__item.is-active) {
|
||||
border-bottom-width: 1px !important;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
:closable="false"
|
||||
:title="$t('firewall.importLimit', [FIREWALL_BATCH_LIMIT, FIREWALL_IMPORT_MAX_SIZE / 1024])"
|
||||
/>
|
||||
<el-alert v-if="submitError" class="mb-3" type="error" :closable="false" :title="submitError" />
|
||||
<div class="import-file-bar mt-3">
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
@@ -100,7 +99,6 @@ const policies = ref<Firewall.DockerGuardPolicy[]>([]);
|
||||
const selects = ref(new Set<Firewall.DockerGuardPolicy>());
|
||||
const uploadRef = ref();
|
||||
const uploaderFiles = ref<UploadFile[]>([]);
|
||||
const submitError = ref('');
|
||||
const displaySources = (policy: Firewall.DockerGuardPolicy) => formatHostAddressList(policy.sources, policy.family);
|
||||
|
||||
const fileOnChange = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
|
||||
@@ -109,7 +107,6 @@ const fileOnChange = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
|
||||
|
||||
policies.value = [];
|
||||
selects.value = new Set();
|
||||
submitError.value = '';
|
||||
uploaderFiles.value = uploadFiles;
|
||||
if (uploadFile.raw.size > FIREWALL_IMPORT_MAX_SIZE) {
|
||||
uploadRef.value?.clearFiles();
|
||||
@@ -162,7 +159,6 @@ const onImport = async () => {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
submitError.value = '';
|
||||
try {
|
||||
const result = (
|
||||
await upsertDockerPortGuardPolicies({
|
||||
@@ -171,16 +167,17 @@ const onImport = async () => {
|
||||
})
|
||||
).data;
|
||||
if (!result.taskID || !result.queued) {
|
||||
submitError.value = i18n.global.t('commons.msg.operationFailed');
|
||||
MsgError(i18n.global.t('commons.msg.operationFailed'));
|
||||
return;
|
||||
}
|
||||
visible.value = false;
|
||||
emit('created', result.taskID);
|
||||
} catch (error) {
|
||||
submitError.value =
|
||||
MsgError(
|
||||
(isAxiosError(error) && error.response?.data?.message) ||
|
||||
(error && getErrorMessage(error)) ||
|
||||
i18n.global.t('commons.res.commonError');
|
||||
(error && getErrorMessage(error)) ||
|
||||
i18n.global.t('commons.res.commonError'),
|
||||
);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -200,7 +197,6 @@ const acceptParams = () => {
|
||||
policies.value = [];
|
||||
selects.value = new Set();
|
||||
uploaderFiles.value = [];
|
||||
submitError.value = '';
|
||||
uploadRef.value?.clearFiles();
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
v-loading="loading"
|
||||
>
|
||||
<template #prompt>
|
||||
<el-alert v-if="data.base.message" type="warning" :closable="false" :title="data.base.message" />
|
||||
<el-alert v-else type="info" :closable="false" :title="$t('firewall.dockerGuardHelper')" />
|
||||
<el-alert type="info" :closable="false" :title="$t('firewall.dockerGuardHelper')" />
|
||||
</template>
|
||||
<template #leftToolBar>
|
||||
<el-button plain @click="orphanDrawerVisible = true">
|
||||
@@ -220,7 +219,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { computed, reactive, ref, watch } from 'vue';
|
||||
import FireRouter from '@/views/host/firewall/index.vue';
|
||||
import DockerGuardStatus from '@/views/host/firewall/docker/status/index.vue';
|
||||
@@ -264,14 +263,7 @@ const openRuleTask = (taskID: string) => taskLogRef.value?.openWithTaskID(taskID
|
||||
const orphanDrawerVisible = ref(false);
|
||||
const orphanSearchName = ref('');
|
||||
const searchName = ref('');
|
||||
useSearchReset(
|
||||
{ search: searchName },
|
||||
{
|
||||
onRevisit: () => {
|
||||
paginationConfig.currentPage = 1;
|
||||
},
|
||||
},
|
||||
);
|
||||
useSearchPersistence('host/firewall/docker/index', { search: searchName });
|
||||
const selects = ref<Firewall.DockerGuardContainer[]>([]);
|
||||
const orphanSelects = ref<Firewall.DockerGuardEndpoint[]>([]);
|
||||
const data = reactive<Firewall.DockerGuardList>({
|
||||
@@ -462,6 +454,7 @@ const search = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
Object.assign(data, (await loadDockerPortGuard()).data);
|
||||
if (data.base.message) MsgError(data.base.message);
|
||||
selects.value = [];
|
||||
orphanSelects.value = [];
|
||||
} finally {
|
||||
|
||||
@@ -25,9 +25,6 @@
|
||||
<span v-else>{{ $t('firewall.basicStatus') }}</span>
|
||||
</el-card>
|
||||
<LayoutContent :title="$t('firewall.forwardRule', 2)" :class="{ mask: !isInit || !isBind }">
|
||||
<template v-if="fireStatusRef?.forwardDropFamilies" #prompt>
|
||||
<el-alert type="warning" :closable="false" :title="$t('firewall.forwardPolicyDropWarning')" />
|
||||
</template>
|
||||
<template #leftToolBar>
|
||||
<el-button v-permission v-node-admin type="primary" @click="onOpenDialog('create')">
|
||||
{{ $t('commons.button.create') }}
|
||||
@@ -120,14 +117,14 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import OperateDialog from './operate/index.vue';
|
||||
import ImportDialog from './import/index.vue';
|
||||
import FireRouter from '@/views/host/firewall/index.vue';
|
||||
import FireStatus from '@/views/host/firewall/status/index.vue';
|
||||
import RuleReset from '@/views/host/firewall/components/rule-reset.vue';
|
||||
import TaskLog from '@/components/log/task/index.vue';
|
||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { resetFirewallRules, operateForwardRule, searchForwardRule } from '@/api/modules/firewall';
|
||||
import { Firewall } from '@/api/interface/firewall';
|
||||
import { buildForwardRuleExport } from './transfer';
|
||||
@@ -141,6 +138,7 @@ import { ElMessageBox } from 'element-plus';
|
||||
const loading = ref();
|
||||
const selects = ref<any>([]);
|
||||
const searchName = ref();
|
||||
useSearchPersistence('host/firewall/forward/index', { search: searchName });
|
||||
|
||||
const isInit = ref(false);
|
||||
const isBind = ref(false);
|
||||
@@ -191,7 +189,6 @@ const paginationConfig = reactive({
|
||||
pageSize: Number(localStorage.getItem('firewall-forward-page-size')) || 20,
|
||||
total: 0,
|
||||
});
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
|
||||
const search = async () => {
|
||||
if (!isInit.value || !isBind.value || fireName.value === '-') {
|
||||
|
||||
@@ -302,7 +302,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { Firewall } from '@/api/interface/firewall';
|
||||
import type { FuTableOperationButton } from '@/components/table/shared';
|
||||
import { buildHostRuleExport } from './transfer';
|
||||
@@ -330,7 +330,7 @@ import RuleReset from '@/views/host/firewall/components/rule-reset.vue';
|
||||
import TaskLog from '@/components/log/task/index.vue';
|
||||
import DockerRestart from '@/components/docker-proxy/docker-restart.vue';
|
||||
import { loadDockerStatus } from '@/api/modules/container';
|
||||
import { toRef, computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue';
|
||||
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue';
|
||||
import { useElementSize } from '@vueuse/core';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { Expand, Loading, Lock, Refresh } from '@element-plus/icons-vue';
|
||||
@@ -405,6 +405,7 @@ const showFirewallUnavailablePrompt = computed(
|
||||
const firewallVersion = ref('');
|
||||
const selectedRuleFilters = ref<RuleFilter[]>(loadCachedFilterValues(ruleFilterStorageKey, ruleFilterOptions, []));
|
||||
const searchName = ref('');
|
||||
useSearchPersistence('host/firewall/rule/index', { search: searchName });
|
||||
const inventoryItems = ref<Firewall.InventoryItem[]>([]);
|
||||
const positionRanges = ref<Partial<Record<Firewall.Family, Firewall.PositionRange>>>({});
|
||||
const inventoryTotal = ref(0);
|
||||
@@ -432,7 +433,6 @@ const paginationConfig = reactive({
|
||||
pageSize: Number(localStorage.getItem('firewall-rule-page-size')) || 20,
|
||||
total: 0,
|
||||
});
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
|
||||
const providerScopes = (): Firewall.Scope[] => {
|
||||
if (provider.value === 'iptables' || provider.value === 'nftables') {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</template>
|
||||
</i18n-t>
|
||||
</NoSuchService>
|
||||
<div class="app-status card-interval" v-else-if="baseInfo.isExist && !baseInfo.message">
|
||||
<div class="app-status card-interval" v-else-if="baseInfo.isExist">
|
||||
<el-card>
|
||||
<div class="flex w-full flex-col gap-4 md:flex-row">
|
||||
<div class="flex flex-wrap gap-4 ml-3">
|
||||
@@ -145,16 +145,6 @@
|
||||
:title="$t('firewall.directBackendConflictWarning', [backendName, baseInfo.conflictBackend])"
|
||||
/>
|
||||
</div>
|
||||
<el-alert v-else-if="baseInfo.isExist" class="card-interval" type="error" show-icon :closable="false">
|
||||
<template #title>
|
||||
<div class="flex items-center gap-2">
|
||||
<span>{{ baseInfo.message }}</span>
|
||||
<el-button v-permission v-node-admin type="primary" link @click="goToFirewallSetting">
|
||||
{{ $t('commons.button.set') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
<DockerRestart
|
||||
ref="dockerRef"
|
||||
v-model:withDockerRestart="withDockerRestart"
|
||||
@@ -210,7 +200,6 @@ const baseInfo = ref<Firewall.FirewallBase>({
|
||||
conflictBackend: '',
|
||||
version: '',
|
||||
pingStatus: '',
|
||||
message: '',
|
||||
reason: '',
|
||||
ipv4: { available: false, initialized: false, bound: false },
|
||||
ipv6: { available: false, initialized: false, bound: false },
|
||||
@@ -243,13 +232,6 @@ const familyStatuses = computed(() =>
|
||||
).filter((item) => item.family === 'IPv4' || baseInfo.value.ipv6Enabled !== false),
|
||||
);
|
||||
const availableFamilies = computed(() => familyStatuses.value.filter((item) => item.status.available));
|
||||
const forwardDropFamilies = computed(() => {
|
||||
if (backendUnavailable.value || !baseInfo.value.isExist || baseInfo.value.message) return '';
|
||||
return familyStatuses.value
|
||||
.filter((item) => item.status.forwardPolicy === 'DROP')
|
||||
.map((item) => item.family)
|
||||
.join(', ');
|
||||
});
|
||||
const anyFamilyInitialized = computed(() => availableFamilies.value.some((item) => item.status.initialized));
|
||||
const allAvailableFamiliesInitialized = computed(
|
||||
() => availableFamilies.value.length > 0 && availableFamilies.value.every((item) => item.status.initialized),
|
||||
@@ -297,7 +279,7 @@ const familyActions = computed(() =>
|
||||
const familyIssueText = (issue: FamilyIssue) => {
|
||||
if (!issue.available) return i18n.global.t('firewall.familyUnsupported', [issue.family]);
|
||||
if (issue.reason === 'ipv6_ra_required') {
|
||||
return i18n.global.t('firewall.ipv6RARisk', [issue.raInterfaces?.join(', ') || '-']);
|
||||
return i18n.global.t('firewall.ipv6RARisk');
|
||||
}
|
||||
if (issue.reason === 'ipv6_ra_check_failed') return i18n.global.t('firewall.ipv6RACheckFailed');
|
||||
if (issue.partial) return i18n.global.t('firewall.familyIncomplete', [issue.family]);
|
||||
@@ -481,7 +463,6 @@ onBeforeUnmount(() => {
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
forwardDropFamilies,
|
||||
acceptParams,
|
||||
openInitialization: onInit,
|
||||
});
|
||||
|
||||
@@ -93,11 +93,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
|
||||
import { dateFormatServerTimezone } from '@/utils/date';
|
||||
import { downloadFile } from '@/utils/file';
|
||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { cleanSSHLogs, exportSSHLogs, loadSSHLogs } from '@/api/modules/host';
|
||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
import i18n from '@/lang';
|
||||
@@ -120,7 +120,7 @@ const exportConfig = reactive({
|
||||
status: 'All',
|
||||
});
|
||||
const searchInfo = ref();
|
||||
useSearchReset({ search: searchInfo, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('host/ssh/log/log', { search: searchInfo });
|
||||
const searchStatus = ref('All');
|
||||
|
||||
const search = async () => {
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import FireRouter from '@/views/host/ssh/index.vue';
|
||||
import { toRef, ref, onMounted, onUnmounted, reactive } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
@@ -38,7 +38,7 @@ const sshSearch = reactive({
|
||||
type: 'ssh',
|
||||
loginUser: '',
|
||||
});
|
||||
useSearchReset({ search: toRef(sshSearch, 'loginUser') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('host/ssh/session/index', { search: toRef(sshSearch, 'loginUser') });
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { onMounted, onUnmounted, reactive, ref, watch } from 'vue';
|
||||
import { Log } from '@/api/interface/log';
|
||||
import { getSystemLogStatus, listRunningServices, readSystemLogs } from '@/api/modules/log';
|
||||
@@ -114,7 +114,7 @@ const logs = ref<Log.SystemLogItem[]>([]);
|
||||
const loading = ref(false);
|
||||
const watching = ref(false);
|
||||
const keyword = ref('');
|
||||
useSearchReset({ keyword }, { onRevisit: () => scheduleFilterSearch() });
|
||||
useSearchPersistence('log/host-system/index', { keyword });
|
||||
const priority = ref('');
|
||||
const service = ref('');
|
||||
const services = ref<string[]>([]);
|
||||
@@ -274,13 +274,11 @@ onUnmounted(() => {
|
||||
if (filterTimer) clearTimeout(filterTimer);
|
||||
});
|
||||
|
||||
const scheduleFilterSearch = () => {
|
||||
watch([keyword, priority, service], () => {
|
||||
requestID++;
|
||||
if (filterTimer) clearTimeout(filterTimer);
|
||||
filterTimer = setTimeout(resetAndLoadLogs, 300);
|
||||
};
|
||||
|
||||
watch([keyword, priority, service], scheduleFilterSearch);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -45,12 +45,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
|
||||
import LogRouter from '@/views/log/router/index.vue';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
import { cleanLogs, getLoginLogs } from '@/api/modules/log';
|
||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
@@ -67,7 +67,7 @@ const paginationConfig = reactive({
|
||||
total: 0,
|
||||
});
|
||||
const searchInfo = ref<string>('');
|
||||
useSearchReset({ search: searchInfo, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('log/login/index', { search: searchInfo });
|
||||
const searchStatus = ref<string>('');
|
||||
|
||||
const search = async () => {
|
||||
|
||||
@@ -125,12 +125,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
|
||||
import LogRouter from '@/views/log/router/index.vue';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
import { cleanLogs, getOperationLogs } from '@/api/modules/log';
|
||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
@@ -146,7 +146,7 @@ const paginationConfig = reactive({
|
||||
total: 0,
|
||||
});
|
||||
const searchName = ref<string>('');
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('log/operation/index', { search: searchName });
|
||||
const searchGroup = ref<string>('');
|
||||
const searchStatus = ref<string>('');
|
||||
const searchNode = ref<string>('');
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
import { toRef, onMounted, ref } from 'vue';
|
||||
import { searchBackup, deleteBackup, refreshToken } from '@/api/modules/backup';
|
||||
@@ -143,10 +143,7 @@ const paginationConfig = reactive({
|
||||
type: '',
|
||||
name: '',
|
||||
});
|
||||
useSearchReset(
|
||||
{ search: toRef(paginationConfig, 'name'), page: toRef(paginationConfig, 'currentPage') },
|
||||
{ onRevisit: () => search() },
|
||||
);
|
||||
useSearchPersistence('setting/backup-account/index', { search: toRef(paginationConfig, 'name') });
|
||||
const opRef = ref();
|
||||
const dialogRef = ref();
|
||||
const detailRef = ref();
|
||||
|
||||
@@ -225,7 +225,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import {
|
||||
searchSnapshotPage,
|
||||
snapshotDelete,
|
||||
@@ -233,7 +233,7 @@ import {
|
||||
snapshotRollback,
|
||||
updateSnapshotDescription,
|
||||
} from '@/api/modules/setting';
|
||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { computeSize } from '@/utils/size';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
import { newUUID } from '@/utils/id';
|
||||
@@ -261,7 +261,7 @@ const paginationConfig = reactive({
|
||||
order: 'null',
|
||||
});
|
||||
const searchName = ref();
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('setting/snapshot/index', { search: searchName });
|
||||
|
||||
const opRef = ref();
|
||||
|
||||
|
||||
@@ -100,13 +100,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { Command } from '@/api/interface/command';
|
||||
import GroupDialog from '@/components/group/index.vue';
|
||||
import OperateDialog from '@/views/terminal/command/operate/index.vue';
|
||||
import ImportDialog from '@/views/terminal/command/import/index.vue';
|
||||
import { editCommand, deleteCommand, getCommandPage, exportCommands } from '@/api/modules/command';
|
||||
import { toRef, reactive, ref } from 'vue';
|
||||
import { reactive, ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { getGroupList } from '@/api/modules/group';
|
||||
@@ -124,7 +124,7 @@ const paginationConfig = reactive({
|
||||
order: 'ascending',
|
||||
});
|
||||
const info = ref();
|
||||
useSearchReset({ search: info, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('terminal/command/index', { search: info });
|
||||
const group = ref<string>('');
|
||||
const dialogRef = ref();
|
||||
const opRef = ref();
|
||||
|
||||
@@ -79,12 +79,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import GroupDialog from '@/components/agent-group/index.vue';
|
||||
import OperateDialog from '@/views/terminal/host/operate/index.vue';
|
||||
import { deleteHost, editHostGroup, searchHosts } from '@/api/modules/terminal';
|
||||
import { getAgentGroupList } from '@/api/modules/group';
|
||||
import { toRef, reactive, ref } from 'vue';
|
||||
import { reactive, ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
import { Host } from '@/api/interface/host';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
@@ -100,7 +100,7 @@ const paginationConfig = reactive({
|
||||
total: 0,
|
||||
});
|
||||
const info = ref();
|
||||
useSearchReset({ search: info, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('terminal/host/index', { search: info });
|
||||
const group = ref<string>('');
|
||||
|
||||
const opRef = ref();
|
||||
|
||||
@@ -190,8 +190,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { deleteClam, handleClamScan, searchClam, updateClam, updateClamStatus } from '@/api/modules/toolbox';
|
||||
@@ -218,7 +218,7 @@ const paginationConfig = reactive({
|
||||
order: 'null',
|
||||
});
|
||||
const searchName = ref();
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('toolbox/clam/index', { search: searchName });
|
||||
|
||||
const opRef = ref();
|
||||
const dialogRef = ref();
|
||||
|
||||
@@ -174,8 +174,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
import { deleteFtp, searchFtp, updateFtp, syncFtp, operateFtp, getFtpBase } from '@/api/modules/toolbox';
|
||||
@@ -201,7 +201,7 @@ const paginationConfig = reactive({
|
||||
order: 'null',
|
||||
});
|
||||
const searchName = ref();
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
useSearchPersistence('toolbox/ftp/index', { search: searchName });
|
||||
|
||||
const form = reactive({
|
||||
isActive: true,
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { Runtime } from '@/api/interface/runtime';
|
||||
import { GetPHPExtensions, InstallPHPExtension, UnInstallPHPExtension } from '@/api/modules/runtime';
|
||||
import i18n from '@/lang';
|
||||
@@ -49,7 +49,7 @@ const supportExtensions = ref([]);
|
||||
const loading = ref(false);
|
||||
const taskLogRef = ref();
|
||||
const searchName = ref('');
|
||||
useSearchReset({ search: searchName }, { onRevisit: () => searchByName() });
|
||||
useSearchPersistence('website/runtime/php/extension-management/index', { search: searchName });
|
||||
const data = ref([]);
|
||||
|
||||
const handleClose = () => {
|
||||
|
||||
@@ -221,7 +221,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { toRef, defineAsyncComponent, onMounted, reactive, ref } from 'vue';
|
||||
import { deleteSSL, downloadFile, pushSSLToNode, searchSSL, updateSSL } from '@/api/modules/website';
|
||||
import DnsAccount from './dns-account/index.vue';
|
||||
@@ -287,10 +287,7 @@ const req = reactive({
|
||||
orderBy: 'updated_at',
|
||||
order: 'descending',
|
||||
});
|
||||
useSearchReset(
|
||||
{ search: toRef(req, 'domain'), page: toRef(paginationConfig, 'currentPage') },
|
||||
{ onRevisit: () => search() },
|
||||
);
|
||||
useSearchPersistence('website/ssl/index', { search: toRef(req, 'domain') });
|
||||
const pushForm = ref({
|
||||
id: 0,
|
||||
pushNode: true,
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import { Website } from '@/api/interface/website';
|
||||
import { deleteTemplate, searchTemplates } from '@/api/modules/website';
|
||||
import TemplateOperate from '@/views/website/template/operate/index.vue';
|
||||
@@ -67,11 +67,12 @@ import OutputCreate from '@/views/website/template/output/create.vue';
|
||||
import OutputList from '@/views/website/template/output/index.vue';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
import i18n from '@/lang';
|
||||
import { toRef, reactive, ref, onMounted } from 'vue';
|
||||
import { reactive, ref, onMounted } from 'vue';
|
||||
|
||||
const loading = ref(false);
|
||||
const data = ref<Website.Template[]>([]);
|
||||
const searchName = ref('');
|
||||
useSearchPersistence('website/template/index', { search: searchName });
|
||||
const opRef = ref();
|
||||
const operateRef = ref();
|
||||
const outputCreateRef = ref();
|
||||
@@ -90,7 +91,6 @@ const paginationConfig = reactive({
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
});
|
||||
useSearchReset({ search: searchName, page: toRef(paginationConfig, 'currentPage') }, { onRevisit: () => search() });
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
|
||||
@@ -346,7 +346,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSearchReset } from '@/composables/useSearchReset';
|
||||
import { useSearchPersistence } from '@/composables/useSearchPersistence';
|
||||
import Backups from '@/components/backup/index.vue';
|
||||
import UploadDialog from '@/components/upload/index.vue';
|
||||
import DefaultServer from '@/views/website/website/default/index.vue';
|
||||
@@ -451,10 +451,7 @@ const pageState = usePageState(() => ({
|
||||
}));
|
||||
const paginationConfig = pageState.paginationConfig;
|
||||
const req = pageState.req;
|
||||
useSearchReset(
|
||||
{ search: toRef(req, 'name'), page: toRef(paginationConfig, 'currentPage') },
|
||||
{ onRevisit: () => search() },
|
||||
);
|
||||
useSearchPersistence('website/website/index', { search: toRef(req, 'name') });
|
||||
const tableSort = pageState.tableSort;
|
||||
|
||||
const goRouter = async (key: string) => {
|
||||
|
||||
Reference in New Issue
Block a user