fix: Fix log file generation failure (#8172)

This commit is contained in:
ssongliu 2025-03-18 11:11:33 +08:00 committed by GitHub
parent 5c048a590d
commit 9cd83cd5dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 4130 additions and 1487 deletions

View file

@ -534,7 +534,7 @@ func (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlS
info.File = "OFF"
info.Position = "OFF"
masterStatus := "show master status;"
if common.CompareAppVersion(app.Version, "8.4.0") {
if common.CompareAppVersion(app.Version, "8.4.0") && req.Type == constant.AppMysql {
masterStatus = "show binary log status;"
}
rows, err := executeSqlForRows(app.ContainerName, app.Key, app.Password, masterStatus)

View file

@ -227,20 +227,24 @@ func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error {
if err != nil {
return err
}
err = itemHelper.FileOp.CopyFile("/usr/local/bin/1panel-core", baseDir)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-core"), err)
if err != nil {
return err
if global.IsMaster {
err = itemHelper.FileOp.CopyFile("/usr/local/bin/1panel-core", baseDir)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-core"), err)
if err != nil {
return err
}
}
err = itemHelper.FileOp.CopyFile("/usr/local/bin/1panel-agent", baseDir)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-agent"), err)
if err != nil {
return err
}
err = itemHelper.FileOp.CopyFile("/etc/systemd/system/1panel-core.service", baseDir)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel-core.service"), err)
if err != nil {
return err
if global.IsMaster {
err = itemHelper.FileOp.CopyFile("/etc/systemd/system/1panel-core.service", baseDir)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel-core.service"), err)
if err != nil {
return err
}
}
err = itemHelper.FileOp.CopyFile("/etc/systemd/system/1panel-agent.service", baseDir)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel-agent.service"), err)

View file

@ -68,7 +68,7 @@ func (b *BaseApi) SearchScript(c *gin.Context) {
// @Tags ScriptLibrary
// @Summary Delete script
// @Accept json
// @Param request body dto.BatchDeleteReq true "request"
// @Param request body dto.OperateByIDs true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp

View file

@ -42,7 +42,7 @@ type LoginLog struct {
Agent string `json:"agent"`
Status string `json:"status"`
Message string `json:"message"`
CreatedAt time.Time `json:"created_at"`
CreatedAt time.Time `json:"createdAt"`
}
type CleanLog struct {

View file

@ -172,7 +172,7 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
go writeLogs(req.Version)
_ = settingRepo.Update("SystemVersion", req.Version)
_ = settingRepo.Update("SystemStatus", "Free")
_, _ = cmd.ExecWithTimeOut("systemctl daemon-reload && systemctl restart 1panel-core.service && systemctl restart 1pane-agent.service", 1*time.Minute)
_, _ = cmd.ExecWithTimeOut("systemctl daemon-reload && systemctl restart 1pane-agent.service && systemctl restart 1panel-core.service", 1*time.Minute)
}()
return nil
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -3,32 +3,81 @@ package docs
import (
"encoding/json"
"fmt"
"github.com/spf13/afero"
"go/ast"
"go/parser"
"go/token"
"os"
"os/exec"
"path"
"strings"
"testing"
)
func TestSome(t *testing.T) {
err := afero.NewOsFs().Rename("/opt/tmp/ceshi", "/opt/tmp/ceshi2")
func TestGenerateXlog(t *testing.T) {
workDir := "/usr/songliu/1Panel"
fset := token.NewFileSet()
apiDirs := []string{workDir + "/agent/app/api/v2", workDir + "/core/app/api/v2", workDir + "/agent/xpack/app/api/v2", workDir + "/core/xpack/app/api/v2"}
xlogMap := make(map[string]operationJson)
for _, dir := range apiDirs {
entries, _ := os.ReadDir(dir)
for _, info := range entries {
if info.IsDir() {
continue
}
fileItem, err := parser.ParseFile(fset, path.Join(dir, info.Name()), nil, parser.ParseComments)
if err != nil {
continue
}
for _, decl := range fileItem.Decls {
switch d := decl.(type) {
case *ast.FuncDecl:
if d.Doc != nil {
routerContent := ""
logContent := ""
for _, comment := range d.Doc.List {
if strings.HasPrefix(comment.Text, "// @Router") {
routerContent = replaceStr(comment.Text, "// @Router", "[post]", "[get]", " ")
}
if strings.HasPrefix(comment.Text, "// @x-panel-log") {
logContent = replaceStr(comment.Text, "// @x-panel-log", " ")
}
}
if len(routerContent) != 0 && len(logContent) != 0 {
var item operationJson
if err := json.Unmarshal([]byte(logContent), &item); err != nil {
panic(fmt.Sprintf("json unamrshal failed, err: %v", err))
}
xlogMap[routerContent] = item
}
}
}
}
}
}
newJson, err := json.MarshalIndent(xlogMap, "", "\t")
if err != nil {
fmt.Println(err)
panic(fmt.Sprintf("json marshal for new file failed, err: %v", err))
}
if err := os.WriteFile("x-log.json", newJson, 0640); err != nil {
panic(fmt.Sprintf("write new swagger.json failed, err: %v", err))
}
}
func TestGenerateSwaggerDoc(t *testing.T) {
workDir := "/Users/wangzhengkun/projects/github.com/1Panel-dev/1Panel"
swagBin := "/Users/wangzhengkun/go/bin/swag"
workDir := "/usr/songliu/1Panel"
swagBin := "/root/go/bin/swag"
cmd1 := exec.Command(swagBin, "init", "-o", workDir+"/cmd/server/docs/docs_agent", "-d", workDir+"/agent", "-g", "./cmd/server/main.go")
cmd1 := exec.Command(swagBin, "init", "-o", workDir+"/core/cmd/server/docs/docs_agent", "-d", workDir+"/agent", "-g", "../agent/cmd/server/main.go")
cmd1.Dir = workDir
std1, err := cmd1.CombinedOutput()
if err != nil {
fmt.Printf("generate swagger doc of agent failed, std1: %v, err: %v", string(std1), err)
return
}
cmd2 := exec.Command(swagBin, "init", "-o", workDir+"/cmd/server/docs/docs_core", "-d", workDir+"/core", "-g", "./cmd/server/main.go")
cmd2 := exec.Command(swagBin, "init", "-o", workDir+"/core/cmd/server/docs/docs_core", "-d", workDir+"/core", "-g", "./cmd/server/main.go")
cmd2.Dir = workDir
std2, err := cmd2.CombinedOutput()
if err != nil {
@ -36,7 +85,7 @@ func TestGenerateSwaggerDoc(t *testing.T) {
return
}
agentJson := workDir + "/cmd/server/docs/docs_agent/swagger.json"
agentJson := workDir + "/core/cmd/server/docs/docs_agent/swagger.json"
agentFile, err := os.ReadFile(agentJson)
if err != nil {
fmt.Printf("read file docs_agent failed, err: %v", err)
@ -48,7 +97,7 @@ func TestGenerateSwaggerDoc(t *testing.T) {
return
}
coreJson := workDir + "/cmd/server/docs/docs_core/swagger.json"
coreJson := workDir + "/core/cmd/server/docs/docs_core/swagger.json"
coreFile, err := os.ReadFile(coreJson)
if err != nil {
fmt.Printf("read file docs_core failed, err: %v", err)
@ -76,26 +125,6 @@ func TestGenerateSwaggerDoc(t *testing.T) {
newSwagger.Paths[key] = val
}
newXLog := make(map[string]interface{})
for key, val := range newSwagger.Paths {
methodMap, isMethodMap := val.(map[string]interface{})
if !isMethodMap {
continue
}
dataMap, hasPost := methodMap["post"]
if !hasPost {
continue
}
data, isDataMap := dataMap.(map[string]interface{})
if !isDataMap {
continue
}
xLog, hasXLog := data["x-panel-log"]
if !hasXLog {
continue
}
newXLog[key] = xLog
}
newJson, err := json.MarshalIndent(newSwagger, "", "\t")
if err != nil {
fmt.Printf("json marshal for new file failed, err: %v", err)
@ -111,18 +140,8 @@ func TestGenerateSwaggerDoc(t *testing.T) {
return
}
newXLogFile, err := json.MarshalIndent(newXLog, "", "\t")
if err != nil {
fmt.Printf("json marshal for new x-log file failed, err: %v", err)
return
}
if err := os.WriteFile("x-log.json", newXLogFile, 0640); err != nil {
fmt.Printf("write new x-log.json failed, err: %v", err)
return
}
_ = os.RemoveAll(workDir + "/cmd/server/docs/docs_agent")
_ = os.RemoveAll(workDir + "/cmd/server/docs/docs_core")
_ = os.RemoveAll(workDir + "/core/cmd/server/docs/docs_agent")
_ = os.RemoveAll(workDir + "/core/cmd/server/docs/docs_core")
}
type Swagger struct {
@ -158,3 +177,26 @@ func init() {
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}`
}
func replaceStr(val string, rep ...string) string {
for _, item := range rep {
val = strings.ReplaceAll(val, item, "")
}
return val
}
type operationJson struct {
BodyKeys []string `json:"bodyKeys"`
ParamKeys []string `json:"paramKeys"`
BeforeFunctions []functionInfo `json:"beforeFunctions"`
FormatZH string `json:"formatZH"`
FormatEN string `json:"formatEN"`
}
type functionInfo struct {
InputColumn string `json:"input_column"`
InputValue string `json:"input_value"`
IsList bool `json:"isList"`
DB string `json:"db"`
OutputColumn string `json:"output_column"`
OutputValue string `json:"output_value"`
}

File diff suppressed because it is too large Load diff

View file

@ -1,20 +1,26 @@
<template>
<div>
<el-popover placement="bottom-start" :width="200" trigger="click">
<template #reference>
<el-dropdown @command="changeRefresh">
<el-badge
badge-style="background-color: transparent; font-size: 12px; border: none; color: black"
:offset="[-12, 7]"
:value="refreshRateUnit === 0 ? '' : refreshRateUnit + 's'"
class="item"
>
<el-button class="timer-button" icon="Clock"></el-button>
</el-badge>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="0">{{ $t('commons.table.noRefresh') }}</el-dropdown-item>
<el-dropdown-item :command="5">{{ $t('commons.table.refreshRateUnit', [5]) }}</el-dropdown-item>
<el-dropdown-item :command="10">{{ $t('commons.table.refreshRateUnit', [10]) }}</el-dropdown-item>
<el-dropdown-item :command="30">{{ $t('commons.table.refreshRateUnit', [30]) }}</el-dropdown-item>
<el-dropdown-item :command="60">{{ $t('commons.table.refreshRateUnit', [60]) }}</el-dropdown-item>
<el-dropdown-item :command="120">{{ $t('commons.table.refreshRateUnit', [120]) }}</el-dropdown-item>
<el-dropdown-item :command="300">{{ $t('commons.table.refreshRateUnit', [300]) }}</el-dropdown-item>
</el-dropdown-menu>
</template>
<el-select v-model="refreshRate" @change="changeRefresh">
<template #prefix>{{ $t('commons.table.refreshRate') }}</template>
<el-option :label="$t('commons.table.noRefresh')" :value="0"></el-option>
<el-option :label="$t('commons.table.refreshRateUnit', [5])" :value="5"></el-option>
<el-option :label="$t('commons.table.refreshRateUnit', [10])" :value="10"></el-option>
<el-option :label="$t('commons.table.refreshRateUnit', [30])" :value="30"></el-option>
<el-option :label="$t('commons.table.refreshRateUnit', [60])" :value="60"></el-option>
<el-option :label="$t('commons.table.refreshRateUnit', [120])" :value="120"></el-option>
<el-option :label="$t('commons.table.refreshRateUnit', [300])" :value="300"></el-option>
</el-select>
</el-popover>
</el-dropdown>
</div>
</template>
@ -22,7 +28,7 @@
import { onMounted, onUnmounted, ref } from 'vue';
defineOptions({ name: 'TableSetting' });
const refreshRate = ref<number>(0);
const refreshRateUnit = ref<number>(0);
const emit = defineEmits(['search']);
const props = defineProps({
title: String,
@ -31,42 +37,43 @@ const props = defineProps({
let timer: NodeJS.Timer | null = null;
const changeRefresh = () => {
if (refreshRate.value !== 0) {
const changeRefresh = (command: number) => {
refreshRateUnit.value = command || 0;
if (refreshRateUnit.value !== 0) {
if (timer) {
clearInterval(Number(timer));
timer = null;
}
timer = setInterval(() => {
emit('search');
}, 1000 * refreshRate.value);
}, 1000 * refreshRateUnit.value);
} else {
if (timer) {
clearInterval(Number(timer));
timer = null;
}
}
localStorage.setItem(props.title, refreshRate.value + '');
localStorage.setItem(props.title, refreshRateUnit.value + '');
};
onUnmounted(() => {
clearInterval(Number(timer));
timer = null;
if (props.title) {
localStorage.setItem(props.title, refreshRate.value + '');
localStorage.setItem(props.title, refreshRateUnit.value + '');
}
});
onMounted(() => {
if (props.title && localStorage.getItem(props.title) != null) {
let rate = Number(localStorage.getItem(props.title));
refreshRate.value = rate ? Number(rate) : 0;
changeRefresh();
refreshRateUnit.value = rate ? Number(rate) : 0;
changeRefresh(refreshRateUnit.value);
return;
}
if (props.rate) {
refreshRate.value = props.rate;
changeRefresh();
refreshRateUnit.value = props.rate;
changeRefresh(refreshRateUnit.value);
}
});
</script>

View file

@ -1267,6 +1267,7 @@ const message = {
deleteLogs: 'Clearing Logs',
resource: 'Resource',
detail: {
ai: 'AI',
groups: 'Group',
hosts: 'Host',
apps: 'App',
@ -1283,6 +1284,8 @@ const message = {
settings: 'Setting',
cronjobs: 'Cronjob',
databases: 'Database',
licenses: 'License',
nodes: 'Node',
},
websiteLog: 'Website Logs',
runLog: 'Run Log',
@ -3199,6 +3202,10 @@ const message = {
addNode: 'Add Node',
connInfo: 'Connection Information',
nodeInfo: 'Node Information',
nodeSyncHelper: 'Node information synchronization will sync the following information:',
nodeSyncHelper1: '1. Public backup account information',
nodeSyncHelper2: '2. Overview page recommended application information',
nodeSyncHelper3: '3. Connection information between the main node and sub-nodes',
panelExist:
'Detected that there is already 1panel service on this node. Adding this node will use the original service port and installation directory of 1panel. Do you want to continue?',
coreExist:

View file

@ -1204,6 +1204,7 @@ const message = {
deleteLogs: 'クリーンログ',
resource: 'リソース',
detail: {
ai: 'AI',
groups: 'グループ',
hosts: 'ホスト',
apps: 'アプリケーション',
@ -1220,6 +1221,8 @@ const message = {
settings: 'パネル設定',
cronjobs: 'スケジュールされたタスク',
databases: 'データベース',
licenses: 'ライセンス',
nodes: 'ノード',
},
websiteLog: 'ウェブサイトログ',
runLog: 'ログを実行します',
@ -3013,6 +3016,11 @@ const message = {
addNode: 'ノードを追加',
connInfo: '接続情報',
nodeInfo: 'ノード情報',
nodeSyncHelper: 'ノード情報の同期は以下の情報を同期します',
nodeSyncHelper1: '1. 公共のバックアップアカウント情報',
nodeSyncHelper2: '2. 概要ページの推奨アプリ情報',
nodeSyncHelper3: '3. 主ノードとサブノードの接続情報',
panelExist:
'このードに既に1panelサービスが存在しますこのノードを追加すると1panelの元のサービスポートとインストールディレクトリが使用されます続行しますか',
coreExist:

View file

@ -1192,6 +1192,7 @@ const message = {
deleteLogs: '로그 정리',
resource: '자원',
detail: {
ai: 'AI',
groups: '그룹',
hosts: '호스트',
apps: '애플리케이션',
@ -1208,6 +1209,8 @@ const message = {
settings: '패널 설정',
cronjobs: '예약 작업',
databases: '데이터베이스',
licenses: '라이선스',
nodes: '노드',
},
websiteLog: '웹사이트 로그',
runLog: '실행 로그',
@ -2962,6 +2965,10 @@ const message = {
addNode: '노드 추가',
connInfo: '연결 정보',
nodeInfo: '노드 정보',
nodeSyncHelper: '노드 정보 동기화는 다음 정보를 동기화합니다:',
nodeSyncHelper1: '1. 공용 백업 계정 정보',
nodeSyncHelper2: '2. 개요 페이지 추천 애플리케이션 정보',
nodeSyncHelper3: '3. 노드와 하위 노드 간의 연결 정보',
panelExist:
' 노드에 이미 1panel 서비스가 존재합니다. 노드를 추가하면 1panel의 원래 서비스 포트와 설치 디렉토리를 사용합니다. 계속하시겠습니까?',
coreExist:

View file

@ -1247,6 +1247,7 @@ const message = {
deleteLogs: 'Bersihkan Log',
resource: 'Sumber',
detail: {
ai: 'AI',
groups: 'Kumpulan',
hosts: 'Hos',
apps: 'Aplikasi',
@ -1263,6 +1264,8 @@ const message = {
settings: 'Tetapan Panel',
cronjobs: 'Tugas Terjadual',
databases: 'Pangkalan',
licenses: 'lesen',
nodes: 'nod',
},
websiteLog: 'Log Laman Web',
runLog: 'Log Jalankan',
@ -3083,6 +3086,10 @@ const message = {
addNode: 'Tambah Nod',
connInfo: 'Maklumat Sambungan',
nodeInfo: 'Maklumat Nod',
nodeSyncHelper: 'Penyelarasan maklumat nod akan menyelaraskan maklumat berikut:',
nodeSyncHelper1: '1. Maklumat akaun sandaran awam',
nodeSyncHelper2: '2. Maklumat aplikasi yang disyorkan di halaman gambaran keseluruhan',
nodeSyncHelper3: '3. Maklumat sambungan antara nod utama dan nod sub',
panelExist:
'Dikesan bahawa terdapat perkhidmatan 1panel yang sudah ada pada nod ini. Menambah nod ini akan menggunakan port dan direktori pemasangan perkhidmatan asal 1panel. Adakah anda ingin meneruskan?',
coreExist:

View file

@ -1230,6 +1230,7 @@ const message = {
deleteLogs: 'Limpar logs',
resource: 'Recurso',
detail: {
ai: 'AI',
groups: 'Grupos',
hosts: 'Hosts',
apps: 'Aplicativos',
@ -1246,6 +1247,8 @@ const message = {
settings: 'Configurações do Painel',
cronjobs: 'Tarefas Agendadas',
databases: 'Bancos de Dados',
licenses: 'licenças',
nodes: 'nós',
},
websiteLog: 'Logs do website',
runLog: 'Logs de execução',
@ -3086,6 +3089,10 @@ const message = {
addNode: 'Adicionar ',
connInfo: 'Informações de Conexão',
nodeInfo: 'Informações do ',
nodeSyncHelper: 'A sincronização das informações do irá sincronizar as seguintes informações:',
nodeSyncHelper1: '1. Informações da conta de backup pública',
nodeSyncHelper2: '2. Informações sobre aplicativos recomendados na página de visão geral',
nodeSyncHelper3: '3. Informações de conexão entre o principal e os sub-nós',
panelExist:
'Detectado que existe um serviço 1panel neste . Adicionar este usará a porta e o diretório de instalação do serviço original do 1panel. Deseja continuar?',
coreExist:

View file

@ -1238,6 +1238,7 @@ const message = {
deleteLogs: 'Очистить логи',
resource: 'Ресурс',
detail: {
ai: 'AI',
groups: 'Группы',
hosts: 'Хосты',
apps: 'Приложения',
@ -1254,6 +1255,8 @@ const message = {
settings: 'Настройки панели',
cronjobs: 'Запланированные задачи',
databases: 'Базы данных',
licenses: 'лицензии',
nodes: 'ноды',
},
websiteLog: 'Логи веб-сайта',
runLog: 'Логи выполнения',
@ -3076,6 +3079,10 @@ const message = {
addNode: 'Добавить узел',
connInfo: 'Информация о подключении',
nodeInfo: 'Информация об узле',
nodeSyncHelper: 'Синхронизация информации о узле будет синхронизировать следующую информацию:',
nodeSyncHelper1: '1. Информация о публичной резервной учетной записи',
nodeSyncHelper2: '2. Информация о рекомендуемых приложениях на странице обзора',
nodeSyncHelper3: '3. Информация о соединении между основным узлом и подузлами',
panelExist:
'Обнаружено, что на этом узле уже существует служба 1panel. Добавление этого узла будет использовать оригинальный порт и каталог установки службы 1panel. Вы хотите продолжить?',
coreExist:

View file

@ -1196,6 +1196,7 @@ const message = {
deleteLogs: '清空日誌',
resource: '資源',
detail: {
ai: 'AI',
groups: '分組',
hosts: '主機',
apps: '應用',
@ -1212,6 +1213,8 @@ const message = {
settings: '面板設定',
cronjobs: '計畫任務',
databases: '資料庫',
licenses: '許可證',
nodes: '節點',
},
websiteLog: '網站日誌',
runLog: '運行日誌',
@ -2963,6 +2966,10 @@ const message = {
addNode: '新增節點',
connInfo: '連接資訊',
nodeInfo: '節點資訊',
nodeSyncHelper: '節點信息同步將同步以下信息',
nodeSyncHelper1: '1. 公用的備份帳號信息',
nodeSyncHelper2: '2. 概覽頁推薦應用信息',
nodeSyncHelper3: '3. 主節點與子節點的連接信息',
panelExist:
'已檢測到該節點上已存在 1panel 服務新增該節點將沿用 1panel 原服務的埠號及安裝目錄是否繼續',
coreExist: '已檢測到該節點上已存在 1panel-core 服務無法新增該節點請檢查後再試',

View file

@ -1194,6 +1194,7 @@ const message = {
deleteLogs: '清空日志',
resource: '资源',
detail: {
ai: 'AI',
groups: '分组',
hosts: '主机',
apps: '应用',
@ -1210,6 +1211,8 @@ const message = {
settings: '面板设置',
cronjobs: '计划任务',
databases: '数据库',
licenses: '许可证',
nodes: '节点',
},
websiteLog: '网站日志',
runLog: '运行日志',
@ -2944,6 +2947,10 @@ const message = {
addNode: '添加节点',
connInfo: '连接信息',
nodeInfo: '节点信息',
nodeSyncHelper: '节点信息同步将同步以下信息',
nodeSyncHelper1: '1. 公用的备份账号信息',
nodeSyncHelper2: '2. 概览页推荐应用信息',
nodeSyncHelper3: '3. 主节点与子节点的连接信息',
panelExist:
'检测到该节点上已存在 1panel 服务添加该节点将沿用 1panel 原服务的端口以及安装目录是否继续',
coreExist: '检测到该节点上已存在 1panel-core 服务无法添加该节点请检查后重试',

View file

@ -176,6 +176,7 @@
<LogFile
:defaultButton="false"
class="w-full"
:key="currentRecord?.taskID"
:heightDiff="410"
:config="{ type: 'task', taskID: currentRecord?.taskID, tail: true }"
/>
@ -378,7 +379,6 @@ const search = async () => {
if (currentRecord.value?.records) {
loadRecord(currentRecord.value);
}
console.log(currentRecord.value);
};
const forDetail = async (row: Cronjob.Record) => {
@ -386,6 +386,8 @@ const forDetail = async (row: Cronjob.Record) => {
loadRecord(row);
};
const loadRecord = async (row: Cronjob.Record) => {
currentRecord.value = row;
console.log(currentRecord.value);
if (row.records) {
const res = await getRecordLog(row.id);
let log = res.data.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '');

View file

@ -265,6 +265,9 @@ const buttons = [
},
{
label: i18n.global.t('commons.button.sync'),
disabled: (row: any) => {
return row.status === 'Free';
},
click: (row: any) => {
onSync(row);
},