mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-20 16:00:51 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1121758335 |
@@ -293,7 +293,7 @@ func (b *BaseApi) PageAlertConfig(c *gin.Context) {
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /alert/config/update [post]
|
||||
// @x-panel-log {"bodyKeys":["displayName"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新告警配置 [displayName]","formatEN":"update alert config [displayName]"}
|
||||
// @x-panel-log {"bodyKeys":["id","displayName"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新告警配置 [id][displayName]","formatEN":"update alert config [id][displayName]"}
|
||||
func (b *BaseApi) UpdateAlertConfig(c *gin.Context) {
|
||||
var req dto.AlertConfigUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -321,15 +321,13 @@ func (b *BaseApi) UpdateAlertConfig(c *gin.Context) {
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /alert/config/status [post]
|
||||
// @x-panel-log {"bodyKeys":["status"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新告警配置状态 [name][status]","formatEN":"update alert config status [name][status]"}
|
||||
// @x-panel-log {"bodyKeys":["id","status"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新告警配置状态 [id][status]","formatEN":"update alert config status [id][status]"}
|
||||
func (b *BaseApi) UpdateAlertConfigStatus(c *gin.Context) {
|
||||
var req dto.AlertConfigStatusUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
name, err := alertService.UpdateAlertConfigStatus(req, loadAuditUser(c))
|
||||
helper.SetOperationLogValues(c, map[string]string{"name": name})
|
||||
if err != nil {
|
||||
if err := alertService.UpdateAlertConfigStatus(req, loadAuditUser(c)); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
@@ -355,14 +353,13 @@ func loadAuditUser(c *gin.Context) string {
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /alert/config/del [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除告警配置 [name]","formatEN":"delete alert config [name]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除告警配置 [id]","formatEN":"delete alert config [id]"}
|
||||
func (b *BaseApi) DeleteAlertConfig(c *gin.Context) {
|
||||
var req dto.DeleteRequest
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
name, err := alertService.DeleteAlertConfig(req.ID)
|
||||
helper.SetOperationLogValues(c, map[string]string{"name": name})
|
||||
err := alertService.DeleteAlertConfig(req.ID)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
|
||||
@@ -3,7 +3,6 @@ package helper
|
||||
import (
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -185,23 +184,3 @@ func GetParamInt32(paramName string, c *gin.Context) (int32, error) {
|
||||
intNum, _ := strconv.Atoi(idParam)
|
||||
return int32(intNum), nil
|
||||
}
|
||||
|
||||
// SetOperationLogValues supplies server-resolved values to Core's operation log.
|
||||
// Call before writing the response. Only pass public audit fields, never credentials.
|
||||
func SetOperationLogValues(c *gin.Context, values map[string]string) {
|
||||
const header = "X-Op-Resolved"
|
||||
resolved := make(map[string]string)
|
||||
if data, err := base64.RawURLEncoding.DecodeString(c.Writer.Header().Get(header)); err == nil {
|
||||
_ = json.Unmarshal(data, &resolved)
|
||||
}
|
||||
if resolved == nil {
|
||||
resolved = make(map[string]string)
|
||||
}
|
||||
for key, value := range values {
|
||||
resolved[key] = value
|
||||
}
|
||||
data, err := json.Marshal(resolved)
|
||||
if err == nil {
|
||||
c.Header(header, base64.RawURLEncoding.EncodeToString(data))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ type FirewallSubsystemStatus struct {
|
||||
}
|
||||
|
||||
type FirewallLifecycleOperation struct {
|
||||
BackupFile string `json:"backupFile,omitempty" validate:"omitempty,max=255"`
|
||||
Operation string `json:"operation" validate:"required,oneof=start stop restart disableBanPing enableBanPing"`
|
||||
WithDockerRestart bool `json:"withDockerRestart"`
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
@@ -145,11 +144,6 @@ type MonitorVLLMCurrent struct {
|
||||
AppInstallID uint `json:"appInstallID" validate:"required"`
|
||||
}
|
||||
|
||||
type MonitorVLLMCurrentData struct {
|
||||
model.MonitorVLLM
|
||||
Metrics map[string]json.RawMessage `json:"metrics,omitempty"`
|
||||
}
|
||||
|
||||
type MonitorVLLMClean struct {
|
||||
AppInstallID uint `json:"appInstallID" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -86,9 +86,7 @@ type MonitorVLLM struct {
|
||||
RawMetrics string `json:"-"`
|
||||
HistogramDeltas string `json:"-"`
|
||||
IntervalSeconds float64 `json:"intervalSeconds" gorm:"-"`
|
||||
SampleIntervalSeconds int `json:"-"`
|
||||
RequestDeltas string `json:"-"`
|
||||
CompletedRequests *float64 `json:"completedRequests" gorm:"-"`
|
||||
QueueTime *float64 `json:"queueTime"`
|
||||
AverageGenerationTokens *float64 `json:"averageGenerationTokens"`
|
||||
PrefixCacheHitRate *float64 `json:"prefixCacheHitRate"`
|
||||
|
||||
@@ -16,8 +16,7 @@ type VLLMMonitorRepo struct{}
|
||||
|
||||
type VLLMHistoryPoint struct {
|
||||
model.MonitorVLLM
|
||||
Bucket int64
|
||||
CompletedRequests *float64
|
||||
Bucket int64
|
||||
}
|
||||
|
||||
func (r *VLLMMonitorRepo) Create(point *model.MonitorVLLM) error {
|
||||
@@ -78,19 +77,12 @@ func (r *VLLMMonitorRepo) History(ctx context.Context, id uint, start, end time.
|
||||
}
|
||||
db = db.Group("bucket").Order("bucket ASC")
|
||||
} else {
|
||||
columns = append([]string{"id", "created_at", "app_install_id", "status", "sample_interval_seconds"}, metrics...)
|
||||
columns = append([]string{"id", "created_at", "app_install_id", "status"}, metrics...)
|
||||
if group == "" || group == "requests" {
|
||||
columns = append(columns, "histogram_deltas", "request_deltas")
|
||||
}
|
||||
db = db.Order("created_at ASC, id ASC")
|
||||
}
|
||||
if group == "" || group == "overview" {
|
||||
count := "CASE WHEN json_valid(request_deltas) THEN CASE WHEN json_extract(request_deltas, '$.seconds') > 0 THEN ROUND(request_throughput * json_extract(request_deltas, '$.seconds')) END END"
|
||||
if seconds > 0 {
|
||||
count = "SUM(" + count + ")"
|
||||
}
|
||||
columns = append(columns, count+" AS completed_requests")
|
||||
}
|
||||
var points []VLLMHistoryPoint
|
||||
err := db.Select(strings.Join(columns, ", ")).Scan(&points).Error
|
||||
return points, err
|
||||
|
||||
+14
-26
@@ -74,8 +74,8 @@ type IAlertService interface {
|
||||
GetAlertConfig(req dto.AlertConfigQuery) ([]model.AlertConfig, error)
|
||||
PageAlertConfig(req dto.AlertConfigPageReq) (int64, []model.AlertConfig, error)
|
||||
UpdateAlertConfig(req dto.AlertConfigUpdate, operator string) error
|
||||
UpdateAlertConfigStatus(req dto.AlertConfigStatusUpdate, operator string) (string, error)
|
||||
DeleteAlertConfig(id uint) (string, error)
|
||||
UpdateAlertConfigStatus(req dto.AlertConfigStatusUpdate, operator string) error
|
||||
DeleteAlertConfig(id uint) error
|
||||
TestAlertConfig(req dto.AlertConfigTest) (bool, error)
|
||||
TestCustomAlertConfig(req dto.AlertConfigTest, lang string) (dto.AlertConfigTestResult, error)
|
||||
}
|
||||
@@ -623,26 +623,25 @@ func (a AlertService) updateCustomAlertConfig(req dto.AlertConfigUpdate, operato
|
||||
})
|
||||
}
|
||||
|
||||
func (a AlertService) UpdateAlertConfigStatus(req dto.AlertConfigStatusUpdate, operator string) (string, error) {
|
||||
func (a AlertService) UpdateAlertConfigStatus(req dto.AlertConfigStatusUpdate, operator string) error {
|
||||
if err := validateAlertConfigStatus(req.Status); err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
config, err := alertRepo.GetConfigById(req.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
name := alertConfigLogName(config.Config, config.Title)
|
||||
if req.Status == constant.AlertEnable {
|
||||
if err := a.validateCommunityAlertConfigType(config.Type); err != nil {
|
||||
return name, err
|
||||
return err
|
||||
}
|
||||
if config.Type == constant.Custom {
|
||||
if _, err := alertwebhook.Resolve(config); err != nil {
|
||||
return name, err
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return name, alertRepo.UpdateAlertConfig(map[string]interface{}{
|
||||
return alertRepo.UpdateAlertConfig(map[string]interface{}{
|
||||
"status": req.Status,
|
||||
"update_user": operator,
|
||||
}, repo.WithByID(req.ID))
|
||||
@@ -814,20 +813,19 @@ func alertConfigSMSPhone(configData string) string {
|
||||
return strings.TrimSpace(cfg.Phone)
|
||||
}
|
||||
|
||||
func (a AlertService) DeleteAlertConfig(id uint) (string, error) {
|
||||
config, err := alertRepo.GetConfigById(id)
|
||||
func (a AlertService) DeleteAlertConfig(id uint) error {
|
||||
_, err := alertRepo.GetConfigById(id)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
name := alertConfigLogName(config.Config, config.Title)
|
||||
usedAlerts, err := alertRepo.List(alertRepo.WithByAlertMethodContainsConfigID(id))
|
||||
if err != nil {
|
||||
return name, err
|
||||
return err
|
||||
}
|
||||
if len(usedAlerts) > 0 {
|
||||
return name, buserr.New("ErrAlertConfigInUse")
|
||||
return buserr.New("ErrAlertConfigInUse")
|
||||
}
|
||||
return name, alertRepo.DeleteAlertConfig(repo.WithByID(id))
|
||||
return alertRepo.DeleteAlertConfig(repo.WithByID(id))
|
||||
}
|
||||
|
||||
func (a AlertService) TestAlertConfig(req dto.AlertConfigTest) (bool, error) {
|
||||
@@ -1045,13 +1043,3 @@ func (a AlertService) validateCronJobAlertChannels(alertType, advanced, method s
|
||||
_, err = a.validateAlertMethodReferences(method)
|
||||
return err
|
||||
}
|
||||
|
||||
func alertConfigLogName(config, title string) string {
|
||||
var value struct {
|
||||
DisplayName string `json:"displayName"`
|
||||
}
|
||||
if json.Unmarshal([]byte(config), &value) == nil && strings.TrimSpace(value.DisplayName) != "" {
|
||||
return strings.TrimSpace(value.DisplayName)
|
||||
}
|
||||
return title
|
||||
}
|
||||
|
||||
@@ -131,9 +131,6 @@ func (s *FirewallService) LoadBaseInfo(chainGroup string) (dto.FirewallSubsystem
|
||||
|
||||
func (s *FirewallService) QueueFirewallOperation(request dto.FirewallLifecycleOperation) (dto.FirewallLifecycleOperationResponse, error) {
|
||||
response := dto.FirewallLifecycleOperationResponse{}
|
||||
if request.BackupFile != "" && request.Operation != string(lifecycle.OperationStart) {
|
||||
return response, fmt.Errorf("backup restoration is only supported when starting firewalld or ufw")
|
||||
}
|
||||
if request.Operation == "disableBanPing" || request.Operation == "enableBanPing" {
|
||||
return response, s.OperateFirewall(request)
|
||||
}
|
||||
@@ -158,19 +155,6 @@ func (s *FirewallService) QueueFirewallOperation(request dto.FirewallLifecycleOp
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
var restoreRequest dto.FirewallRuleCreate
|
||||
if request.BackupFile != "" {
|
||||
if client.Name() != lifecycle.ProviderFirewalld && client.Name() != lifecycle.ProviderUFW {
|
||||
return response, fmt.Errorf("backup restoration is only supported when starting firewalld or ufw")
|
||||
}
|
||||
restoreRequest, err = prepareFirewallRuleCreate(dto.FirewallRuleCreate{BackupFile: request.BackupFile, Initialize: true})
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
if err := validateFirewallCreateBatch(restoreRequest, filter.Provider(client.Name())); err != nil {
|
||||
return response, err
|
||||
}
|
||||
}
|
||||
if (client.Name() != lifecycle.ProviderFirewalld && client.Name() != lifecycle.ProviderUFW) ||
|
||||
(request.Operation != string(lifecycle.OperationStart) && request.Operation != string(lifecycle.OperationStop) &&
|
||||
request.Operation != string(lifecycle.OperationRestart)) {
|
||||
@@ -189,26 +173,7 @@ func (s *FirewallService) QueueFirewallOperation(request dto.FirewallLifecycleOp
|
||||
return response, err
|
||||
}
|
||||
taskItem.AddSubTaskWithOps(name, func(t *task.Task) error {
|
||||
if request.BackupFile != "" {
|
||||
if err := s.checkSelectedProvider(t.TaskCtx, filter.Provider(client.Name())); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
prepareStart := func(lifecycle.Client) error {
|
||||
if request.BackupFile != "" {
|
||||
return runFirewallLifecycleAction(t, i18n.GetMsgByKey("FirewallCreateRulesStep"), func() error {
|
||||
return s.runFirewallCreateRulesTask(t, restoreRequest, filter.Provider(client.Name()))
|
||||
})
|
||||
}
|
||||
return runFirewallLifecycleAction(t, i18n.GetMsgByKey("FirewallSyncWhitelistStep"), func() error {
|
||||
err := s.SyncPortWhitelist(t.TaskCtx)
|
||||
if err == nil && client.Name() == lifecycle.ProviderFirewalld {
|
||||
err = lifecycleproviders.RemoveFirewalldSSHService()
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
return s.runFirewallLifecycleTask(t, client, request, prepareStart)
|
||||
return s.runFirewallLifecycleTask(t, client, request, true)
|
||||
}, nil, 0, 0)
|
||||
if err := taskRepo.Save(context.Background(), taskItem.Task); err != nil {
|
||||
closeUnstartedFirewallTask(taskItem)
|
||||
@@ -589,9 +554,29 @@ func (s *FirewallService) LoadFirewallNativeDetail(ctx context.Context, request
|
||||
}
|
||||
|
||||
func (s *FirewallService) Create(ctx context.Context, request dto.FirewallRuleCreate) (dto.FirewallRuleCreateResponse, error) {
|
||||
request, err := prepareFirewallRuleCreate(request)
|
||||
if err != nil {
|
||||
return dto.FirewallRuleCreateResponse{}, err
|
||||
if request.BackupFile != "" {
|
||||
if len(request.Items) != 0 {
|
||||
return dto.FirewallRuleCreateResponse{}, filter.ErrInvalidRule
|
||||
}
|
||||
items, err := readFirewallBackup(request.BackupFile)
|
||||
if err != nil {
|
||||
return dto.FirewallRuleCreateResponse{}, err
|
||||
}
|
||||
for _, item := range items {
|
||||
request.Items = append(request.Items, dto.FirewallRuleCreateItem{Rule: item.FirewallRule, Raw: item.Raw, ParseStatus: item.ParseStatus, SourceKind: constant.FirewallRuleSourceImported})
|
||||
}
|
||||
}
|
||||
|
||||
for index, item := range request.Items {
|
||||
if item.SourceKind != constant.FirewallRuleSourceImported || item.Rule.Scope.Provider != filter.ProviderUFW || item.ParseStatus != filter.ParseStatusPartial || item.Raw == "" {
|
||||
continue
|
||||
}
|
||||
observed := filterufw.ParseRules(item.Rule.Scope, item.Raw)
|
||||
if len(observed) == 1 && observed[0].ParseStatus == filter.ParseStatusSupported {
|
||||
observed[0].Rule.Description = item.Rule.Description
|
||||
request.Items[index].Rule = observed[0].Rule
|
||||
request.Items[index].ParseStatus, request.Items[index].Raw = filter.ParseStatusSupported, ""
|
||||
}
|
||||
}
|
||||
|
||||
provider, err := s.selectedProvider(ctx)
|
||||
@@ -624,11 +609,30 @@ func (s *FirewallService) Create(ctx context.Context, request dto.FirewallRuleCr
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.runFirewallLifecycleTask(t, client, dto.FirewallLifecycleOperation{Operation: "start"}, nil)
|
||||
return s.runFirewallLifecycleTask(t, client, dto.FirewallLifecycleOperation{Operation: "start"}, false)
|
||||
}, nil)
|
||||
}
|
||||
ruleState := make(map[string]filter.RuleSet)
|
||||
taskItem.AddSubTaskWithOps(i18n.GetMsgByKey("FirewallCreateRulesStep"), func(t *task.Task) error {
|
||||
return s.runFirewallCreateRulesTask(t, request, provider)
|
||||
firewallRuleMutationMu.Lock()
|
||||
if err := s.checkSelectedProvider(t.TaskCtx, provider); err != nil {
|
||||
firewallRuleMutationMu.Unlock()
|
||||
return err
|
||||
}
|
||||
_, err := s.createRules(t.TaskCtx, request, t, ruleState)
|
||||
firewallRuleMutationMu.Unlock()
|
||||
if !request.Initialize || errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
ruleState = nil
|
||||
}
|
||||
whitelistErr := s.syncPortWhitelist(t.TaskCtx, ruleState)
|
||||
if whitelistErr == nil && provider == filter.ProviderFirewalld {
|
||||
whitelistErr = lifecycleproviders.RemoveFirewalldSSHService()
|
||||
}
|
||||
t.LogWithStatus(i18n.GetMsgByKey("FirewallSyncWhitelistStep"), whitelistErr)
|
||||
return errors.Join(err, whitelistErr)
|
||||
}, nil, 0, 0)
|
||||
if err := taskRepo.Save(context.Background(), taskItem.Task); err != nil {
|
||||
taskItem.LogFailedWithErr(taskItem.Name, err)
|
||||
@@ -773,11 +777,7 @@ func (s *FirewallService) restoreFirewallAfterStart(client lifecycle.Client) err
|
||||
return nil
|
||||
}
|
||||
}
|
||||
whitelistErr := s.SyncPortWhitelist(ctx)
|
||||
if whitelistErr == nil && provider == filter.ProviderFirewalld {
|
||||
whitelistErr = lifecycleproviders.RemoveFirewalldSSHService()
|
||||
}
|
||||
recordFailure("restore whitelist allowances", whitelistErr)
|
||||
recordFailure("restore whitelist allowances", s.SyncPortWhitelist(ctx))
|
||||
return errors.Join(recoveryErrors...)
|
||||
}
|
||||
|
||||
@@ -805,9 +805,17 @@ func (s *FirewallService) restoreFirewalldRuntimeDependents(ctx context.Context,
|
||||
return restoreErr
|
||||
}
|
||||
|
||||
func (s *FirewallService) runFirewallLifecycleTask(t *task.Task, client lifecycle.Client, request dto.FirewallLifecycleOperation, prepareStart func(lifecycle.Client) error) error {
|
||||
func (s *FirewallService) runFirewallLifecycleTask(t *task.Task, client lifecycle.Client, request dto.FirewallLifecycleOperation, syncWhitelist bool) error {
|
||||
ctx := t.TaskCtx
|
||||
provider := filter.Provider(client.Name())
|
||||
var prepareStart func(lifecycle.Client) error
|
||||
if syncWhitelist {
|
||||
prepareStart = func(lifecycle.Client) error {
|
||||
return runFirewallLifecycleAction(t, i18n.GetMsgByKey("FirewallSyncWhitelistStep"), func() error {
|
||||
return s.SyncPortWhitelist(ctx)
|
||||
})
|
||||
}
|
||||
}
|
||||
operationErr := operateFirewallLifecycle(firewallLifecycleClient{client}, request.Operation, request.WithDockerRestart, prepareStart, t)
|
||||
if request.Operation == string(lifecycle.OperationStop) {
|
||||
return operationErr
|
||||
|
||||
@@ -554,58 +554,6 @@ func (s *FirewallService) checkSelectedProvider(ctx context.Context, requested f
|
||||
return nil
|
||||
}
|
||||
|
||||
func prepareFirewallRuleCreate(request dto.FirewallRuleCreate) (dto.FirewallRuleCreate, error) {
|
||||
if request.BackupFile != "" {
|
||||
if len(request.Items) != 0 {
|
||||
return request, filter.ErrInvalidRule
|
||||
}
|
||||
items, err := readFirewallBackup(request.BackupFile)
|
||||
if err != nil {
|
||||
return request, err
|
||||
}
|
||||
for _, item := range items {
|
||||
request.Items = append(request.Items, dto.FirewallRuleCreateItem{Rule: item.FirewallRule, Raw: item.Raw, ParseStatus: item.ParseStatus, SourceKind: constant.FirewallRuleSourceImported})
|
||||
}
|
||||
}
|
||||
|
||||
for index, item := range request.Items {
|
||||
if item.SourceKind != constant.FirewallRuleSourceImported || item.Rule.Scope.Provider != filter.ProviderUFW || item.ParseStatus != filter.ParseStatusPartial || item.Raw == "" {
|
||||
continue
|
||||
}
|
||||
observed := filterufw.ParseRules(item.Rule.Scope, item.Raw)
|
||||
if len(observed) == 1 && observed[0].ParseStatus == filter.ParseStatusSupported {
|
||||
observed[0].Rule.Description = item.Rule.Description
|
||||
request.Items[index].Rule = observed[0].Rule
|
||||
request.Items[index].ParseStatus, request.Items[index].Raw = filter.ParseStatusSupported, ""
|
||||
}
|
||||
}
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func (s *FirewallService) runFirewallCreateRulesTask(t *task.Task, request dto.FirewallRuleCreate, provider filter.Provider) error {
|
||||
ruleState := make(map[string]filter.RuleSet)
|
||||
firewallRuleMutationMu.Lock()
|
||||
if err := s.checkSelectedProvider(t.TaskCtx, provider); err != nil {
|
||||
firewallRuleMutationMu.Unlock()
|
||||
return err
|
||||
}
|
||||
_, err := s.createRules(t.TaskCtx, request, t, ruleState)
|
||||
firewallRuleMutationMu.Unlock()
|
||||
if !request.Initialize || errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
ruleState = nil
|
||||
}
|
||||
whitelistErr := s.syncPortWhitelist(t.TaskCtx, ruleState)
|
||||
if whitelistErr == nil && provider == filter.ProviderFirewalld {
|
||||
whitelistErr = lifecycleproviders.RemoveFirewalldSSHService()
|
||||
}
|
||||
t.LogWithStatus(i18n.GetMsgByKey("FirewallSyncWhitelistStep"), whitelistErr)
|
||||
return errors.Join(err, whitelistErr)
|
||||
}
|
||||
|
||||
func (s *FirewallService) createRules(ctx context.Context, request dto.FirewallRuleCreate, t *task.Task, ruleState map[string]filter.RuleSet) (result dto.FirewallRuleCreateResponse, taskErr error) {
|
||||
var firstFailure error
|
||||
defer func() {
|
||||
@@ -1786,6 +1734,9 @@ func operateFirewallLifecycle(client lifecycle.Client, operation string, withDoc
|
||||
var recoveryErrors []error
|
||||
if prepareStart != nil {
|
||||
err := prepareStart(client)
|
||||
if err == nil && client.Name() == constant.FirewallProviderFirewalld {
|
||||
err = lifecycleproviders.RemoveFirewalldSSHService()
|
||||
}
|
||||
if err != nil {
|
||||
recoveryErrors = append(recoveryErrors, fmt.Errorf("prepare firewall after %s: %w", operation, err))
|
||||
}
|
||||
@@ -1807,82 +1758,17 @@ func operateFirewallLifecycle(client lifecycle.Client, operation string, withDoc
|
||||
}
|
||||
|
||||
func (c firewallLifecycleClient) Start() error {
|
||||
firewallWhitelistMu.Lock()
|
||||
defer firewallWhitelistMu.Unlock()
|
||||
firewallRuleMutationMu.Lock()
|
||||
defer firewallRuleMutationMu.Unlock()
|
||||
if err := c.prepareAccessWhitelistBeforeStart(); err != nil {
|
||||
global.LOG.Warnf("prepare %s access whitelist before start failed: %v", c.Name(), err)
|
||||
}
|
||||
return c.Client.Start()
|
||||
}
|
||||
|
||||
func (c firewallLifecycleClient) Restart() error {
|
||||
firewallWhitelistMu.Lock()
|
||||
defer firewallWhitelistMu.Unlock()
|
||||
firewallRuleMutationMu.Lock()
|
||||
defer firewallRuleMutationMu.Unlock()
|
||||
if err := c.prepareAccessWhitelistBeforeStart(); err != nil {
|
||||
global.LOG.Warnf("prepare %s access whitelist before restart failed: %v", c.Name(), err)
|
||||
}
|
||||
return c.Client.Restart()
|
||||
}
|
||||
|
||||
func (c firewallLifecycleClient) prepareAccessWhitelistBeforeStart() error {
|
||||
provider := filter.Provider(c.Name())
|
||||
if provider != filter.ProviderFirewalld && provider != filter.ProviderUFW {
|
||||
return nil
|
||||
}
|
||||
ports, err := LoadRequiredFirewallPortWhiteList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var adapter filter.Adapter
|
||||
var active bool
|
||||
if provider == filter.ProviderFirewalld {
|
||||
active, err = c.Status()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
adapter = filterfirewalld.NewAdapter()
|
||||
} else {
|
||||
adapter = filterufw.NewAdapter()
|
||||
}
|
||||
var commands []filter.NativeCommand
|
||||
for _, port := range firewall.ExpandPortWhitelist(ports) {
|
||||
rule := firewall.RuleForSystemPort(provider, port)
|
||||
rule.Scope = rule.Scope.Normalize()
|
||||
rule.UUID = uuid.NewString()
|
||||
if provider == filter.ProviderUFW {
|
||||
position := int64(1)
|
||||
rule.OrderIndex = &position
|
||||
}
|
||||
plan, err := adapter.BuildCommands(filter.RuleSet{Scope: rule.Scope}, []filter.RuleChange{{Operation: filter.ChangeCreate, After: &rule}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range plan.Rules {
|
||||
for _, command := range item.Commands {
|
||||
if provider == filter.ProviderFirewalld {
|
||||
if active {
|
||||
command.Args = append(command.Args, "--permanent")
|
||||
} else {
|
||||
command.Executable = "firewall-offline-cmd"
|
||||
}
|
||||
}
|
||||
commands = append(commands, command)
|
||||
}
|
||||
}
|
||||
}
|
||||
manager := cmd.NewCommandMgr(cmd.WithTimeout(60*time.Second), cmd.WithEnv("LC_ALL=C", "LANGUAGE=en_US:en"))
|
||||
for _, command := range commands {
|
||||
if err := manager.RunWithOptionalSudo(command.Executable, command.Args...); err != nil {
|
||||
return fmt.Errorf("prepare %s access whitelist before start: %w", provider, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runFirewallLifecycleAction(t *task.Task, name string, action func() error) error {
|
||||
t.Log(i18n.GetWithName("TaskStart", name))
|
||||
started := time.Now()
|
||||
|
||||
@@ -55,7 +55,7 @@ type IMonitorService interface {
|
||||
LoadIOOptions() []string
|
||||
LoadNetworkOptions() []string
|
||||
LoadVLLMMonitorData(ctx context.Context, req dto.MonitorVLLMSearch) (dto.MonitorVLLMData, error)
|
||||
LoadVLLMCurrent(ctx context.Context, req dto.MonitorVLLMCurrent) (dto.MonitorVLLMCurrentData, error)
|
||||
LoadVLLMCurrent(ctx context.Context, req dto.MonitorVLLMCurrent) (model.MonitorVLLM, error)
|
||||
CleanVLLMMonitor(req dto.MonitorVLLMClean) error
|
||||
LoadGPUOptions() dto.MonitorGPUOptions
|
||||
LoadGPUMonitorData(req dto.MonitorGPUSearch) (dto.MonitorGPUData, error)
|
||||
|
||||
@@ -60,9 +60,6 @@ func (m *MonitorService) LoadVLLMMonitorData(ctx context.Context, req dto.Monito
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
for i := range points {
|
||||
points[i].MonitorVLLM.CompletedRequests = points[i].CompletedRequests
|
||||
}
|
||||
if data.BucketSeconds > 0 {
|
||||
aggregates := map[int64]*vllm.HistoryAccumulator{}
|
||||
err = vllmMonitorRepo.WalkDeltas(ctx, req.AppInstallID, req.StartTime, req.EndTime, data.BucketSeconds, req.Aggregation, req.Group, func(bucket int64, histograms, requests string) error {
|
||||
@@ -94,6 +91,15 @@ func (m *MonitorService) LoadVLLMMonitorData(ctx context.Context, req dto.Monito
|
||||
data.Points = append(data.Points, point)
|
||||
}
|
||||
} else {
|
||||
setting, err := settingRepo.GetValueByKey("VLLMMonitorInterval")
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
intervalSeconds, err := strconv.Atoi(setting)
|
||||
if err != nil || intervalSeconds <= 0 {
|
||||
return data, fmt.Errorf("invalid vLLM monitoring interval: %s", setting)
|
||||
}
|
||||
interval := time.Duration(intervalSeconds) * time.Second
|
||||
for i, point := range points {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return data, err
|
||||
@@ -103,20 +109,8 @@ func (m *MonitorService) LoadVLLMMonitorData(ctx context.Context, req dto.Monito
|
||||
return data, err
|
||||
}
|
||||
aggregate.Apply(&point.MonitorVLLM, "max")
|
||||
if i > 0 {
|
||||
previous := points[i-1]
|
||||
interval := time.Duration(max(previous.SampleIntervalSeconds, point.SampleIntervalSeconds)) * time.Second
|
||||
if interval == 0 {
|
||||
if i > 1 {
|
||||
interval = previous.CreatedAt.Sub(points[i-2].CreatedAt)
|
||||
}
|
||||
if i+1 < len(points) {
|
||||
interval = max(interval, points[i+1].CreatedAt.Sub(point.CreatedAt))
|
||||
}
|
||||
}
|
||||
if interval > 0 && point.CreatedAt.Sub(previous.CreatedAt) > 2*interval {
|
||||
data.Points = append(data.Points, model.MonitorVLLM{CreatedAt: previous.CreatedAt.Add(interval), AppInstallID: req.AppInstallID})
|
||||
}
|
||||
if i > 0 && point.CreatedAt.Sub(points[i-1].CreatedAt) > 2*interval {
|
||||
data.Points = append(data.Points, model.MonitorVLLM{CreatedAt: points[i-1].CreatedAt.Add(interval), AppInstallID: req.AppInstallID})
|
||||
}
|
||||
data.Points = append(data.Points, point.MonitorVLLM)
|
||||
}
|
||||
@@ -124,30 +118,31 @@ func (m *MonitorService) LoadVLLMMonitorData(ctx context.Context, req dto.Monito
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (m *MonitorService) LoadVLLMCurrent(ctx context.Context, req dto.MonitorVLLMCurrent) (dto.MonitorVLLMCurrentData, error) {
|
||||
func (m *MonitorService) LoadVLLMCurrent(ctx context.Context, req dto.MonitorVLLMCurrent) (model.MonitorVLLM, error) {
|
||||
install, err := appInstallRepo.GetFirst(repo.WithByID(req.AppInstallID))
|
||||
if err != nil {
|
||||
return dto.MonitorVLLMCurrentData{}, err
|
||||
return model.MonitorVLLM{}, err
|
||||
}
|
||||
if install.App.Key != "vllm" {
|
||||
return dto.MonitorVLLMCurrentData{}, fmt.Errorf("not a vLLM installation")
|
||||
return model.MonitorVLLM{}, fmt.Errorf("not a vLLM installation")
|
||||
}
|
||||
point, err := collectVLLMMetrics(ctx, install, model.MonitorVLLM{})
|
||||
data := dto.MonitorVLLMCurrentData{MonitorVLLM: point}
|
||||
previous, err := collectVLLMMetrics(ctx, install, model.MonitorVLLM{})
|
||||
if err != nil {
|
||||
global.LOG.Debugf("Collect vLLM metrics on port %d failed: %v", install.HttpPort, err)
|
||||
return data, nil
|
||||
return previous, nil
|
||||
}
|
||||
if err := json.Unmarshal([]byte(point.RawMetrics), &data.Metrics); err != nil {
|
||||
return data, err
|
||||
timer := time.NewTimer(time.Second)
|
||||
defer timer.Stop()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return model.MonitorVLLM{}, ctx.Err()
|
||||
case <-timer.C:
|
||||
}
|
||||
for name := range data.Metrics {
|
||||
if name == "request_prompt_tokens_sum" || name == "request_prompt_tokens_count" ||
|
||||
(!strings.HasSuffix(name, "_total") && !strings.HasSuffix(name, "_sum") && !strings.HasSuffix(name, "_count")) {
|
||||
delete(data.Metrics, name)
|
||||
}
|
||||
point, err := collectVLLMMetrics(ctx, install, previous)
|
||||
if err != nil {
|
||||
global.LOG.Debugf("Collect vLLM metrics on port %d failed: %v", install.HttpPort, err)
|
||||
}
|
||||
return data, nil
|
||||
return point, nil
|
||||
}
|
||||
|
||||
func (m *MonitorService) CleanVLLMMonitor(req dto.MonitorVLLMClean) error {
|
||||
@@ -169,7 +164,7 @@ func StartVLLMMonitor(interval string) error {
|
||||
return fmt.Errorf("invalid vLLM monitoring interval: %s", interval)
|
||||
}
|
||||
service := &MonitorService{}
|
||||
job := cron.NewChain(cron.Recover(cron.DefaultLogger)).Then(cron.FuncJob(func() { service.saveVLLMData(seconds) }))
|
||||
job := cron.NewChain(cron.Recover(cron.DefaultLogger)).Then(cron.FuncJob(service.saveVLLMData))
|
||||
id, err := global.Cron.AddFunc(fmt.Sprintf("@every %ds", seconds), func() { go job.Run() })
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -181,7 +176,7 @@ func StartVLLMMonitor(interval string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MonitorService) saveVLLMData(intervalSeconds int) {
|
||||
func (m *MonitorService) saveVLLMData() {
|
||||
if !vllmMonitorMutex.TryLock() {
|
||||
return
|
||||
}
|
||||
@@ -233,7 +228,6 @@ func (m *MonitorService) saveVLLMData(intervalSeconds int) {
|
||||
return err
|
||||
}
|
||||
point, err := collectVLLMMetrics(context.Background(), install, previous)
|
||||
point.SampleIntervalSeconds = intervalSeconds
|
||||
if err != nil {
|
||||
global.LOG.Debugf("Collect vLLM metrics for %s failed: %v", install.Name, err)
|
||||
}
|
||||
|
||||
@@ -212,17 +212,10 @@ func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) (*model.Website
|
||||
websiteSSL.Shell = req.Shell
|
||||
}
|
||||
if req.PushDir {
|
||||
dirs := certificateDirectories(req.Dir)
|
||||
if len(dirs) == 0 {
|
||||
if !files.NewFileOp().Stat(req.Dir) {
|
||||
return nil, buserr.New("ErrLinkPathNotFound")
|
||||
}
|
||||
for _, dir := range dirs {
|
||||
info, err := os.Stat(dir)
|
||||
if err != nil || !info.IsDir() {
|
||||
return nil, buserr.New("ErrLinkPathNotFound")
|
||||
}
|
||||
}
|
||||
websiteSSL.Dir = strings.Join(dirs, "\n")
|
||||
websiteSSL.Dir = req.Dir
|
||||
}
|
||||
if req.Domains != "" {
|
||||
domainArray := strings.Split(req.Domains, "\n")
|
||||
@@ -355,9 +348,7 @@ func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) (*model.Website
|
||||
if websiteSSL.ExecShell {
|
||||
workDir := global.Dir.DataDir
|
||||
if websiteSSL.PushDir {
|
||||
if dirs := certificateDirectories(websiteSSL.Dir); len(dirs) > 0 {
|
||||
workDir = dirs[0]
|
||||
}
|
||||
workDir = websiteSSL.Dir
|
||||
}
|
||||
logger.Println(i18n.GetMsgByKey("ExecShellStart"))
|
||||
if err = runShellScriptFile(workDir, websiteSSL.Shell, logger); err != nil {
|
||||
|
||||
@@ -209,17 +209,11 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
|
||||
websiteSSL.Shell = create.Shell
|
||||
}
|
||||
if create.PushDir {
|
||||
dirs := certificateDirectories(create.Dir)
|
||||
if len(dirs) == 0 {
|
||||
fileOP := files.NewFileOp()
|
||||
if !fileOP.Stat(create.Dir) {
|
||||
return res, buserr.New("ErrLinkPathNotFound")
|
||||
}
|
||||
for _, dir := range dirs {
|
||||
info, err := os.Stat(dir)
|
||||
if err != nil || !info.IsDir() {
|
||||
return res, buserr.New("ErrLinkPathNotFound")
|
||||
}
|
||||
}
|
||||
websiteSSL.Dir = strings.Join(dirs, "\n")
|
||||
websiteSSL.Dir = create.Dir
|
||||
}
|
||||
setSSLPushConfig(&websiteSSL, create.PushNode, create.Nodes)
|
||||
|
||||
@@ -579,9 +573,7 @@ func (w WebsiteSSLService) obtainSSL(id uint, autoRenew bool) error {
|
||||
if websiteSSL.ExecShell {
|
||||
workDir := global.Dir.DataDir
|
||||
if websiteSSL.PushDir {
|
||||
if dirs := certificateDirectories(websiteSSL.Dir); len(dirs) > 0 {
|
||||
workDir = dirs[0]
|
||||
}
|
||||
workDir = websiteSSL.Dir
|
||||
}
|
||||
printSSLLog(logger, "ExecShellStart", nil)
|
||||
if err = runShellScriptFile(workDir, websiteSSL.Shell, logger); err != nil {
|
||||
@@ -801,16 +793,11 @@ func (w WebsiteSSLService) Update(update request.WebsiteSSLUpdate) error {
|
||||
}
|
||||
|
||||
if update.PushDir {
|
||||
dirs := certificateDirectories(update.Dir)
|
||||
if len(dirs) == 0 {
|
||||
return buserr.New("ErrLinkPathNotFound")
|
||||
fileOP := files.NewFileOp()
|
||||
if !fileOP.Stat(update.Dir) {
|
||||
_ = fileOP.CreateDir(update.Dir, constant.DirPerm)
|
||||
}
|
||||
for _, dir := range dirs {
|
||||
if err := os.MkdirAll(dir, constant.DirPerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
updateParams["dir"] = strings.Join(dirs, "\n")
|
||||
updateParams["dir"] = update.Dir
|
||||
}
|
||||
var domains []string
|
||||
if update.OtherDomains != "" {
|
||||
|
||||
@@ -1529,41 +1529,26 @@ func checkWebsitePort(defaultHTTPsPort, port int, websiteType string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// certificateDirectories accepts both legacy single paths and newline-separated paths.
|
||||
func certificateDirectories(value string) []string {
|
||||
var dirs []string
|
||||
seen := make(map[string]bool)
|
||||
for _, dir := range strings.Split(value, "\n") {
|
||||
dir = strings.TrimSpace(dir)
|
||||
if dir == "" {
|
||||
continue
|
||||
}
|
||||
dir = filepath.Clean(dir)
|
||||
if !seen[dir] {
|
||||
seen[dir] = true
|
||||
dirs = append(dirs, dir)
|
||||
}
|
||||
}
|
||||
return dirs
|
||||
}
|
||||
|
||||
func saveCertificateFile(websiteSSL *model.WebsiteSSL, logger *log.Logger) {
|
||||
if !websiteSSL.PushDir {
|
||||
return
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
for _, dir := range certificateDirectories(websiteSSL.Dir) {
|
||||
status := i18n.GetMsgByKey("Success")
|
||||
for _, file := range []struct{ name, content string }{
|
||||
{"privkey.pem", websiteSSL.PrivateKey},
|
||||
{"fullchain.pem", websiteSSL.Pem},
|
||||
} {
|
||||
if err := fileOp.SaveFile(path.Join(dir, file.name), file.content, constant.FilePerm); err != nil {
|
||||
status = i18n.GetMsgByKey("Failed")
|
||||
logger.Printf("Push dir %s failed: %s", dir, err)
|
||||
}
|
||||
if websiteSSL.PushDir {
|
||||
fileOp := files.NewFileOp()
|
||||
var (
|
||||
pushErr error
|
||||
MsgMap = map[string]interface{}{"path": websiteSSL.Dir, "status": i18n.GetMsgByKey("Success")}
|
||||
)
|
||||
if pushErr = fileOp.SaveFile(path.Join(websiteSSL.Dir, "privkey.pem"), websiteSSL.PrivateKey, constant.FilePerm); pushErr != nil {
|
||||
MsgMap["status"] = i18n.GetMsgByKey("Failed")
|
||||
logger.Println(i18n.GetMsgWithMap("PushDirLog", MsgMap))
|
||||
logger.Println("Push dir failed:" + pushErr.Error())
|
||||
}
|
||||
if pushErr = fileOp.SaveFile(path.Join(websiteSSL.Dir, "fullchain.pem"), websiteSSL.Pem, constant.FilePerm); pushErr != nil {
|
||||
MsgMap["status"] = i18n.GetMsgByKey("Failed")
|
||||
logger.Println(i18n.GetMsgWithMap("PushDirLog", MsgMap))
|
||||
logger.Println("Push dir failed:" + pushErr.Error())
|
||||
}
|
||||
if pushErr == nil {
|
||||
logger.Println(i18n.GetMsgWithMap("PushDirLog", MsgMap))
|
||||
}
|
||||
logger.Println(i18n.GetMsgWithMap("PushDirLog", map[string]interface{}{"path": dir, "status": status}))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,29 +108,33 @@
|
||||
"formatEN": "create alert [title]"
|
||||
},
|
||||
"/alert/config/del": {
|
||||
"bodyKeys": [],
|
||||
"bodyKeys": [
|
||||
"id"
|
||||
],
|
||||
"paramKeys": [],
|
||||
"formatZH": "删除告警配置 [name]",
|
||||
"formatEN": "delete alert config [name]",
|
||||
"beforeFunctions": []
|
||||
"beforeFunctions": [],
|
||||
"formatZH": "删除告警配置 [id]",
|
||||
"formatEN": "delete alert config [id]"
|
||||
},
|
||||
"/alert/config/status": {
|
||||
"bodyKeys": [
|
||||
"id",
|
||||
"status"
|
||||
],
|
||||
"paramKeys": [],
|
||||
"formatZH": "更新告警配置状态 [name][status]",
|
||||
"formatEN": "update alert config status [name][status]",
|
||||
"beforeFunctions": []
|
||||
"beforeFunctions": [],
|
||||
"formatZH": "更新告警配置状态 [id][status]",
|
||||
"formatEN": "update alert config status [id][status]"
|
||||
},
|
||||
"/alert/config/update": {
|
||||
"bodyKeys": [
|
||||
"id",
|
||||
"displayName"
|
||||
],
|
||||
"paramKeys": [],
|
||||
"formatZH": "更新告警配置 [displayName]",
|
||||
"formatEN": "update alert config [displayName]",
|
||||
"beforeFunctions": []
|
||||
"beforeFunctions": [],
|
||||
"formatZH": "更新告警配置 [id][displayName]",
|
||||
"formatEN": "update alert config [id][displayName]"
|
||||
},
|
||||
"/alert/del": {
|
||||
"bodyKeys": [
|
||||
|
||||
@@ -114,7 +114,6 @@ func agentDBMigrations() []*gormigrate.Migration {
|
||||
migrations.AddVLLMMonitor,
|
||||
migrations.AddMonitorSettings,
|
||||
migrations.AddVLLMRequestMetrics,
|
||||
migrations.AddVLLMSampleInterval,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1881,10 +1881,3 @@ var AddVLLMRequestMetrics = &gormigrate.Migration{
|
||||
return global.VLLMMonitorDB.AutoMigrate(&model.MonitorVLLM{})
|
||||
},
|
||||
}
|
||||
|
||||
var AddVLLMSampleInterval = &gormigrate.Migration{
|
||||
ID: "20261016-vllm-sample-interval",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return global.VLLMMonitorDB.AutoMigrate(&model.MonitorVLLM{})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -314,17 +314,7 @@ func (s *Session) detach(a *attachment, clean, revalidate bool, cursor uint64) {
|
||||
if !s.Persistent || revalidate {
|
||||
timeout = revalidateGrace
|
||||
}
|
||||
// Stop cannot cancel a fired timer; a racing Attach clears s.grace.
|
||||
var grace *time.Timer
|
||||
grace = time.AfterFunc(timeout, func() {
|
||||
s.mu.Lock()
|
||||
expired := s.grace == grace
|
||||
s.mu.Unlock()
|
||||
if expired {
|
||||
s.Close()
|
||||
}
|
||||
})
|
||||
s.grace = grace
|
||||
s.grace = time.AfterFunc(timeout, s.Close)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
if clean {
|
||||
|
||||
@@ -3906,11 +3906,13 @@ const docTemplate = `{
|
||||
"Alert"
|
||||
],
|
||||
"x-panel-log": {
|
||||
"bodyKeys": [],
|
||||
"paramKeys": [],
|
||||
"formatZH": "删除告警配置 [name]",
|
||||
"formatEN": "delete alert config [name]",
|
||||
"BeforeFunctions": []
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [
|
||||
"id"
|
||||
],
|
||||
"formatEN": "delete alert config [id]",
|
||||
"formatZH": "删除告警配置 [id]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4039,13 +4041,14 @@ const docTemplate = `{
|
||||
"Alert"
|
||||
],
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [
|
||||
"id",
|
||||
"displayName"
|
||||
],
|
||||
"paramKeys": [],
|
||||
"formatZH": "更新告警配置 [displayName]",
|
||||
"formatEN": "update alert config [displayName]",
|
||||
"BeforeFunctions": []
|
||||
"formatEN": "update alert config [id][displayName]",
|
||||
"formatZH": "更新告警配置 [id][displayName]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3902,11 +3902,13 @@
|
||||
"Alert"
|
||||
],
|
||||
"x-panel-log": {
|
||||
"bodyKeys": [],
|
||||
"paramKeys": [],
|
||||
"formatZH": "删除告警配置 [name]",
|
||||
"formatEN": "delete alert config [name]",
|
||||
"BeforeFunctions": []
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [
|
||||
"id"
|
||||
],
|
||||
"formatEN": "delete alert config [id]",
|
||||
"formatZH": "删除告警配置 [id]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4035,13 +4037,14 @@
|
||||
"Alert"
|
||||
],
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [
|
||||
"id",
|
||||
"displayName"
|
||||
],
|
||||
"paramKeys": [],
|
||||
"formatZH": "更新告警配置 [displayName]",
|
||||
"formatEN": "update alert config [displayName]",
|
||||
"BeforeFunctions": []
|
||||
"formatEN": "update alert config [id][displayName]",
|
||||
"formatZH": "更新告警配置 [id][displayName]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -108,29 +108,33 @@
|
||||
"formatEN": "create alert [title]"
|
||||
},
|
||||
"/alert/config/del": {
|
||||
"bodyKeys": [],
|
||||
"bodyKeys": [
|
||||
"id"
|
||||
],
|
||||
"paramKeys": [],
|
||||
"formatZH": "删除告警配置 [name]",
|
||||
"formatEN": "delete alert config [name]",
|
||||
"beforeFunctions": []
|
||||
"beforeFunctions": [],
|
||||
"formatZH": "删除告警配置 [id]",
|
||||
"formatEN": "delete alert config [id]"
|
||||
},
|
||||
"/alert/config/status": {
|
||||
"bodyKeys": [
|
||||
"id",
|
||||
"status"
|
||||
],
|
||||
"paramKeys": [],
|
||||
"formatZH": "更新告警配置状态 [name][status]",
|
||||
"formatEN": "update alert config status [name][status]",
|
||||
"beforeFunctions": []
|
||||
"beforeFunctions": [],
|
||||
"formatZH": "更新告警配置状态 [id][status]",
|
||||
"formatEN": "update alert config status [id][status]"
|
||||
},
|
||||
"/alert/config/update": {
|
||||
"bodyKeys": [
|
||||
"id",
|
||||
"displayName"
|
||||
],
|
||||
"paramKeys": [],
|
||||
"formatZH": "更新告警配置 [displayName]",
|
||||
"formatEN": "update alert config [displayName]",
|
||||
"beforeFunctions": []
|
||||
"beforeFunctions": [],
|
||||
"formatZH": "更新告警配置 [id][displayName]",
|
||||
"formatEN": "update alert config [id][displayName]"
|
||||
},
|
||||
"/alert/del": {
|
||||
"bodyKeys": [
|
||||
|
||||
@@ -133,9 +133,11 @@ func OperationLog() gin.HandlerFunc {
|
||||
record.AuthMethod = "session"
|
||||
}
|
||||
|
||||
// Handlers may supply audit values even without database resolution metadata.
|
||||
mergeResolvedData(writer.resolvedHeader, formatMap)
|
||||
if len(operationDic.BeforeFunctions) != 0 {
|
||||
if needAgentResolve {
|
||||
mergeResolvedData(writer.resolvedHeader, formatMap)
|
||||
}
|
||||
|
||||
if allowCoreFallback && !hasAllResolvedData(formatMap, operationDic.BeforeFunctions) {
|
||||
dbItem, err := newDB(record.Path)
|
||||
if err == nil {
|
||||
|
||||
Generated
+35
-188
@@ -45,7 +45,7 @@
|
||||
"vue-clipboard3": "^2.0.0",
|
||||
"vue-codemirror": "^6.1.1",
|
||||
"vue-demi": "^0.14.6",
|
||||
"vue-i18n": "^11.4.4",
|
||||
"vue-i18n": "^11.4.14",
|
||||
"vue-router": "^5.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -126,7 +126,6 @@
|
||||
"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.29.7",
|
||||
"@babel/generator": "^7.29.7",
|
||||
@@ -1014,7 +1013,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1031,7 +1029,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1048,7 +1045,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1065,7 +1061,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1082,7 +1077,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1099,7 +1093,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1116,7 +1109,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1133,7 +1125,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1150,7 +1141,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1167,7 +1157,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1184,7 +1173,6 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1201,7 +1189,6 @@
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1218,7 +1205,6 @@
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1235,7 +1221,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1252,7 +1237,6 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1269,7 +1253,6 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1286,7 +1269,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1303,7 +1285,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1320,7 +1301,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1337,7 +1317,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1354,7 +1333,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1371,7 +1349,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1388,7 +1365,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1405,7 +1381,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1422,7 +1397,6 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1439,7 +1413,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1684,14 +1657,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/core-base": {
|
||||
"version": "11.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.4.4.tgz",
|
||||
"integrity": "sha512-w/vItlylrAmhebkIbVl5YY8XMCtj8Mb2g70ttxktMYuf5AuRahgEHL2iLgLIsZBIbTSgs4hkUo7ucCL0uTJvOg==",
|
||||
"version": "11.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.4.14.tgz",
|
||||
"integrity": "sha512-Lox+qRGBLnEYfNz4iajqKpw1zuvSAWl4PtzEk654bEM+0NT1GBy9AbOecxzSh7krsyqn9mGKhgmvtuyBsqS6oQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/devtools-types": "11.4.4",
|
||||
"@intlify/message-compiler": "11.4.4",
|
||||
"@intlify/shared": "11.4.4"
|
||||
"@intlify/devtools-types": "11.4.14",
|
||||
"@intlify/message-compiler": "11.4.14",
|
||||
"@intlify/shared": "11.4.14"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 22"
|
||||
@@ -1701,13 +1674,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/devtools-types": {
|
||||
"version": "11.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/devtools-types/-/devtools-types-11.4.4.tgz",
|
||||
"integrity": "sha512-PcBLmGmDQsTSVV911P8upzpcLJO1CNVYi/IH6bGnLR2nA+0L963+kXN1ZrisTEnbtw2ewN6HMMSldqzjronA0Q==",
|
||||
"version": "11.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/devtools-types/-/devtools-types-11.4.14.tgz",
|
||||
"integrity": "sha512-EQHRYveQg6yHQ51kePfsFyaW2WW+0hk3FdVxSpLDyuKtqFtsRFoyQR2QzASZPmY767ujs9ipuxc2DiGJARkuiQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/core-base": "11.4.4",
|
||||
"@intlify/shared": "11.4.4"
|
||||
"@intlify/core-base": "11.4.14",
|
||||
"@intlify/shared": "11.4.14"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 22"
|
||||
@@ -1717,12 +1690,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/message-compiler": {
|
||||
"version": "11.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.4.4.tgz",
|
||||
"integrity": "sha512-vn0OAV9pYkJlPPmgnsSm5eAG3mL0+9C/oaded2JY9jmxBbhmUXT3TcAUY8WRgLY9Hte7lkUJKpXrVlYjMXBD2w==",
|
||||
"version": "11.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.4.14.tgz",
|
||||
"integrity": "sha512-y79GatHfMhN672Vooxw0v6mgX/vclFHggO1ENL6aH/QubLXiV529WXu/+FhVUbYF+HLTeDDkQpmnpfvSJddBCg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/shared": "11.4.4",
|
||||
"@intlify/shared": "11.4.14",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1733,9 +1706,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/shared": {
|
||||
"version": "11.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.4.4.tgz",
|
||||
"integrity": "sha512-QRUCHqda1U6aR14FR0vvXD4+4gj6+fm0AhAozvSuRCw0fCvrmCugWpgiR4xH2NI6s8am6N9p5OhirplsX8ZS3g==",
|
||||
"version": "11.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.4.14.tgz",
|
||||
"integrity": "sha512-f8zSlPs4+Yd9za3CPQfW9OHxAJBOeRQzB/hhOhkbGA7hlqWk0W0bUtSV/LmyrGNwdl8JriBnFtJqZyF140bjGg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 22"
|
||||
@@ -1773,18 +1746,6 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/source-map": {
|
||||
"version": "0.3.11",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
|
||||
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
@@ -2013,7 +1974,6 @@
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz",
|
||||
"integrity": "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@@ -2052,7 +2012,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2073,7 +2032,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2094,7 +2052,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2115,7 +2072,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2136,7 +2092,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2157,7 +2112,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2178,7 +2132,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2199,7 +2152,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2220,7 +2172,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2241,7 +2192,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2262,7 +2212,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2283,7 +2232,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2328,7 +2276,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2345,7 +2292,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2362,7 +2308,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2379,7 +2324,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2396,7 +2340,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2413,7 +2356,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2430,7 +2372,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2447,7 +2388,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2464,7 +2404,6 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2481,7 +2420,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2498,7 +2436,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2515,7 +2452,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2532,7 +2468,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2549,7 +2484,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2943,7 +2877,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz",
|
||||
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
@@ -2975,9 +2908,8 @@
|
||||
"version": "26.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.3.tgz",
|
||||
"integrity": "sha512-dsqMQQoeTLqu9wynDD00q573mNzso3IdQOAfHRJqLCcmCFPoGo9A1bDpUcv/9tnKpErQWv9uKeGfl37EIS02Yg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~8.9.0"
|
||||
}
|
||||
@@ -3058,7 +2990,6 @@
|
||||
"integrity": "sha512-aTtmZjp/x3gsGHkDs04/1I8kNogBJkpQaeGaN0PfiG43NzDEmW2n+OP5im9wfsvyX0yP1UZWBrAGtDBfH9jEVw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.71.2",
|
||||
"@typescript-eslint/types": "8.71.2",
|
||||
@@ -3718,7 +3649,6 @@
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz",
|
||||
"integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -3975,7 +3905,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.11.26",
|
||||
"caniuse-lite": "^1.0.30001812",
|
||||
@@ -3994,7 +3923,7 @@
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bundle-name": {
|
||||
@@ -4237,7 +4166,6 @@
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz",
|
||||
"integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/commands": "^6.0.0",
|
||||
@@ -5270,7 +5198,6 @@
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
@@ -5332,7 +5259,6 @@
|
||||
"integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
@@ -5392,7 +5318,6 @@
|
||||
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"eslint-config-prettier": "bin/cli.js"
|
||||
},
|
||||
@@ -5825,7 +5750,6 @@
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@@ -6305,7 +6229,7 @@
|
||||
"version": "5.1.9",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz",
|
||||
"integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/imurmurhash": {
|
||||
@@ -6378,7 +6302,7 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -6388,7 +6312,7 @@
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
@@ -6508,7 +6432,7 @@
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz",
|
||||
"integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jiti": "lib/jiti-cli.mjs"
|
||||
@@ -7083,15 +7007,13 @@
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
||||
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz",
|
||||
"integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash-unified": {
|
||||
"version": "1.0.3",
|
||||
@@ -7173,7 +7095,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1",
|
||||
"entities": "^4.5.0",
|
||||
@@ -7622,7 +7543,6 @@
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
|
||||
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
@@ -7885,7 +7805,6 @@
|
||||
"resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz",
|
||||
"integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/devtools-api": "^7.7.7"
|
||||
},
|
||||
@@ -7966,7 +7885,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.20",
|
||||
"picocolors": "^1.1.1",
|
||||
@@ -8078,7 +7996,6 @@
|
||||
"integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
@@ -8430,7 +8347,6 @@
|
||||
"integrity": "sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@oxc-project/types": "=0.143.0",
|
||||
"@rolldown/pluginutils": "^1.0.0"
|
||||
@@ -8527,9 +8443,8 @@
|
||||
"version": "1.105.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.105.0.tgz",
|
||||
"integrity": "sha512-4EjSuRTyG8M4yRXXY1PT9OUV5zz23xBxJkbIAprohKHqf6HOU4MCpE3U3vAC+iO1gxobBjESNbcj8rYAQZWnyA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"chokidar": "^5.0.0",
|
||||
"immutable": "^5.1.5",
|
||||
@@ -8712,29 +8627,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-support": {
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-support/node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/spdx-correct": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
|
||||
@@ -9069,34 +8961,6 @@
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.44.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz",
|
||||
"integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
"acorn": "^8.15.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"bin": {
|
||||
"terser": "bin/terser"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/text-extensions": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz",
|
||||
@@ -9228,7 +9092,6 @@
|
||||
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -9267,7 +9130,7 @@
|
||||
"version": "8.9.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz",
|
||||
"integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unimport": {
|
||||
@@ -9594,7 +9457,6 @@
|
||||
"integrity": "sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"lightningcss": "^1.33.0",
|
||||
"picomatch": "^4.0.5",
|
||||
@@ -9859,7 +9721,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -9880,7 +9741,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -9901,7 +9761,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -9922,7 +9781,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -9943,7 +9801,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -9964,7 +9821,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -9985,7 +9841,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -10006,7 +9861,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -10027,7 +9881,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -10048,7 +9901,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -10069,7 +9921,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -10095,7 +9946,6 @@
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.43.tgz",
|
||||
"integrity": "sha512-o5qZoksdnjIKvW1srZ3ab7pcDNYAerBjRe54D0LBLfRdCYFrSgBHVXokMas35czQc0//lmx4/tuY4ZNQ+Rf2Ng==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.43",
|
||||
"@vue/compiler-sfc": "3.5.43",
|
||||
@@ -10149,7 +9999,6 @@
|
||||
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
@@ -10176,7 +10025,6 @@
|
||||
"integrity": "sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.4.0",
|
||||
"eslint-scope": "^8.2.0 || ^9.0.0",
|
||||
@@ -10209,14 +10057,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vue-i18n": {
|
||||
"version": "11.4.4",
|
||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.4.4.tgz",
|
||||
"integrity": "sha512-gIbXVSFQV4jcSJxfwdZ5zSZmZ+12CnX0K3vBkRSd6Zn+HSzCp+QwUgPwpD/uN0oKNKI9RzlUXPKVedEuMgNG0A==",
|
||||
"version": "11.4.14",
|
||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.4.14.tgz",
|
||||
"integrity": "sha512-oRFZtQjRDRQtfxu7tFlsZlnCa5gPkg/KX89kJDKdC9bz4GIYgo3cAMaD0XFeUx/WHuKoVbDMRTIo9Hvg67HnPw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/core-base": "11.4.4",
|
||||
"@intlify/devtools-types": "11.4.4",
|
||||
"@intlify/shared": "11.4.4",
|
||||
"@intlify/core-base": "11.4.14",
|
||||
"@intlify/devtools-types": "11.4.14",
|
||||
"@intlify/shared": "11.4.14",
|
||||
"@vue/devtools-api": "^6.5.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -10512,7 +10360,6 @@
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz",
|
||||
"integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"bin": {
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
"vue-clipboard3": "^2.0.0",
|
||||
"vue-codemirror": "^6.1.1",
|
||||
"vue-demi": "^0.14.6",
|
||||
"vue-i18n": "^11.4.4",
|
||||
"vue-i18n": "^11.4.14",
|
||||
"vue-router": "^5.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -21,10 +21,10 @@ export const loadForwardBaseInfo = () =>
|
||||
export const searchForwardRule = (request: Firewall.ForwardRuleSearch) =>
|
||||
http.post<ResPage<Firewall.RuleInfo>>('/hosts/firewall/forward/search', request, TimeoutEnum.T_40S);
|
||||
|
||||
export const operateFire = (operation: string, withDockerRestart: boolean, backupFile?: string) =>
|
||||
export const operateFire = (operation: string, withDockerRestart: boolean) =>
|
||||
http.post<Firewall.FilterChainOperationResult>(
|
||||
'/hosts/firewall/operate',
|
||||
{ operation, withDockerRestart, backupFile },
|
||||
{ operation, withDockerRestart },
|
||||
TimeoutEnum.T_10M,
|
||||
);
|
||||
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
<el-table-column :label="$t('commons.table.name')" prop="name">
|
||||
<template #default="{ row }">
|
||||
<div v-if="!row.edit">
|
||||
<span>{{ row.name }}</span>
|
||||
<span v-if="row.name === 'Default'">
|
||||
{{ $t('commons.table.default') }}
|
||||
</span>
|
||||
<span v-if="row.name !== 'Default'">{{ row.name }}</span>
|
||||
<el-tag v-if="row.isDefault" type="success" class="ml-2" size="small">
|
||||
({{ $t('commons.table.default') }})
|
||||
</el-tag>
|
||||
|
||||
@@ -79,16 +79,6 @@
|
||||
@row-click="handleRowClick"
|
||||
class="cursor-pointer dialog-file-list"
|
||||
>
|
||||
<el-table-column v-if="isMultiple && form.dir" width="48">
|
||||
<template #default="{ row }">
|
||||
<el-checkbox
|
||||
v-if="row.isDir && !row.isCreate"
|
||||
:model-value="selectedPathSet.has(row.path)"
|
||||
@click.stop
|
||||
@change="toggleDirectory(row.path)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" show-overflow-tooltip fix>
|
||||
<template #default="{ row }">
|
||||
<svg-icon
|
||||
@@ -141,13 +131,6 @@
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="file-list-bottom">
|
||||
<el-checkbox
|
||||
v-if="isMultiple && form.dir"
|
||||
:model-value="selectedPathSet.has(req.path)"
|
||||
@change="toggleDirectory(req.path)"
|
||||
>
|
||||
{{ req.path }}
|
||||
</el-checkbox>
|
||||
<div v-if="!isMultiple && selectRow?.path">
|
||||
{{ $t('file.currentSelect') }}
|
||||
<el-tooltip :content="selectRow.path" placement="top">
|
||||
@@ -180,11 +163,7 @@
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="selectFile"
|
||||
:disabled="disBtn || (isMultiple && selectRows.length === 0)"
|
||||
>
|
||||
<el-button type="primary" @click="selectFile" :disabled="disBtn">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
@@ -195,7 +174,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { File } from '@/api/interface/file';
|
||||
import { computeDirSize, createFile, getFileContent, getFilesList, searchFavorite } from '@/api/modules/files';
|
||||
import { computed, nextTick, reactive, ref } from 'vue';
|
||||
import { nextTick, onUpdated, reactive, ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess, MsgWarning } from '@/utils/message';
|
||||
import { useSearchableForSelect } from '@/views/host/file-management/hooks/searchable';
|
||||
@@ -207,7 +186,6 @@ const paths = ref<string[]>([]);
|
||||
const req = reactive({ path: '/', expand: true, page: 1, pageSize: 300, showHidden: true });
|
||||
const selectRow = ref({ path: '', name: '' });
|
||||
const selectRows = ref<Array<{ path: string; name: string }>>([]);
|
||||
const selectedPathSet = computed(() => new Set(selectRows.value.map((item) => item.path)));
|
||||
const isMultiple = ref(false);
|
||||
const rowRefs = ref();
|
||||
const open = ref(false);
|
||||
@@ -233,7 +211,6 @@ interface DialogProps {
|
||||
isAll?: boolean;
|
||||
disabled?: boolean;
|
||||
multiple?: boolean;
|
||||
selectedPaths?: string[];
|
||||
}
|
||||
const acceptParams = (props: DialogProps): void => {
|
||||
form.path = props.path || '/';
|
||||
@@ -241,8 +218,7 @@ const acceptParams = (props: DialogProps): void => {
|
||||
form.isAll = props.isAll || false;
|
||||
form.disabled = props.disabled || false;
|
||||
isMultiple.value = props.multiple || false;
|
||||
disBtn.value = false;
|
||||
selectRows.value = [...new Set(props.selectedPaths || [])].map((path) => ({ path, name: path }));
|
||||
selectRows.value = [];
|
||||
selectRow.value = { path: '', name: '' };
|
||||
openPage();
|
||||
req.path = form.path;
|
||||
@@ -305,7 +281,7 @@ const openDir = async (row: File.File, column: any, event: any) => {
|
||||
}
|
||||
// Handle file click
|
||||
const fullPath = (req.path === '/' ? req.path : req.path + '/') + row.name;
|
||||
if (isMultiple.value && (form.isAll || !form.dir)) {
|
||||
if (isMultiple.value) {
|
||||
// Multiple mode: toggle selection
|
||||
const index = selectRows.value.findIndex((r) => r.path === fullPath);
|
||||
if (index > -1) {
|
||||
@@ -497,25 +473,22 @@ const createFolder = async (row: any) => {
|
||||
Object.assign(addItem, addForm);
|
||||
loading.value = true;
|
||||
createFile(addItem as File.FileCreate)
|
||||
.then(async () => {
|
||||
.then(() => {
|
||||
row.isCreate = false;
|
||||
disBtn.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
|
||||
await search(req);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const toggleDirectory = (path: string) => {
|
||||
const index = selectRows.value.findIndex((item) => item.path === path);
|
||||
if (index >= 0) {
|
||||
selectRows.value.splice(index, 1);
|
||||
} else {
|
||||
selectRows.value.push({ path, name: path });
|
||||
onUpdated(() => {
|
||||
if (form.path != '') {
|
||||
req.path = form.path;
|
||||
}
|
||||
};
|
||||
search(req);
|
||||
});
|
||||
|
||||
const removeSelected = (index: number) => {
|
||||
selectRows.value.splice(index, 1);
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
<el-table-column :label="$t('commons.table.name')" prop="name">
|
||||
<template #default="{ row }">
|
||||
<div v-if="!row.edit">
|
||||
<span>{{ row.name }}</span>
|
||||
<span v-if="row.name === 'Default'">
|
||||
{{ $t('commons.table.default') }}
|
||||
</span>
|
||||
<span v-if="row.name !== 'Default'">{{ row.name }}</span>
|
||||
<el-tag v-if="row.isDefault" type="success" class="ml-2" size="small">
|
||||
({{ $t('commons.table.default') }})
|
||||
</el-tag>
|
||||
|
||||
@@ -154,7 +154,7 @@ const connectionTree = computed<ConnectionTreeItem[]>(() => {
|
||||
groups.push(
|
||||
...hostTree.value.map((group): ConnectionTreeItem => ({
|
||||
id: `host-group-${group.id}`,
|
||||
label: group.label,
|
||||
label: group.label === 'Default' ? i18n.global.t('commons.table.default') : group.label,
|
||||
kind: 'group',
|
||||
children: (group.children || []).map((host) => ({
|
||||
id: `host-${host.id}`,
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
size="w-70"
|
||||
:show-close="false"
|
||||
:modal="false"
|
||||
modal-penetrable
|
||||
@opened="claim"
|
||||
>
|
||||
<template #header>
|
||||
|
||||
@@ -38,7 +38,14 @@
|
||||
</el-form-item>
|
||||
<el-form-item v-if="!form.isLocal" :label="$t('commons.table.group')" prop="groupID">
|
||||
<el-select filterable v-model="form.groupID" clearable style="width: 100%">
|
||||
<el-option v-for="item in groupList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
<div v-for="item in groupList" :key="item.id">
|
||||
<el-option
|
||||
v-if="item.name === 'Default'"
|
||||
:label="$t('commons.table.default')"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-option v-else :label="item.name" :value="item.id" />
|
||||
</div>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="!form.isLocal" :label="$t('commons.table.title')" prop="name">
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
:ref="(el: any) => store.setInstance(item.key, el)"
|
||||
@session="(id: string) => store.setSessionId(item.key, id)"
|
||||
@expired="store.onExpired(item.key)"
|
||||
@reconnect="store.reconnect(item.key)"
|
||||
/>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
@@ -31,7 +31,7 @@ const { currentNode } = useGlobalStore();
|
||||
|
||||
// session: agent side session id known (fresh or reattached)
|
||||
// expired: the agent no longer has the session; a reconnect must open a new one
|
||||
const emit = defineEmits(['session', 'expired', 'reconnect']);
|
||||
const emit = defineEmits(['session', 'expired']);
|
||||
|
||||
// Close codes of the agent's session protocol (agent/utils/terminal/session.go).
|
||||
const CLOSE_SESSION_NOT_FOUND = 4404;
|
||||
@@ -56,7 +56,6 @@ let closing = false;
|
||||
let reconnecting = false;
|
||||
let reconnectNoticeShown = false;
|
||||
let revalidating = false;
|
||||
let expired = false;
|
||||
let reconnectStartedAt = 0;
|
||||
let reconnectDelay = 1000;
|
||||
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
@@ -280,7 +279,6 @@ function resizeTerminal() {
|
||||
if (terminalElement.value.clientWidth <= 0 || terminalElement.value.clientHeight <= 0) {
|
||||
return;
|
||||
}
|
||||
if (terminalElement.value.closest('.terminal-host')) return;
|
||||
|
||||
fitAddon.fit();
|
||||
if (isWsOpen()) {
|
||||
@@ -486,7 +484,6 @@ const closeRealTerminal = (ev: CloseEvent) => {
|
||||
'31',
|
||||
ev.code === 1000 ? 'The connection has been disconnected.' : i18n.global.t('terminal.sessionExpired'),
|
||||
);
|
||||
expired = true;
|
||||
emit('expired');
|
||||
return;
|
||||
case CLOSE_ATTACHED_ELSEWHERE:
|
||||
@@ -605,13 +602,6 @@ function sendMsg(data: string, line: string = '') {
|
||||
|
||||
function onTermData(data: string) {
|
||||
if (!data) return;
|
||||
if (expired) {
|
||||
if (isEnterInputData(data)) {
|
||||
expired = false;
|
||||
emit('reconnect');
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (aiNotice.value.loading) return;
|
||||
sendMsg(data, isEnterInputData(data) ? getCurrentTerminalLine() : '');
|
||||
}
|
||||
|
||||
@@ -4,8 +4,11 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, nextTick, watch, onBeforeUnmount, ref } from 'vue';
|
||||
import echarts from '@/utils/echarts';
|
||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
const { themeConfig } = useGlobalStore();
|
||||
const isDarkTheme = ref(false);
|
||||
const PieChartRef = ref<HTMLElement>();
|
||||
let themeObserver: MutationObserver | undefined;
|
||||
let mediaQuery: MediaQueryList;
|
||||
let resizeObserver: ResizeObserver | undefined;
|
||||
let resizeFrame: number | undefined;
|
||||
|
||||
@@ -44,17 +47,16 @@ function resizeChart() {
|
||||
echarts.getInstanceByDom(PieChartRef.value)?.resize();
|
||||
}
|
||||
function getThemeColors() {
|
||||
// Match the applied page theme, including while settings are still loading.
|
||||
const isDarkTheme = document.documentElement.classList.contains('dark');
|
||||
const style = getComputedStyle(document.documentElement);
|
||||
return {
|
||||
primaryLight2: style.getPropertyValue('--panel-color-primary-light-3').trim() || '#4c8ef1',
|
||||
primaryLight1: style.getPropertyValue('--panel-color-primary').trim() || '#005eeb',
|
||||
pieBgColor: isDarkTheme ? '#434552' : '#ffffff',
|
||||
textColor: isDarkTheme ? '#ffffff' : '#0f0f0f',
|
||||
subtextColor: isDarkTheme ? '#BBBFC4' : '#646A73',
|
||||
shadowColor: isDarkTheme ? '#16191D' : 'rgba(0, 94, 235, 0.1)',
|
||||
backgroundStyleColor: isDarkTheme ? 'rgba(255, 255, 255, 0.05)' : 'rgba(0, 94, 235, 0.05)',
|
||||
primaryLight2: getComputedStyle(document.documentElement)
|
||||
.getPropertyValue('--panel-color-primary-light-3')
|
||||
.trim(),
|
||||
primaryLight1: getComputedStyle(document.documentElement).getPropertyValue('--panel-color-primary').trim(),
|
||||
pieBgColor: isDarkTheme.value ? '#434552' : '#ffffff',
|
||||
textColor: isDarkTheme.value ? '#ffffff' : '#0f0f0f',
|
||||
subtextColor: isDarkTheme.value ? '#BBBFC4' : '#646A73',
|
||||
shadowColor: isDarkTheme.value ? '#16191D' : 'rgba(0, 94, 235, 0.1)',
|
||||
backgroundStyleColor: isDarkTheme.value ? 'rgba(255, 255, 255, 0.05)' : 'rgba(0, 94, 235, 0.05)',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -62,6 +64,11 @@ function initChart() {
|
||||
if (!PieChartRef.value) {
|
||||
return;
|
||||
}
|
||||
if (themeConfig.value.theme === 'auto') {
|
||||
isDarkTheme.value = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
} else {
|
||||
isDarkTheme.value = themeConfig.value.theme === 'dark';
|
||||
}
|
||||
let myChart = echarts?.getInstanceByDom(PieChartRef.value);
|
||||
if (myChart === null || myChart === undefined) {
|
||||
myChart = echarts.init(PieChartRef.value);
|
||||
@@ -187,11 +194,8 @@ function handleThemeChange() {
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
themeObserver = new MutationObserver(handleThemeChange);
|
||||
themeObserver.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class', 'style'],
|
||||
});
|
||||
mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
mediaQuery.addEventListener('change', handleThemeChange);
|
||||
initChart();
|
||||
window.addEventListener('resize', changeChartSize);
|
||||
if (PieChartRef.value) {
|
||||
@@ -210,7 +214,7 @@ onBeforeUnmount(() => {
|
||||
echarts.getInstanceByDom(PieChartRef.value)?.dispose();
|
||||
}
|
||||
window.removeEventListener('resize', changeChartSize);
|
||||
themeObserver?.disconnect();
|
||||
mediaQuery?.removeEventListener('change', handleThemeChange);
|
||||
resizeObserver?.disconnect();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1517,14 +1517,6 @@ const message = {
|
||||
extraHeaders: 'Extra Headers',
|
||||
},
|
||||
vllmMonitor: {
|
||||
concurrency: 'Current concurrency',
|
||||
sampleMoment: 'At sampling time',
|
||||
sampleWindow: 'Sample interval',
|
||||
requestUnit: 'requests',
|
||||
processing: 'Processing',
|
||||
queued: 'Queued',
|
||||
requestTiming: 'Request timing',
|
||||
completedRequests: 'Requests completed in interval',
|
||||
tokensPerSecond: 'tokens/s',
|
||||
requestsPerSecond: 'Requests/s',
|
||||
finishReasonLabels: {
|
||||
@@ -4252,7 +4244,7 @@ const message = {
|
||||
pushDir: 'Push the certificate to the local directory',
|
||||
dir: 'Directory',
|
||||
pushDirHelper:
|
||||
'Select multiple directories or enter one directory per line. Each directory will receive "fullchain.pem" and "privkey.pem".',
|
||||
'Certificate file "fullchain.pem" and key file "privkey.pem" will be generated in this directory.',
|
||||
organizationDetail: 'Organization details',
|
||||
fromWebsite: 'From website',
|
||||
dnsMauanlHelper:
|
||||
@@ -4307,14 +4299,9 @@ const message = {
|
||||
familyIncomplete: '{0} chains are incomplete',
|
||||
resetWithBackup: 'Back up rules and reset',
|
||||
resetOnly: 'Reset only',
|
||||
resetWithBackupHelper:
|
||||
'Save the current module rules as JSON for restoration during initialization or startup.',
|
||||
resetWithBackupHelper: 'Save the current module rules as JSON for restoration during initialization.',
|
||||
resetOnlyHelper: 'Clear current rules without creating a new backup. Existing backups are kept.',
|
||||
backupDirectoryHelper: 'Valid rule backups in {0} are listed below. The newest file is selected by default.',
|
||||
startOnly: 'Start only',
|
||||
startAndRestore: 'Start and restore from backup',
|
||||
startWithBackupHelper:
|
||||
'Start the firewall normally, or choose an existing JSON backup to start and restore rules.',
|
||||
restoreAllRules: 'Initialize and restore rules',
|
||||
importRuleBackup: 'Import from backup',
|
||||
initializeOnly: 'Initialize only',
|
||||
|
||||
@@ -1526,14 +1526,6 @@ const message = {
|
||||
extraHeaders: 'Encabezados adicionales',
|
||||
},
|
||||
vllmMonitor: {
|
||||
concurrency: 'Concurrencia actual',
|
||||
sampleMoment: 'Al tomar la muestra',
|
||||
sampleWindow: 'Intervalo de muestreo',
|
||||
requestUnit: 'solicitudes',
|
||||
processing: 'En proceso',
|
||||
queued: 'En cola',
|
||||
requestTiming: 'Tiempo de las solicitudes',
|
||||
completedRequests: 'Solicitudes completadas en el intervalo',
|
||||
tokensPerSecond: 'tokens/s',
|
||||
requestsPerSecond: 'Solicitudes/s',
|
||||
finishReasonLabels: {
|
||||
@@ -4301,8 +4293,7 @@ const message = {
|
||||
domainHelper: 'Un dominio por línea, admite * y dirección IP',
|
||||
pushDir: 'Guardar certificado en el directorio local',
|
||||
dir: 'Directorio',
|
||||
pushDirHelper:
|
||||
'Seleccione varios directorios o introduzca un directorio por línea. En cada directorio se generarán los archivos "fullchain.pem" y "privkey.pem".',
|
||||
pushDirHelper: 'Se generarán los archivos "fullchain.pem" y "privkey.pem" en este directorio.',
|
||||
organizationDetail: 'Detalles de la organización',
|
||||
fromWebsite: 'Desde el sitio web',
|
||||
dnsMauanlHelper: 'En modo manual, haz clic en solicitar tras la creación para obtener el valor DNS',
|
||||
@@ -4356,14 +4347,10 @@ const message = {
|
||||
resetWithBackup: 'Guardar reglas y restablecer',
|
||||
resetOnly: 'Solo restablecer',
|
||||
resetWithBackupHelper:
|
||||
'Guarde las reglas del módulo actual en JSON para restaurarlas al inicializar o iniciar el cortafuegos.',
|
||||
'Guarda las reglas del módulo actual en JSON para restaurarlas durante la inicialización.',
|
||||
resetOnlyHelper: 'Borrar las reglas actuales sin crear una copia nueva. Se conservan las copias existentes.',
|
||||
backupDirectoryHelper:
|
||||
'Se muestran las copias válidas de reglas en {0}. Se selecciona la más reciente por defecto.',
|
||||
startOnly: 'Solo iniciar',
|
||||
startAndRestore: 'Iniciar y restaurar desde una copia de seguridad',
|
||||
startWithBackupHelper:
|
||||
'Inicie el cortafuegos normalmente o elija una copia JSON existente para iniciar y restaurar reglas.',
|
||||
restoreAllRules: 'Inicializar y restaurar reglas',
|
||||
importRuleBackup: 'Importar desde copia de seguridad',
|
||||
initializeOnly: 'Solo inicializar',
|
||||
|
||||
@@ -1502,14 +1502,6 @@ const message = {
|
||||
extraHeaders: 'هدرهای اضافی',
|
||||
},
|
||||
vllmMonitor: {
|
||||
concurrency: 'درخواستهای همزمان فعلی',
|
||||
sampleMoment: 'در لحظه نمونهبرداری',
|
||||
sampleWindow: 'بازه نمونهبرداری',
|
||||
requestUnit: 'درخواست',
|
||||
processing: 'در حال پردازش',
|
||||
queued: 'در صف',
|
||||
requestTiming: 'زمان پردازش درخواست',
|
||||
completedRequests: 'تعداد درخواستهای تکمیلشده در بازه',
|
||||
tokensPerSecond: 'tokens/ثانیه',
|
||||
requestsPerSecond: 'درخواست/ثانیه',
|
||||
finishReasonLabels: {
|
||||
@@ -4214,8 +4206,7 @@ const message = {
|
||||
domainHelper: 'هر خط یک نام دامنه، پشتیبانی از * و آدرس IP',
|
||||
pushDir: 'ارسال گواهی به دایرکتوری محلی',
|
||||
dir: 'دایرکتوری',
|
||||
pushDirHelper:
|
||||
'چند دایرکتوری انتخاب کنید یا در هر خط یک دایرکتوری وارد کنید. فایلهای "fullchain.pem" و "privkey.pem" در هر دایرکتوری ایجاد میشوند.',
|
||||
pushDirHelper: 'فایل گواهی "fullchain.pem" و فایل کلید "privkey.pem" در این دایرکتوری تولید میشوند.',
|
||||
organizationDetail: 'جزئیات سازمان',
|
||||
fromWebsite: 'از وبسایت',
|
||||
dnsMauanlHelper:
|
||||
@@ -4268,15 +4259,10 @@ const message = {
|
||||
familyIncomplete: 'زنجیرههای {0} ناقص هستند',
|
||||
resetWithBackup: 'پشتیبانگیری قوانین و بازنشانی',
|
||||
resetOnly: 'فقط بازنشانی',
|
||||
resetWithBackupHelper:
|
||||
'قوانین ماژول فعلی را بهصورت JSON ذخیره کنید تا هنگام مقداردهی اولیه یا راهاندازی بازیابی شوند.',
|
||||
resetWithBackupHelper: 'قوانین ماژول فعلی را بهصورت JSON ذخیره کنید تا هنگام مقداردهی اولیه بازیابی شوند.',
|
||||
resetOnlyHelper: 'قوانین فعلی بدون ایجاد نسخه پشتیبان جدید پاک میشوند. نسخههای پشتیبان قبلی حفظ میشوند.',
|
||||
backupDirectoryHelper:
|
||||
'نسخههای پشتیبان معتبر قوانین در {0} نمایش داده میشوند. جدیدترین فایل بهطور پیشفرض انتخاب میشود.',
|
||||
startOnly: 'فقط راه\u200cاندازی',
|
||||
startAndRestore: 'راه\u200cاندازی و بازیابی از نسخه پشتیبان',
|
||||
startWithBackupHelper:
|
||||
'فایروال را بهطور معمول راهاندازی کنید یا یک نسخه پشتیبان JSON موجود را برای راهاندازی و بازیابی قوانین انتخاب کنید.',
|
||||
restoreAllRules: 'مقداردهی اولیه و بازیابی قوانین',
|
||||
importRuleBackup: 'ورود از نسخه پشتیبان',
|
||||
initializeOnly: 'فقط راهاندازی',
|
||||
|
||||
@@ -1505,14 +1505,6 @@ const message = {
|
||||
extraHeaders: '追加ヘッダー',
|
||||
},
|
||||
vllmMonitor: {
|
||||
concurrency: '現在の同時リクエスト',
|
||||
sampleMoment: 'サンプリング時点',
|
||||
sampleWindow: '集計区間',
|
||||
requestUnit: '件',
|
||||
processing: '処理中',
|
||||
queued: '待機中',
|
||||
requestTiming: 'リクエスト処理時間',
|
||||
completedRequests: '区間内の完了リクエスト数',
|
||||
tokensPerSecond: 'tokens/秒',
|
||||
requestsPerSecond: 'リクエスト/秒',
|
||||
finishReasonLabels: {
|
||||
@@ -4237,8 +4229,7 @@ const message = {
|
||||
domainHelper: '1行ごとに1つのドメイン名、 *およびIPアドレスをサポートします',
|
||||
pushDir: '証明書をローカルディレクトリにプッシュします',
|
||||
dir: 'ディレクトリ',
|
||||
pushDirHelper:
|
||||
'複数のディレクトリを選択するか、1行に1つずつディレクトリを入力してください。各ディレクトリに「fullchain.pem」と「privkey.pem」が生成されます。',
|
||||
pushDirHelper: '証明書ファイル「FullChain.PEM」とキーファイル「Privkey.Pem」がこのディレクトリで生成されます。',
|
||||
organizationDetail: '組織の詳細',
|
||||
fromWebsite: 'ウェブサイトから',
|
||||
dnsMauanlHelper: '手動解像度モードでは、作成後に適用ボタンをクリックしてDNS解像度値を取得する必要があります',
|
||||
@@ -4291,12 +4282,9 @@ const message = {
|
||||
familyIncomplete: '{0} のチェーンが不完全です',
|
||||
resetWithBackup: 'ルールをバックアップしてリセット',
|
||||
resetOnly: 'リセットのみ',
|
||||
resetWithBackupHelper: '現在のモジュールのルールを JSON に保存し、初期化時または起動時に復元できます。',
|
||||
resetWithBackupHelper: '現在のモジュールのルールを JSON に保存し、初期化時に復元できます。',
|
||||
resetOnlyHelper: '新しいバックアップを作成せずに現在のルールを消去します。既存のバックアップは保持されます。',
|
||||
backupDirectoryHelper: '{0} 内の有効なルールバックアップを表示します。既定では最新のファイルが選択されます。',
|
||||
startOnly: '起動のみ',
|
||||
startAndRestore: '起動してバックアップから復元',
|
||||
startWithBackupHelper: '通常起動するか、既存の JSON バックアップを選んで起動とルールの復元を行います。',
|
||||
restoreAllRules: '初期化してルールを復元',
|
||||
importRuleBackup: 'バックアップからインポート',
|
||||
initializeOnly: '初期化のみ',
|
||||
|
||||
@@ -1490,14 +1490,6 @@ const message = {
|
||||
extraHeaders: '추가 헤더',
|
||||
},
|
||||
vllmMonitor: {
|
||||
concurrency: '현재 동시 요청',
|
||||
sampleMoment: '샘플링 시점',
|
||||
sampleWindow: '집계 구간',
|
||||
requestUnit: '개 요청',
|
||||
processing: '처리 중',
|
||||
queued: '대기 중',
|
||||
requestTiming: '요청 소요 시간',
|
||||
completedRequests: '구간 내 완료 요청 수',
|
||||
tokensPerSecond: 'tokens/초',
|
||||
requestsPerSecond: '요청/초',
|
||||
finishReasonLabels: {
|
||||
@@ -4159,8 +4151,7 @@ const message = {
|
||||
domainHelper: '줄당 하나의 도메인 이름, * 및 IP 주소 지원',
|
||||
pushDir: '로컬 디렉토리로 인증서 푸시',
|
||||
dir: '디렉토리',
|
||||
pushDirHelper:
|
||||
'여러 디렉터리를 선택하거나 한 줄에 하나씩 입력하세요. 각 디렉터리에 "fullchain.pem"과 "privkey.pem" 파일이 생성됩니다.',
|
||||
pushDirHelper: '인증서 파일 "fullchain.pem" 및 키 파일 "privkey.pem"이 이 디렉토리에 생성됩니다.',
|
||||
organizationDetail: '조직 세부 정보',
|
||||
fromWebsite: '웹사이트에서 가져오기',
|
||||
dnsMauanlHelper: '수동 해석 모드에서는 생성 후 신청 버튼을 클릭하여 DNS 해석 값을 얻어야 합니다.',
|
||||
@@ -4213,12 +4204,9 @@ const message = {
|
||||
familyIncomplete: '{0} 체인이 불완전합니다',
|
||||
resetWithBackup: '규칙 백업 후 초기화',
|
||||
resetOnly: '초기화만 수행',
|
||||
resetWithBackupHelper: '현재 모듈의 규칙을 JSON으로 저장하여 초기화 또는 시작 시 복원할 수 있습니다.',
|
||||
resetWithBackupHelper: '현재 모듈의 규칙을 JSON으로 저장하여 초기화 시 복원할 수 있습니다.',
|
||||
resetOnlyHelper: '새 백업 없이 현재 규칙을 삭제합니다. 기존 백업은 유지됩니다.',
|
||||
backupDirectoryHelper: '{0}의 유효한 규칙 백업을 표시합니다. 기본적으로 최신 파일이 선택됩니다.',
|
||||
startOnly: '시작만',
|
||||
startAndRestore: '시작 및 백업에서 복원',
|
||||
startWithBackupHelper: '방화벽을 일반적으로 시작하거나 기존 JSON 백업을 선택하여 시작하고 규칙을 복원합니다.',
|
||||
restoreAllRules: '초기화 및 규칙 복원',
|
||||
importRuleBackup: '백업에서 가져오기',
|
||||
initializeOnly: '초기화만 수행',
|
||||
|
||||
@@ -1484,14 +1484,6 @@ const message = {
|
||||
extraHeaders: 'Headers ເພີ່ມເຕີມ',
|
||||
},
|
||||
vllmMonitor: {
|
||||
concurrency: 'ຄຳຮ້ອງຂໍພ້ອມກັນໃນປັດຈຸບັນ',
|
||||
sampleMoment: 'ໃນເວລາເກັບຕົວຢ່າງ',
|
||||
sampleWindow: 'ຊ່ວງເວລາເກັບຕົວຢ່າງ',
|
||||
requestUnit: 'ຄຳຮ້ອງຂໍ',
|
||||
processing: 'ກຳລັງປະມວນຜົນ',
|
||||
queued: 'ກຳລັງລໍຖ້າ',
|
||||
requestTiming: 'ເວລາປະມວນຜົນຄຳຮ້ອງຂໍ',
|
||||
completedRequests: 'ຈຳນວນຄຳຮ້ອງຂໍທີ່ສຳເລັດໃນຊ່ວງເວລາ',
|
||||
tokensPerSecond: 'tokens/ວິນາທີ',
|
||||
requestsPerSecond: 'ຄຳຮ້ອງຂໍ/ວິນາທີ',
|
||||
finishReasonLabels: {
|
||||
@@ -4131,8 +4123,7 @@ const message = {
|
||||
domainHelper: 'ໜຶ່ງໂດເມນຕໍ່ແຖວ, ຮອງຮັບ * ແລະ ທີ່ຢູ່ IP',
|
||||
pushDir: 'ສົ່ງໃບຢັ້ງຢືນໄປຍັງໂຟນເດີໃນເຄື່ອງ',
|
||||
dir: 'ໂຟນເດີ',
|
||||
pushDirHelper:
|
||||
'ເລືອກຫຼາຍໂຟນເດີ ຫຼື ປ້ອນໜຶ່ງໂຟນເດີຕໍ່ແຖວ. ໄຟລ໌ "fullchain.pem" ແລະ "privkey.pem" ຈະຖືກສ້າງໃນແຕ່ລະໂຟນເດີ.',
|
||||
pushDirHelper: 'ໄຟລ໌ "fullchain.pem" ແລະ "privkey.pem" ຈະຖືກສ້າງໃນໂຟນເດີນີ້.',
|
||||
organizationDetail: 'ລາຍລະອຽດອົງກອນ',
|
||||
fromWebsite: 'ຈາກເວັບໄຊ',
|
||||
dnsMauanlHelper: 'ໃນໂໝດຈັດການ DNS ດ້ວຍຕົນເອງ, ທ່ານຕ້ອງຄລິກປຸ່ມ "ຂໍໃບຢັ້ງຢືນ" ຫຼັງຈາກສ້າງເພື່ອຮັບຄ່າ DNS',
|
||||
@@ -4182,12 +4173,9 @@ const message = {
|
||||
familyIncomplete: 'ຊຸດກົດ {0} ບໍ່ຄົບຖ້ວນ',
|
||||
resetWithBackup: 'ສຳຮອງກົດແລ້ວຣີເຊັດ',
|
||||
resetOnly: 'ຣີເຊັດເທົ່ານັ້ນ',
|
||||
resetWithBackupHelper: 'ບັນທຶກກົດຂອງໂມດູນປັດຈຸບັນເປັນ JSON ເພື່ອກູ້ຄືນເມື່ອເລີ່ມຕົ້ນ ຫຼື ເປີດໃຊ້ໄຟວໍ.',
|
||||
resetWithBackupHelper: 'ບັນທຶກກົດຂອງໂມດູນປັດຈຸບັນເປັນ JSON ເພື່ອກູ້ຄືນເມື່ອເລີ່ມຕົ້ນ.',
|
||||
resetOnlyHelper: 'ລຶບກົດປັດຈຸບັນໂດຍບໍ່ສ້າງໄຟລ໌ສຳຮອງໃໝ່. ໄຟລ໌ສຳຮອງເກົ່າຍັງຄົງຢູ່.',
|
||||
backupDirectoryHelper: 'ສະແດງໄຟລ໌ສຳຮອງກົດທີ່ໃຊ້ໄດ້ໃນ {0}. ເລືອກໄຟລ໌ຫຼ້າສຸດເປັນຄ່າເລີ່ມຕົ້ນ.',
|
||||
startOnly: 'ເລີ່ມຕົ້ນເທົ່ານັ້ນ',
|
||||
startAndRestore: 'ເລີ່ມຕົ້ນ ແລະ ກູ້ຄືນຈາກຂໍ້ມູນສຳຮອງ',
|
||||
startWithBackupHelper: 'ເປີດໄຟວໍຕາມປົກກະຕິ ຫຼື ເລືອກຂໍ້ມູນສຳຮອງ JSON ທີ່ມີຢູ່ເພື່ອເລີ່ມຕົ້ນ ແລະ ກູ້ຄືນກົດ.',
|
||||
restoreAllRules: 'ເລີ່ມຕົ້ນ ແລະ ກູ້ຄືນກົດ',
|
||||
importRuleBackup: 'ນຳເຂົ້າຈາກຂໍ້ມູນສຳຮອງ',
|
||||
initializeOnly: 'ເລີ່ມຕົ້ນເທົ່ານັ້ນ',
|
||||
|
||||
@@ -1531,14 +1531,6 @@ const message = {
|
||||
extraHeaders: 'Pengepala Tambahan',
|
||||
},
|
||||
vllmMonitor: {
|
||||
concurrency: 'Permintaan serentak semasa',
|
||||
sampleMoment: 'Pada masa pensampelan',
|
||||
sampleWindow: 'Selang pensampelan',
|
||||
requestUnit: 'permintaan',
|
||||
processing: 'Sedang diproses',
|
||||
queued: 'Dalam baris gilir',
|
||||
requestTiming: 'Tempoh permintaan',
|
||||
completedRequests: 'Permintaan selesai dalam selang masa',
|
||||
tokensPerSecond: 'tokens/s',
|
||||
requestsPerSecond: 'Permintaan/s',
|
||||
finishReasonLabels: {
|
||||
@@ -4315,8 +4307,7 @@ const message = {
|
||||
domainHelper: 'Satu nama domain setiap baris, menyokong * dan alamat IP',
|
||||
pushDir: 'Tolakkan sijil ke direktori tempatan',
|
||||
dir: 'Direktori',
|
||||
pushDirHelper:
|
||||
'Pilih beberapa direktori atau masukkan satu direktori pada setiap baris. Fail "fullchain.pem" dan "privkey.pem" akan dihasilkan dalam setiap direktori.',
|
||||
pushDirHelper: 'Fail sijil "fullchain.pem" dan fail kunci "privkey.pem" akan dihasilkan dalam direktori ini.',
|
||||
organizationDetail: 'Butiran organisasi',
|
||||
fromWebsite: 'Daripada laman web',
|
||||
dnsMauanlHelper:
|
||||
@@ -4371,15 +4362,10 @@ const message = {
|
||||
familyIncomplete: 'Rantaian {0} tidak lengkap',
|
||||
resetWithBackup: 'Sandarkan peraturan dan tetapkan semula',
|
||||
resetOnly: 'Tetapkan semula sahaja',
|
||||
resetWithBackupHelper:
|
||||
'Simpan peraturan modul semasa sebagai JSON untuk dipulihkan semasa pemulaan atau semasa tembok api dimulakan.',
|
||||
resetWithBackupHelper: 'Simpan peraturan modul semasa sebagai JSON untuk dipulihkan semasa pemulaan.',
|
||||
resetOnlyHelper: 'Padam peraturan semasa tanpa sandaran baharu. Sandaran sedia ada dikekalkan.',
|
||||
backupDirectoryHelper:
|
||||
'Sandaran peraturan yang sah dalam {0} disenaraikan di bawah. Fail terkini dipilih secara lalai.',
|
||||
startOnly: 'Mula sahaja',
|
||||
startAndRestore: 'Mula dan pulihkan daripada sandaran',
|
||||
startWithBackupHelper:
|
||||
'Mulakan tembok api seperti biasa atau pilih sandaran JSON sedia ada untuk memulakan dan memulihkan peraturan.',
|
||||
restoreAllRules: 'Mulakan dan pulihkan peraturan',
|
||||
importRuleBackup: 'Import daripada sandaran',
|
||||
initializeOnly: 'Mulakan sahaja',
|
||||
|
||||
@@ -1527,14 +1527,6 @@ const message = {
|
||||
extraHeaders: 'Cabeçalhos extras',
|
||||
},
|
||||
vllmMonitor: {
|
||||
concurrency: 'Concorrência atual',
|
||||
sampleMoment: 'No momento da coleta',
|
||||
sampleWindow: 'Intervalo de coleta',
|
||||
requestUnit: 'solicitações',
|
||||
processing: 'Em processamento',
|
||||
queued: 'Na fila',
|
||||
requestTiming: 'Tempo das solicitações',
|
||||
completedRequests: 'Solicitações concluídas no intervalo',
|
||||
tokensPerSecond: 'tokens/s',
|
||||
requestsPerSecond: 'Solicitações/s',
|
||||
finishReasonLabels: {
|
||||
@@ -4335,7 +4327,7 @@ const message = {
|
||||
pushDir: 'Enviar o certificado para o diretório local',
|
||||
dir: 'Diretório',
|
||||
pushDirHelper:
|
||||
'Selecione vários diretórios ou informe um diretório por linha. Os arquivos "fullchain.pem" e "privkey.pem" serão gerados em cada diretório.',
|
||||
'O arquivo do certificado "fullchain.pem" e o arquivo de chave "privkey.pem" serão gerados neste diretório.',
|
||||
organizationDetail: 'Detalhes da organização',
|
||||
fromWebsite: 'Do site',
|
||||
dnsMauanlHelper:
|
||||
@@ -4392,15 +4384,10 @@ const message = {
|
||||
familyIncomplete: 'As cadeias de {0} estão incompletas',
|
||||
resetWithBackup: 'Salvar regras e redefinir',
|
||||
resetOnly: 'Apenas redefinir',
|
||||
resetWithBackupHelper:
|
||||
'Salve as regras do módulo atual em JSON para restaurá-las ao inicializar ou iniciar o firewall.',
|
||||
resetWithBackupHelper: 'Salve as regras do módulo atual em JSON para restaurá-las durante a inicialização.',
|
||||
resetOnlyHelper: 'Limpar as regras atuais sem criar um novo backup. Os backups existentes são mantidos.',
|
||||
backupDirectoryHelper:
|
||||
'Os backups válidos de regras em {0} são listados abaixo. O mais recente é selecionado por padrão.',
|
||||
startOnly: 'Apenas iniciar',
|
||||
startAndRestore: 'Iniciar e restaurar do backup',
|
||||
startWithBackupHelper:
|
||||
'Inicie o firewall normalmente ou escolha um backup JSON existente para iniciar e restaurar regras.',
|
||||
restoreAllRules: 'Inicializar e restaurar regras',
|
||||
importRuleBackup: 'Importar do backup',
|
||||
initializeOnly: 'Apenas inicializar',
|
||||
|
||||
@@ -1520,14 +1520,6 @@ const message = {
|
||||
extraHeaders: 'Дополнительные заголовки',
|
||||
},
|
||||
vllmMonitor: {
|
||||
concurrency: 'Текущие параллельные запросы',
|
||||
sampleMoment: 'На момент замера',
|
||||
sampleWindow: 'Интервал замера',
|
||||
requestUnit: 'запросов',
|
||||
processing: 'В обработке',
|
||||
queued: 'В очереди',
|
||||
requestTiming: 'Время обработки запросов',
|
||||
completedRequests: 'Завершённые запросы за интервал',
|
||||
tokensPerSecond: 'tokens/с',
|
||||
requestsPerSecond: 'Запросов/с',
|
||||
finishReasonLabels: {
|
||||
@@ -4302,8 +4294,7 @@ const message = {
|
||||
domainHelper: 'Одно доменное имя в строке, поддерживает * и IP-адрес',
|
||||
pushDir: 'Отправить сертификат в локальную директорию',
|
||||
dir: 'Директория',
|
||||
pushDirHelper:
|
||||
'Выберите несколько директорий или укажите по одной директории в строке. В каждой директории будут созданы файлы "fullchain.pem" и "privkey.pem".',
|
||||
pushDirHelper: 'В этой директории будут созданы файл сертификата "fullchain.pem" и файл ключа "privkey.pem".',
|
||||
organizationDetail: 'Детали организации',
|
||||
fromWebsite: 'С веб-сайта',
|
||||
dnsMauanlHelper:
|
||||
@@ -4359,15 +4350,10 @@ const message = {
|
||||
familyIncomplete: 'Цепочки {0} неполны',
|
||||
resetWithBackup: 'Сохранить правила и сбросить',
|
||||
resetOnly: 'Только сбросить',
|
||||
resetWithBackupHelper:
|
||||
'Сохраните правила текущего модуля в JSON для восстановления при инициализации или запуске.',
|
||||
resetWithBackupHelper: 'Сохраните правила текущего модуля в JSON для восстановления при инициализации.',
|
||||
resetOnlyHelper: 'Удалить текущие правила без создания новой копии. Существующие копии сохраняются.',
|
||||
backupDirectoryHelper:
|
||||
'Ниже перечислены действительные резервные копии правил из {0}. По умолчанию выбрана самая новая.',
|
||||
startOnly: 'Только запуск',
|
||||
startAndRestore: 'Запустить и восстановить из резервной копии',
|
||||
startWithBackupHelper:
|
||||
'Запустите брандмауэр как обычно или выберите существующую копию JSON для запуска и восстановления правил.',
|
||||
restoreAllRules: 'Инициализировать и восстановить правила',
|
||||
importRuleBackup: 'Импортировать из резервной копии',
|
||||
initializeOnly: 'Только инициализировать',
|
||||
|
||||
@@ -1527,14 +1527,6 @@ const message = {
|
||||
extraHeaders: 'Ek Başlıklar',
|
||||
},
|
||||
vllmMonitor: {
|
||||
concurrency: 'Mevcut eşzamanlı istekler',
|
||||
sampleMoment: 'Örnekleme anında',
|
||||
sampleWindow: 'Örnekleme aralığı',
|
||||
requestUnit: 'istek',
|
||||
processing: 'İşleniyor',
|
||||
queued: 'Kuyrukta',
|
||||
requestTiming: 'İstek süreleri',
|
||||
completedRequests: 'Aralıkta tamamlanan istek sayısı',
|
||||
tokensPerSecond: 'tokens/sn',
|
||||
requestsPerSecond: 'İstek/sn',
|
||||
finishReasonLabels: {
|
||||
@@ -4321,7 +4313,7 @@ const message = {
|
||||
pushDir: 'Sertifikayı yerel dizine aktar',
|
||||
dir: 'Dizin',
|
||||
pushDirHelper:
|
||||
'Birden fazla dizin seçin veya her satıra bir dizin girin. Her dizinde "fullchain.pem" ve "privkey.pem" dosyaları oluşturulacaktır.',
|
||||
'Sertifika dosyası "fullchain.pem" ve anahtar dosyası "privkey.pem" bu dizinde oluşturulacaktır.',
|
||||
organizationDetail: 'Organizasyon detayları',
|
||||
fromWebsite: 'Web sitesinden',
|
||||
dnsMauanlHelper:
|
||||
@@ -4376,15 +4368,10 @@ const message = {
|
||||
familyIncomplete: '{0} zincirleri eksik',
|
||||
resetWithBackup: 'Kuralları yedekle ve sıfırla',
|
||||
resetOnly: 'Yalnızca sıfırla',
|
||||
resetWithBackupHelper:
|
||||
'Geçerli modül kurallarını ilk kurulumda veya güvenlik duvarı başlatılırken geri yüklemek için JSON olarak kaydedin.',
|
||||
resetWithBackupHelper: 'Geçerli modül kurallarını başlatma sırasında geri yüklemek için JSON olarak kaydedin.',
|
||||
resetOnlyHelper: 'Yeni yedek oluşturmadan mevcut kuralları temizleyin. Önceki yedekler korunur.',
|
||||
backupDirectoryHelper:
|
||||
'{0} içindeki geçerli kural yedekleri listelenir. Varsayılan olarak en yeni dosya seçilir.',
|
||||
startOnly: 'Yalnızca başlat',
|
||||
startAndRestore: 'Başlat ve yedekten geri yükle',
|
||||
startWithBackupHelper:
|
||||
'Güvenlik duvarını normal başlatın veya başlatıp kuralları geri yüklemek için mevcut bir JSON yedeği seçin.',
|
||||
restoreAllRules: 'Başlat ve kuralları geri yükle',
|
||||
importRuleBackup: 'Yedekten içe aktar',
|
||||
initializeOnly: 'Yalnızca başlat',
|
||||
|
||||
@@ -1437,14 +1437,6 @@ const message = {
|
||||
extraHeaders: '額外請求標頭',
|
||||
},
|
||||
vllmMonitor: {
|
||||
concurrency: '目前並行請求',
|
||||
sampleMoment: '取樣時刻',
|
||||
sampleWindow: '統計區間',
|
||||
requestUnit: '個請求',
|
||||
processing: '處理中',
|
||||
queued: '排隊中',
|
||||
requestTiming: '請求耗時',
|
||||
completedRequests: '區間完成請求數',
|
||||
tokensPerSecond: 'tokens/秒',
|
||||
requestsPerSecond: '請求/秒',
|
||||
finishReasonLabels: {
|
||||
@@ -3970,7 +3962,7 @@ const message = {
|
||||
domainHelper: '一行一個網域名稱,支援*和IP位址',
|
||||
pushDir: '推送憑證到本機目錄',
|
||||
dir: '目錄',
|
||||
pushDirHelper: '支援選擇多個目錄,也可每行填寫一個目錄。每個目錄下都會產生 fullchain.pem 和 privkey.pem。',
|
||||
pushDirHelper: '會在此目錄下產生兩個檔案,憑證檔案:fullchain.pem 金鑰檔案:privkey.pem',
|
||||
organizationDetail: '機構詳情',
|
||||
fromWebsite: '從網站取得',
|
||||
dnsMauanlHelper: '手動解析模式需要在建立完之後點選申請按鈕取得 DNS 解析值',
|
||||
@@ -4018,12 +4010,9 @@ const message = {
|
||||
familyIncomplete: '{0} 鏈不完整',
|
||||
resetWithBackup: '備份規則後重置',
|
||||
resetOnly: '僅重置',
|
||||
resetWithBackupHelper: '將目前模組的規則儲存為 JSON,之後可在初始化或啟動時選擇備份還原。',
|
||||
resetWithBackupHelper: '將目前模組的規則儲存為 JSON,之後可在初始化時選擇備份還原。',
|
||||
resetOnlyHelper: '不建立新備份,直接清理目前規則;保留既有歷史備份。',
|
||||
backupDirectoryHelper: '自動讀取 {0} 下的有效規則備份,預設選擇最新檔案。',
|
||||
startOnly: '僅啟動',
|
||||
startAndRestore: '啟動並從備份還原',
|
||||
startWithBackupHelper: '可正常啟動防火牆,或選擇現有的 JSON 備份啟動並還原規則。',
|
||||
restoreAllRules: '初始化並還原規則',
|
||||
importRuleBackup: '從備份匯入',
|
||||
initializeOnly: '僅初始化',
|
||||
|
||||
@@ -1450,14 +1450,6 @@ const message = {
|
||||
extraHeaders: '额外请求头',
|
||||
},
|
||||
vllmMonitor: {
|
||||
concurrency: '当前并发',
|
||||
sampleMoment: '采样时刻',
|
||||
sampleWindow: '统计区间',
|
||||
requestUnit: '个请求',
|
||||
processing: '处理中',
|
||||
queued: '排队中',
|
||||
requestTiming: '请求耗时',
|
||||
completedRequests: '区间完成请求数',
|
||||
tokensPerSecond: 'tokens/秒',
|
||||
requestsPerSecond: '请求/秒',
|
||||
finishReasonLabels: {
|
||||
@@ -4015,8 +4007,7 @@ const message = {
|
||||
domainHelper: '一行一个域名,支持*和IP地址',
|
||||
pushDir: '推送证书到本地目录',
|
||||
dir: '目录',
|
||||
pushDirHelper:
|
||||
'支持选择多个目录,也可每行填写一个目录。每个目录下都会生成证书文件 fullchain.pem 和密钥文件 privkey.pem',
|
||||
pushDirHelper: '会在此目录下生成两个文件,证书文件:fullchain.pem 密钥文件:privkey.pem',
|
||||
organizationDetail: '机构详情',
|
||||
fromWebsite: '从网站中获取',
|
||||
dnsMauanlHelper: '手动解析模式需要在创建完之后点击申请按钮获取 DNS 解析值',
|
||||
@@ -4064,12 +4055,9 @@ const message = {
|
||||
familyIncomplete: '{0} 链不完整',
|
||||
resetWithBackup: '备份规则后重置',
|
||||
resetOnly: '仅重置',
|
||||
resetWithBackupHelper: '将当前模块的规则保存为 JSON,后续可在初始化或启动时选择备份恢复。',
|
||||
resetWithBackupHelper: '将当前模块的规则保存为 JSON,后续可在初始化时选择备份恢复。',
|
||||
resetOnlyHelper: '不生成新备份,直接清理当前规则;已有历史备份保留。',
|
||||
backupDirectoryHelper: '自动读取 {0} 下的有效规则备份,默认选择最新文件。',
|
||||
startOnly: '仅启动',
|
||||
startAndRestore: '启动并从备份恢复',
|
||||
startWithBackupHelper: '可正常启动防火墙,或选择已有的 JSON 备份启动并恢复规则。',
|
||||
restoreAllRules: '初始化并恢复规则',
|
||||
importRuleBackup: '从备份导入',
|
||||
initializeOnly: '仅初始化',
|
||||
|
||||
@@ -163,6 +163,9 @@ const displayNodeName = (item: NodeItem) => {
|
||||
};
|
||||
|
||||
const displayGroupName = (item: NodeItem) => {
|
||||
if (item.groupBelong === 'Default') {
|
||||
return i18n.global.t('commons.table.default');
|
||||
}
|
||||
return item.group || item.groupBelong || i18n.global.t('commons.table.default');
|
||||
};
|
||||
|
||||
|
||||
@@ -45,7 +45,14 @@
|
||||
<TableViewSwitch v-model="viewMode" storage-key="cronjob" />
|
||||
<el-select v-model="searchGroupID" @change="search()" clearable class="p-w-200">
|
||||
<template #prefix>{{ $t('commons.table.group') }}</template>
|
||||
<el-option v-for="item in groupOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||
<div v-for="item in groupOptions" :key="item.id">
|
||||
<el-option
|
||||
v-if="item.name === 'Default'"
|
||||
:label="$t('commons.table.default')"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-option v-else :label="item.name" :value="item.id" />
|
||||
</div>
|
||||
</el-select>
|
||||
<TableSearch @search="search()" v-model:searchName="searchName" />
|
||||
<TableRefresh @search="search()" />
|
||||
@@ -97,14 +104,16 @@
|
||||
@change="updateGroup(row)"
|
||||
>
|
||||
<template #read>
|
||||
{{ row.groupBelong }}
|
||||
{{ row.groupBelong === 'Default' ? $t('commons.table.default') : row.groupBelong }}
|
||||
</template>
|
||||
<el-option
|
||||
v-for="item in groupOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
<div v-for="item in groupOptions" :key="item.id">
|
||||
<el-option
|
||||
v-if="item.name === 'Default'"
|
||||
:label="$t('commons.table.default')"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-option v-else :label="item.name" :value="item.id" />
|
||||
</div>
|
||||
</fu-select-rw-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -465,10 +474,17 @@ const loadGroups = async () => {
|
||||
searchGroupID.value = undefined;
|
||||
paginationConfig.currentPage = 1;
|
||||
}
|
||||
defaultGroupID.value = groupOptions.value.find((group) => group.isDefault)?.id;
|
||||
for (const group of groupOptions.value) {
|
||||
if (group.name === 'Default') {
|
||||
defaultGroupID.value = group.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (const item of data.value) {
|
||||
if (item.groupID === 0) {
|
||||
item.groupBelong = 'Default';
|
||||
item.groupID = defaultGroupID.value;
|
||||
continue;
|
||||
}
|
||||
let hasGroup = false;
|
||||
for (const group of groupOptions.value) {
|
||||
|
||||
@@ -80,12 +80,14 @@
|
||||
<LayoutCol>
|
||||
<el-form-item :label="$t('commons.table.group')" prop="groupID">
|
||||
<el-select filterable v-model="form.groupID" clearable>
|
||||
<el-option
|
||||
v-for="item in groupOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
<div v-for="item in groupOptions" :key="item.id">
|
||||
<el-option
|
||||
v-if="item.name === 'Default'"
|
||||
:label="$t('commons.table.default')"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-option v-else :label="item.name" :value="item.id" />
|
||||
</div>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</LayoutCol>
|
||||
|
||||
@@ -45,7 +45,14 @@
|
||||
<template #rightToolBar>
|
||||
<el-select v-model="group" @change="search()" clearable class="p-w-200">
|
||||
<template #prefix>{{ $t('commons.table.group') }}</template>
|
||||
<el-option v-for="item in groupOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||
<div v-for="item in groupOptions" :key="item.id">
|
||||
<el-option
|
||||
v-if="item.name === 'Default'"
|
||||
:label="$t('commons.table.default')"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-option v-else :label="item.name" :value="item.id" />
|
||||
</div>
|
||||
</el-select>
|
||||
<TableSearch @search="search()" v-model:searchName="searchInfo" />
|
||||
<TableRefresh @search="search()" />
|
||||
@@ -62,12 +69,7 @@
|
||||
:heightDiff="300"
|
||||
>
|
||||
<el-table-column type="selection" fix />
|
||||
<el-table-column
|
||||
:label="$t('commons.table.name')"
|
||||
show-overflow-tooltip
|
||||
prop="name"
|
||||
min-width="140"
|
||||
>
|
||||
<el-table-column :label="$t('commons.table.name')" show-overflow-tooltip prop="name" min-width="60">
|
||||
<template #default="{ row }">
|
||||
<el-text type="primary" class="cursor-pointer" @click="showScript(row)">
|
||||
{{ row.name }}
|
||||
@@ -76,15 +78,21 @@
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('cronjob.library.isInteractive')" prop="isInteractive" min-width="60">
|
||||
<template #default="{ row }">
|
||||
{{ $t(row.isInteractive ? 'commons.true' : 'commons.false') }}
|
||||
<div class="-mb-1">
|
||||
<el-icon v-if="row.isInteractive"><Check /></el-icon>
|
||||
<el-icon v-else><Minus /></el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.table.group')" min-width="100" prop="group">
|
||||
<el-table-column :label="$t('commons.table.group')" min-width="120" prop="group">
|
||||
<template #default="{ row }">
|
||||
<el-button class="mr-3" size="small" v-if="row.isSystem">{{ $t('menu.system') }}</el-button>
|
||||
<span v-if="row.groupBelong">
|
||||
<el-button size="small" v-for="(item, index) in row.groupBelong" :key="index">
|
||||
{{ item }}
|
||||
<span v-if="item === 'Default'">
|
||||
{{ $t('commons.table.default') }}
|
||||
</span>
|
||||
<span v-else>{{ item }}</span>
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@@ -21,7 +21,14 @@
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.table.group')" prop="groupList">
|
||||
<el-select filterable v-model="dialogData.rowData!.groupList" multiple>
|
||||
<el-option v-for="item in groupOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||
<div v-for="item in groupOptions" :key="item.id">
|
||||
<el-option
|
||||
v-if="item.name === 'Default'"
|
||||
:label="$t('commons.table.default')"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-option v-else :label="item.name" :value="item.id" />
|
||||
</div>
|
||||
</el-select>
|
||||
<span class="input-help">{{ $t('cronjob.library.groupHelper') }}</span>
|
||||
</el-form-item>
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
import { ref, nextTick } from 'vue';
|
||||
import Terminal from '@/components/terminal/index.vue';
|
||||
import { getCommandTree } from '@/api/modules/command';
|
||||
import i18n from '@/lang';
|
||||
|
||||
const terminalVisible = ref(false);
|
||||
const terminalRef = ref<InstanceType<typeof Terminal> | null>(null);
|
||||
@@ -63,6 +64,11 @@ const initTerm = async (cwd: string) => {
|
||||
const loadCommandTree = async () => {
|
||||
const res = await getCommandTree('command');
|
||||
commandTree.value = res.data || [];
|
||||
for (const item of commandTree.value) {
|
||||
if (item.label === 'Default') {
|
||||
item.label = i18n.global.t('commons.table.default');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function quickInput(val: Array<string>) {
|
||||
|
||||
@@ -149,97 +149,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.firewall-initialize-dialog.el-dialog,
|
||||
.firewall-lifecycle-dialog.el-dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: min(680px, calc(100vw - 32px));
|
||||
max-height: calc(92dvh - 24px);
|
||||
margin-top: max(24px, 8vh);
|
||||
margin-bottom: 24px;
|
||||
|
||||
.el-dialog__body {
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.firewall-rule-dialog__content {
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.firewall-initialize-dialog__description {
|
||||
margin: 0;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 13px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.firewall-rule-dialog__docker {
|
||||
padding: 14px 16px;
|
||||
border-radius: 8px;
|
||||
background: var(--el-fill-color-light);
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.el-form-item__label {
|
||||
height: auto;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.el-form-item__content {
|
||||
display: block;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.el-radio-group {
|
||||
gap: 4px 24px;
|
||||
}
|
||||
|
||||
.el-radio {
|
||||
height: auto;
|
||||
min-height: 28px;
|
||||
margin-right: 0;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.firewall-rule-dialog__docker-helper {
|
||||
margin: 6px 0 0;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.firewall-rule-dialog__confirmation {
|
||||
padding: 14px 0 16px;
|
||||
color: var(--el-text-color-regular);
|
||||
text-align: left;
|
||||
font-size: 13px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.firewall-rule-dialog__files {
|
||||
max-height: min(240px, 30vh);
|
||||
}
|
||||
|
||||
.firewall-rule-dialog__modes {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.firewall-initialize-dialog__backups {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
border-top: 1px solid var(--el-border-color-lighter);
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.firewall-rule-dialog__file-name > .min-w-0 {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,12 @@
|
||||
<template>
|
||||
<DialogPro
|
||||
v-model="visible"
|
||||
class="firewall-rule-dialog firewall-initialize-dialog"
|
||||
:title="
|
||||
starting
|
||||
? $t('firewall.firewallHelper', [$t('commons.button.start')])
|
||||
: initialize
|
||||
? $t('commons.button.init')
|
||||
: $t('firewall.importRuleBackup')
|
||||
"
|
||||
:show-close="!loading"
|
||||
:close-on-press-escape="!loading"
|
||||
class="firewall-rule-dialog"
|
||||
:title="initialize ? $t('commons.button.init') : $t('firewall.importRuleBackup')"
|
||||
>
|
||||
<template #content>
|
||||
<div v-loading="loading" class="firewall-rule-dialog__content">
|
||||
<slot name="before-options" />
|
||||
<p v-if="message && starting" class="firewall-initialize-dialog__description">{{ message }}</p>
|
||||
<el-alert v-else-if="message" :title="message" type="warning" :closable="false" show-icon />
|
||||
<el-alert v-if="message" :title="message" type="warning" :closable="false" show-icon />
|
||||
<el-alert
|
||||
v-if="backupLoadFailed"
|
||||
:title="$t('firewall.ruleBackupLoadFailed')"
|
||||
@@ -34,61 +24,56 @@
|
||||
class="firewall-rule-dialog__modes"
|
||||
:disabled="loading"
|
||||
>
|
||||
<el-radio value="initialize" border>
|
||||
<span class="firewall-rule-dialog__choice-title">{{ initializeLabel }}</span>
|
||||
</el-radio>
|
||||
<el-radio value="restore" border>
|
||||
<span class="firewall-rule-dialog__choice-title">{{ restoreLabel }}</span>
|
||||
<el-radio value="restore" border :disabled="!loaded || backups.files.length === 0">
|
||||
{{ $t('firewall.restoreAllRules') }}
|
||||
</el-radio>
|
||||
<el-radio value="initialize" border>{{ $t('firewall.initializeOnly') }}</el-radio>
|
||||
</el-radio-group>
|
||||
<div v-if="loaded && mode === 'restore'" class="firewall-rule-dialog__directory">
|
||||
<div v-if="loaded" class="firewall-rule-dialog__directory">
|
||||
<el-icon><FolderOpened /></el-icon>
|
||||
<span>{{ $t('firewall.backupDirectoryHelper', [backups.directory]) }}</span>
|
||||
</div>
|
||||
<div v-if="mode === 'restore'" class="firewall-initialize-dialog__backups">
|
||||
<el-radio-group
|
||||
v-if="backups.files.length"
|
||||
v-model="selected"
|
||||
class="firewall-rule-dialog__files"
|
||||
:disabled="loading"
|
||||
:aria-label="$t('firewall.importRuleBackup')"
|
||||
>
|
||||
<el-radio v-for="file in backups.files" :key="file.name" :value="file.name" border>
|
||||
<span class="firewall-rule-dialog__file-name">
|
||||
<el-icon><Document /></el-icon>
|
||||
<span class="min-w-0">{{ file.name }}</span>
|
||||
<el-button
|
||||
v-permission
|
||||
class="shrink-0"
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
:disabled="loading"
|
||||
:aria-label="`${$t('firewall.downloadRuleBackup')}: ${file.name}`"
|
||||
@click.stop.prevent="
|
||||
downloadFile(`${backups.directory}/${file.name}`, globalStore.currentNode)
|
||||
"
|
||||
>
|
||||
{{ $t('commons.button.download') }}
|
||||
</el-button>
|
||||
</span>
|
||||
<span class="firewall-rule-dialog__file-meta">
|
||||
<el-tag size="small" type="info" effect="plain">{{ file.provider }}</el-tag>
|
||||
<span>{{ $t('firewall.backupRuleCount') }}: {{ file.ruleCount }}</span>
|
||||
<span>{{ new Date(file.modifiedAt).toLocaleString() }}</span>
|
||||
</span>
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
<el-empty
|
||||
v-else-if="!initialize && loaded && !backups.files.length"
|
||||
:description="$t('firewall.noRuleBackup')"
|
||||
:image-size="64"
|
||||
/>
|
||||
</div>
|
||||
<el-radio-group
|
||||
v-if="mode === 'restore' && backups.files.length"
|
||||
v-model="selected"
|
||||
class="firewall-rule-dialog__files"
|
||||
:disabled="loading"
|
||||
:aria-label="$t('firewall.importRuleBackup')"
|
||||
>
|
||||
<el-radio v-for="file in backups.files" :key="file.name" :value="file.name" border>
|
||||
<span class="firewall-rule-dialog__file-name">
|
||||
<el-icon><Document /></el-icon>
|
||||
<span class="min-w-0">{{ file.name }}</span>
|
||||
<el-button
|
||||
v-permission
|
||||
class="shrink-0"
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
:disabled="loading"
|
||||
:aria-label="`${$t('firewall.downloadRuleBackup')}: ${file.name}`"
|
||||
@click.stop.prevent="
|
||||
downloadFile(`${backups.directory}/${file.name}`, globalStore.currentNode)
|
||||
"
|
||||
>
|
||||
{{ $t('commons.button.download') }}
|
||||
</el-button>
|
||||
</span>
|
||||
<span class="firewall-rule-dialog__file-meta">
|
||||
<el-tag size="small" type="info" effect="plain">{{ file.provider }}</el-tag>
|
||||
<span>{{ $t('firewall.backupRuleCount') }}: {{ file.ruleCount }}</span>
|
||||
<span>{{ new Date(file.modifiedAt).toLocaleString() }}</span>
|
||||
</span>
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
<el-empty
|
||||
v-else-if="loaded && !backups.files.length"
|
||||
:description="$t('firewall.noRuleBackup')"
|
||||
:image-size="64"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<slot name="confirmation" />
|
||||
<el-button :disabled="loading" @click="visible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@@ -96,7 +81,13 @@
|
||||
:disabled="loading || (mode === 'restore' && (!loaded || !selected))"
|
||||
@click="submit"
|
||||
>
|
||||
{{ $t('commons.button.confirm') }}
|
||||
{{
|
||||
initialize
|
||||
? mode === 'restore'
|
||||
? $t('firewall.restoreAllRules')
|
||||
: $t('firewall.initializeOnly')
|
||||
: $t('commons.button.import')
|
||||
}}
|
||||
</el-button>
|
||||
</template>
|
||||
</DialogPro>
|
||||
@@ -104,8 +95,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
import { ref } from 'vue';
|
||||
import { Document, FolderOpened } from '@element-plus/icons-vue';
|
||||
import DialogPro from '@/components/dialog-pro/index.vue';
|
||||
import { Firewall } from '@/api/interface/firewall';
|
||||
@@ -120,24 +110,13 @@ import { GlobalStore } from '@/store';
|
||||
import { downloadFile } from '@/utils/file';
|
||||
|
||||
const globalStore = GlobalStore();
|
||||
const emit = defineEmits<{
|
||||
(event: 'initialize'): void;
|
||||
(event: 'restore', backupFile: string): void;
|
||||
(event: 'complete'): void;
|
||||
}>();
|
||||
const emit = defineEmits<{ (event: 'initialize'): void; (event: 'complete'): void }>();
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const loaded = ref(false);
|
||||
const backupLoadFailed = ref(false);
|
||||
const message = ref('');
|
||||
const initialize = ref(true);
|
||||
const starting = ref(false);
|
||||
const initializeLabel = computed(() =>
|
||||
i18n.global.t(starting.value ? 'firewall.startOnly' : 'firewall.initializeOnly'),
|
||||
);
|
||||
const restoreLabel = computed(() =>
|
||||
i18n.global.t(starting.value ? 'firewall.startAndRestore' : 'firewall.restoreAllRules'),
|
||||
);
|
||||
const subsystem = ref<Firewall.BackendSubsystem>('system');
|
||||
const mode = ref<'restore' | 'initialize'>('initialize');
|
||||
const selected = ref('');
|
||||
@@ -148,10 +127,8 @@ const acceptParams = async (
|
||||
warning: string,
|
||||
withInitialization = true,
|
||||
target: Firewall.BackendSubsystem = 'system',
|
||||
start = false,
|
||||
) => {
|
||||
subsystem.value = target;
|
||||
starting.value = start;
|
||||
initialize.value = withInitialization;
|
||||
message.value = warning;
|
||||
visible.value = true;
|
||||
@@ -169,7 +146,7 @@ const loadBackups = async () => {
|
||||
try {
|
||||
backups.value = (await listFirewallRuleBackups(subsystem.value)).data;
|
||||
selected.value = backups.value.files[0]?.name || '';
|
||||
mode.value = !starting.value && (selected.value || !initialize.value) ? 'restore' : 'initialize';
|
||||
mode.value = selected.value || !initialize.value ? 'restore' : 'initialize';
|
||||
loaded.value = true;
|
||||
} catch {
|
||||
backupLoadFailed.value = true;
|
||||
@@ -186,11 +163,6 @@ const submit = async () => {
|
||||
return;
|
||||
}
|
||||
if (!loaded.value || !selected.value) return;
|
||||
if (starting.value) {
|
||||
visible.value = false;
|
||||
emit('restore', selected.value);
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const response =
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
v-node-admin
|
||||
type="primary"
|
||||
:disabled="lifecycleBusy"
|
||||
@click="onInit"
|
||||
@click="onOperate('start')"
|
||||
link
|
||||
>
|
||||
{{ $t('commons.button.start') }}
|
||||
@@ -145,58 +145,18 @@
|
||||
:title="$t('firewall.directBackendConflictWarning', [backendName, baseInfo.conflictBackend])"
|
||||
/>
|
||||
</div>
|
||||
<DialogPro
|
||||
v-model="operationVisible"
|
||||
class="firewall-rule-dialog firewall-lifecycle-dialog"
|
||||
<DockerRestart
|
||||
ref="dockerRef"
|
||||
v-model:withDockerRestart="withDockerRestart"
|
||||
@submit="onSubmit"
|
||||
:title="$t('firewall.firewallHelper', [$t('commons.button.' + operation)])"
|
||||
>
|
||||
<template #content>
|
||||
<el-form class="firewall-rule-dialog__docker" label-position="top" @submit.prevent>
|
||||
<el-form-item :label="$t('firewall.dockerRestart')">
|
||||
<el-radio-group v-model="withDockerRestart">
|
||||
<el-radio :value="true">{{ $t('setting.restartNow') }}</el-radio>
|
||||
<el-radio :value="false">{{ $t('setting.restartLater') }}</el-radio>
|
||||
</el-radio-group>
|
||||
<p class="firewall-rule-dialog__docker-helper">
|
||||
{{ $t(withDockerRestart ? 'xpack.node.syncProxyHelper1' : 'xpack.node.syncProxyHelper2') }}
|
||||
</p>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #helper>
|
||||
<span>{{ $t('firewall.' + operation + 'FirewallHelper') }}</span>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="firewall-rule-dialog__confirmation">
|
||||
<span>{{ $t('firewall.' + operation + 'FirewallHelper') }}</span>
|
||||
</div>
|
||||
<el-button @click="operationVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="onSubmit">{{ $t('commons.button.confirm') }}</el-button>
|
||||
</template>
|
||||
</DialogPro>
|
||||
</DockerRestart>
|
||||
<TaskLog ref="taskLogRef" @close="handleInitializationTaskClose" />
|
||||
<RuleInitialize
|
||||
ref="initializeRef"
|
||||
@initialize="initializeOnly"
|
||||
@restore="onOperate('start', $event)"
|
||||
@complete="loadBaseInfo"
|
||||
>
|
||||
<template v-if="isServiceBackend && dockerStatus" #before-options>
|
||||
<el-form class="firewall-rule-dialog__docker" label-position="top" @submit.prevent>
|
||||
<el-form-item :label="$t('firewall.dockerRestart')">
|
||||
<el-radio-group v-model="withDockerRestart">
|
||||
<el-radio :value="true">{{ $t('setting.restartNow') }}</el-radio>
|
||||
<el-radio :value="false">{{ $t('setting.restartLater') }}</el-radio>
|
||||
</el-radio-group>
|
||||
<p class="firewall-rule-dialog__docker-helper">
|
||||
{{ $t(withDockerRestart ? 'xpack.node.syncProxyHelper1' : 'xpack.node.syncProxyHelper2') }}
|
||||
</p>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
<template v-if="isServiceBackend" #confirmation>
|
||||
<div class="firewall-rule-dialog__confirmation">
|
||||
<span>{{ $t('firewall.startFirewallHelper') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</RuleInitialize>
|
||||
<RuleInitialize ref="initializeRef" @initialize="initializeOnly" @complete="loadBaseInfo" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -211,7 +171,7 @@ import {
|
||||
} from '@/api/modules/firewall';
|
||||
import i18n from '@/lang';
|
||||
import NoSuchService from '@/components/layout-content/no-such-service.vue';
|
||||
import DialogPro from '@/components/dialog-pro/index.vue';
|
||||
import DockerRestart from '@/components/docker-proxy/docker-restart.vue';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { computed, nextTick, onBeforeUnmount, ref } from 'vue';
|
||||
@@ -227,7 +187,7 @@ import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
const props = defineProps({
|
||||
currentTab: String,
|
||||
});
|
||||
const { docsUrl, isIntl } = useGlobalStore();
|
||||
const { docsUrl } = useGlobalStore();
|
||||
|
||||
const goToFirewallSetting = () => routerToName('FirewallSetting');
|
||||
const goToScriptLibrary = () => routerToNameWithQuery('Library', { uncached: 'true' });
|
||||
@@ -246,9 +206,8 @@ const baseInfo = ref<Firewall.FirewallBase>({
|
||||
ipv4: { available: false, initialized: false, bound: false },
|
||||
ipv6: { available: false, initialized: false, bound: false },
|
||||
});
|
||||
const operationVisible = ref(false);
|
||||
const dockerRef = ref();
|
||||
const operation = ref('restart');
|
||||
const restoreBackupFile = ref('');
|
||||
const dockerStatus = ref();
|
||||
const withDockerRestart = ref(false);
|
||||
const taskLogRef = ref();
|
||||
@@ -317,7 +276,7 @@ const familyActions = computed(() =>
|
||||
message: familyIssueText(issue),
|
||||
helpUrl:
|
||||
issue.available && issue.reason === 'ipv6_ra_required'
|
||||
? `${docsUrl.value}/user_manual/${isIntl.value ? 'system' : 'hosts'}/firewall`
|
||||
? `${docsUrl.value}/user_manual/hosts/firewall`
|
||||
: undefined,
|
||||
action,
|
||||
};
|
||||
@@ -406,20 +365,12 @@ const loadDocker = async () => {
|
||||
const initializeRef = ref<InstanceType<typeof RuleInitialize>>();
|
||||
|
||||
const onInit = async () => {
|
||||
if (lifecycleBusy.value) return;
|
||||
if (isServiceBackend.value) withDockerRestart.value = !!dockerStatus.value;
|
||||
const name = baseInfo.value.name || backendName.value;
|
||||
const message = isServiceBackend.value
|
||||
? i18n.global.t('firewall.startWithBackupHelper')
|
||||
: props.currentTab === 'base' && baseInfo.value.conflictBackend
|
||||
? i18n.global.t('firewall.initDirectBackendConflictMsg', [name, baseInfo.value.conflictBackend])
|
||||
: i18n.global.t('firewall.initMsg', [name]);
|
||||
await initializeRef.value?.acceptParams(
|
||||
message,
|
||||
true,
|
||||
props.currentTab === 'forward' ? 'forwarding' : 'system',
|
||||
isServiceBackend.value,
|
||||
);
|
||||
const message =
|
||||
props.currentTab === 'base' && baseInfo.value.conflictBackend
|
||||
? i18n.global.t('firewall.initDirectBackendConflictMsg', [name, baseInfo.value.conflictBackend])
|
||||
: i18n.global.t('firewall.initMsg', [name]);
|
||||
await initializeRef.value?.acceptParams(message, true, props.currentTab === 'forward' ? 'forwarding' : 'system');
|
||||
};
|
||||
|
||||
const initializeOnly = async () => {
|
||||
@@ -471,22 +422,17 @@ const onUnBind = async () => {
|
||||
await loadBaseInfo();
|
||||
};
|
||||
|
||||
const onOperate = async (op: string, backupFile = '') => {
|
||||
const onOperate = async (op: string) => {
|
||||
if (lifecycleBusy.value) return;
|
||||
operation.value = op;
|
||||
restoreBackupFile.value = backupFile;
|
||||
if (op === 'start') {
|
||||
await submitLifecycleOperation(!!dockerStatus.value && withDockerRestart.value);
|
||||
} else if (backendName.value === 'iptables' || backendName.value === 'nftables' || !dockerStatus.value) {
|
||||
if (backendName.value === 'iptables' || backendName.value === 'nftables' || !dockerStatus.value) {
|
||||
await submitLifecycleOperation(false);
|
||||
} else {
|
||||
withDockerRestart.value = true;
|
||||
operationVisible.value = true;
|
||||
dockerRef.value.acceptParams({ title: i18n.global.t('firewall.dockerRestart') });
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
operationVisible.value = false;
|
||||
await submitLifecycleOperation(withDockerRestart.value);
|
||||
};
|
||||
|
||||
@@ -501,7 +447,7 @@ const submitLifecycleOperation = async (restartDocker: boolean) => {
|
||||
lifecycleSubmitting.value = true;
|
||||
emit('update:loading', true);
|
||||
try {
|
||||
const result = (await operateFire(operation.value, restartDocker, restoreBackupFile.value || undefined)).data;
|
||||
const result = (await operateFire(operation.value, restartDocker)).data;
|
||||
if (disposed) return;
|
||||
if (result?.queued && result.taskID) {
|
||||
baseInfo.value.lifecycleTaskID = result.taskID;
|
||||
|
||||
@@ -27,7 +27,10 @@
|
||||
@change="changeGroup"
|
||||
class="p-w-200 ml-2 mt-2"
|
||||
>
|
||||
<el-option v-for="item in groupList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
<div v-for="item in groupList" :key="item.id">
|
||||
<el-option v-if="item.name === 'Default'" :label="$t('commons.table.default')" :value="item.id" />
|
||||
<el-option v-else :label="item.name" :value="item.id" />
|
||||
</div>
|
||||
</el-select>
|
||||
|
||||
<el-card class="mt-2 w-full" v-loading="loading">
|
||||
@@ -51,7 +54,12 @@
|
||||
min-width="80"
|
||||
prop="groupBelong"
|
||||
fix
|
||||
/>
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.groupBelong === 'Default'">{{ $t('commons.table.default') }}</span>
|
||||
<span v-else>{{ row.groupBelong }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('terminal.command')"
|
||||
:min-width="120"
|
||||
|
||||
@@ -27,7 +27,14 @@
|
||||
<template #rightToolBar>
|
||||
<el-select v-model="group" @change="search()" clearable class="p-w-200">
|
||||
<template #prefix>{{ $t('commons.table.group') }}</template>
|
||||
<el-option v-for="item in groupList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
<div v-for="item in groupList" :key="item.id">
|
||||
<el-option
|
||||
v-if="item.name === 'Default'"
|
||||
:label="$t('commons.table.default')"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-option v-else :label="item.name" :value="item.id" />
|
||||
</div>
|
||||
</el-select>
|
||||
<TableSearch @search="search()" v-model:searchName="info" />
|
||||
<TableRefresh @search="search()" />
|
||||
@@ -67,14 +74,16 @@
|
||||
<template #default="{ row }">
|
||||
<fu-select-rw-switch v-model="row.groupID" @change="updateGroup(row)">
|
||||
<template #read>
|
||||
{{ row.groupBelong }}
|
||||
{{ row.groupBelong === 'Default' ? $t('commons.table.default') : row.groupBelong }}
|
||||
</template>
|
||||
<el-option
|
||||
v-for="item in groupList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
<div v-for="item in groupList" :key="item.id">
|
||||
<el-option
|
||||
v-if="item.name === 'Default'"
|
||||
:label="$t('commons.table.default')"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-option v-else :label="item.name" :value="item.id" />
|
||||
</div>
|
||||
</fu-select-rw-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -22,7 +22,14 @@
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.table.group')" prop="groupID">
|
||||
<el-select filterable v-model="dialogData.rowData!.groupID" clearable style="width: 100%">
|
||||
<el-option v-for="item in groupList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
<div v-for="item in groupList" :key="item.id">
|
||||
<el-option
|
||||
v-if="item.name === 'Default'"
|
||||
:label="$t('commons.table.default')"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-option v-else :label="item.name" :value="item.id" />
|
||||
</div>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="!batchMode" :label="$t('terminal.command')" prop="command">
|
||||
|
||||
@@ -16,7 +16,14 @@
|
||||
<el-select v-model="group" @change="search()" clearable class="p-w-200">
|
||||
<template #prefix>{{ $t('commons.table.group') }}</template>
|
||||
<el-option :label="$t('commons.table.all')" value=""></el-option>
|
||||
<el-option v-for="item in groupList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
<div v-for="item in groupList" :key="item.id">
|
||||
<el-option
|
||||
v-if="item.name === 'Default'"
|
||||
:label="$t('commons.table.default')"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-option v-else :label="item.name" :value="item.id" />
|
||||
</div>
|
||||
</el-select>
|
||||
<TableSearch @search="search()" v-model:searchName="info" />
|
||||
<TableRefresh @search="search()" />
|
||||
@@ -41,14 +48,16 @@
|
||||
<template #default="{ row }">
|
||||
<fu-select-rw-switch v-model="row.groupID" @change="updateGroup(row)">
|
||||
<template #read>
|
||||
{{ row.groupBelong }}
|
||||
{{ row.groupBelong === 'Default' ? $t('commons.table.default') : row.groupBelong }}
|
||||
</template>
|
||||
<el-option
|
||||
v-for="item in groupList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
<div v-for="item in groupList" :key="item.id">
|
||||
<el-option
|
||||
v-if="item.name === 'Default'"
|
||||
:label="$t('commons.table.default')"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-option v-else :label="item.name" :value="item.id" />
|
||||
</div>
|
||||
</fu-select-rw-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -50,7 +50,14 @@
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.table.group')" prop="groupID">
|
||||
<el-select filterable v-model="dialogData.rowData!.groupID" clearable style="width: 100%">
|
||||
<el-option v-for="item in groupList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
<div v-for="item in groupList" :key="item.id">
|
||||
<el-option
|
||||
v-if="item.name === 'Default'"
|
||||
:label="$t('commons.table.default')"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-option v-else :label="item.name" :value="item.id" />
|
||||
</div>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.table.title')" prop="name">
|
||||
|
||||
@@ -268,6 +268,11 @@ const handleTabsRemove = async (targetName: string, action: 'remove' | 'add') =>
|
||||
const loadCommandTree = async () => {
|
||||
const res = await getCommandTree('command');
|
||||
commandTree.value = res.data || [];
|
||||
for (const item of commandTree.value) {
|
||||
if (item.label === 'Default') {
|
||||
item.label = i18n.global.t('commons.table.default');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const sendToTerminals = (command: string, all: boolean) => {
|
||||
|
||||
@@ -41,21 +41,14 @@
|
||||
<el-checkbox v-model="obtain.pushDir" :label="$t('ssl.pushDir')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('ssl.dir')" prop="dir" v-if="obtain.pushDir">
|
||||
<el-button
|
||||
icon="Folder"
|
||||
class="mb-2"
|
||||
@click="
|
||||
fileRef.acceptParams({
|
||||
path: obtain.dir.split('\n')[0],
|
||||
dir: true,
|
||||
multiple: true,
|
||||
selectedPaths: obtain.dir.split('\n').filter(Boolean),
|
||||
})
|
||||
"
|
||||
>
|
||||
{{ $t('ssl.dir') }}
|
||||
</el-button>
|
||||
<el-input v-model.trim="obtain.dir" type="textarea" :rows="3" />
|
||||
<el-input v-model.trim="obtain.dir">
|
||||
<template #prepend>
|
||||
<el-button
|
||||
icon="Folder"
|
||||
@click="fileRef.acceptParams({ path: obtain.dir, dir: true })"
|
||||
/>
|
||||
</template>
|
||||
</el-input>
|
||||
<span class="input-help">
|
||||
{{ $t('ssl.pushDirHelper') }}
|
||||
</span>
|
||||
@@ -164,8 +157,8 @@ const resetForm = () => {
|
||||
obtain.value = initData();
|
||||
};
|
||||
|
||||
const getPath = (dirs: string[]) => {
|
||||
obtain.value.dir = dirs.join('\n');
|
||||
const getPath = (dir: string) => {
|
||||
obtain.value.dir = dir;
|
||||
};
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
|
||||
@@ -127,21 +127,11 @@
|
||||
<el-checkbox v-model="ssl.pushDir" :label="$t('ssl.pushDir')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('ssl.dir')" prop="dir" v-if="ssl.pushDir">
|
||||
<el-button
|
||||
icon="Folder"
|
||||
class="mb-2"
|
||||
@click="
|
||||
fileRef.acceptParams({
|
||||
path: ssl.dir.split('\n')[0],
|
||||
dir: true,
|
||||
multiple: true,
|
||||
selectedPaths: ssl.dir.split('\n').filter(Boolean),
|
||||
})
|
||||
"
|
||||
>
|
||||
{{ $t('ssl.dir') }}
|
||||
</el-button>
|
||||
<el-input v-model.trim="ssl.dir" type="textarea" :rows="3" />
|
||||
<el-input v-model.trim="ssl.dir">
|
||||
<template #prepend>
|
||||
<el-button icon="Folder" @click="fileRef.acceptParams({ path: ssl.dir, dir: true })" />
|
||||
</template>
|
||||
</el-input>
|
||||
<span class="input-help">
|
||||
{{ $t('ssl.pushDirHelper') }}
|
||||
</span>
|
||||
@@ -357,8 +347,8 @@ const acceptParams = (op: string, websiteSSL?: Website.SSLDTO) => {
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
const getPath = (dirs: string[]) => {
|
||||
ssl.value.dir = dirs.join('\n');
|
||||
const getPath = (dir: string) => {
|
||||
ssl.value.dir = dir;
|
||||
};
|
||||
|
||||
const getAcmeAccounts = async () => {
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
{{ ssl.acmeAccount.email }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('ssl.pushDir')" v-if="ssl.pushDir">
|
||||
<div v-for="dir in ssl.dir.split('\n')" :key="dir">{{ dir }}</div>
|
||||
{{ ssl.dir }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<template>
|
||||
<el-form-item :label="$t('commons.table.group')" :prop="prop">
|
||||
<el-select v-model="selectedValue">
|
||||
<el-option v-for="(group, index) in groups" :key="index" :label="group.name" :value="group.id" />
|
||||
<el-option
|
||||
v-for="(group, index) in groups"
|
||||
:key="index"
|
||||
:label="group.name === 'Default' ? $t('commons.table.default') : group.name"
|
||||
:value="group.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
@@ -45,7 +45,14 @@
|
||||
<el-select v-model="req.websiteGroupId" @change="search()" class="p-w-200" :disabled="disabledConfig">
|
||||
<template #prefix>{{ $t('commons.table.group') }}</template>
|
||||
<el-option :label="$t('commons.table.all')" :value="0"></el-option>
|
||||
<el-option v-for="item in groups" :key="item.id" :label="item.name" :value="item.id" />
|
||||
<div v-for="item in groups" :key="item.id">
|
||||
<el-option
|
||||
v-if="item.name === 'Default'"
|
||||
:label="$t('commons.table.default')"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-option v-else :label="item.name" :value="item.id" />
|
||||
</div>
|
||||
</el-select>
|
||||
<TableSearch @search="search()" v-model:searchName="req.name" :disabled="disabledConfig" />
|
||||
<TableRefresh @search="search()" :disabled="disabledConfig" />
|
||||
|
||||
Reference in New Issue
Block a user