mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-12-09 20:05:54 +08:00
fix: Operation logs Add node information (#8212)
This commit is contained in:
parent
57f7cb30b4
commit
337dad2ca6
25 changed files with 121 additions and 39 deletions
|
|
@ -137,11 +137,7 @@ func (b *BaseApi) UpdateCommand(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
upMap := make(map[string]interface{})
|
||||
upMap["name"] = req.Name
|
||||
upMap["group_id"] = req.GroupID
|
||||
upMap["command"] = req.Command
|
||||
if err := commandService.Update(req.ID, upMap); err != nil {
|
||||
if err := commandService.Update(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import (
|
|||
)
|
||||
|
||||
type OperationLog struct {
|
||||
ID uint `json:"id"`
|
||||
Source string `json:"source"`
|
||||
|
||||
ID uint `json:"id"`
|
||||
Source string `json:"source"`
|
||||
Node string `json:"node"`
|
||||
IP string `json:"ip"`
|
||||
Path string `json:"path"`
|
||||
Method string `json:"method"`
|
||||
|
|
@ -26,6 +26,7 @@ type SearchOpLogWithPage struct {
|
|||
PageInfo
|
||||
Source string `json:"source"`
|
||||
Status string `json:"status"`
|
||||
Node string `json:"node"`
|
||||
Operation string `json:"operation"`
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ type OperationLog struct {
|
|||
BaseModel
|
||||
Source string `json:"source"`
|
||||
IP string `json:"ip"`
|
||||
Node string `json:"node"`
|
||||
Path string `json:"path"`
|
||||
Method string `json:"method"`
|
||||
UserAgent string `json:"userAgent"`
|
||||
|
|
|
|||
|
|
@ -55,6 +55,11 @@ func WithByStatus(status string) global.DBOption {
|
|||
return g.Where("status = ?", status)
|
||||
}
|
||||
}
|
||||
func WithByNode(node string) global.DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("node = ?", node)
|
||||
}
|
||||
}
|
||||
func WithByGroupBelong(group string) global.DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("group_belong = ?", group)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ type ICommandService interface {
|
|||
SearchForTree(req dto.OperateByType) ([]dto.CommandTree, error)
|
||||
SearchWithPage(search dto.SearchCommandWithPage) (int64, interface{}, error)
|
||||
Create(req dto.CommandOperate) error
|
||||
Update(id uint, upMap map[string]interface{}) error
|
||||
Update(req dto.CommandOperate) error
|
||||
Delete(ids []uint) error
|
||||
}
|
||||
|
||||
|
|
@ -123,6 +123,14 @@ func (u *CommandService) Delete(ids []uint) error {
|
|||
return commandRepo.Delete(repo.WithByIDs(ids))
|
||||
}
|
||||
|
||||
func (u *CommandService) Update(id uint, upMap map[string]interface{}) error {
|
||||
return commandRepo.Update(id, upMap)
|
||||
func (u *CommandService) Update(req dto.CommandOperate) error {
|
||||
command, _ := commandRepo.Get(repo.WithByName(req.Name), repo.WithByType(req.Type))
|
||||
if command.ID != req.ID {
|
||||
return buserr.New("ErrRecordExist")
|
||||
}
|
||||
upMap := make(map[string]interface{})
|
||||
upMap["name"] = req.Name
|
||||
upMap["group_id"] = req.GroupID
|
||||
upMap["command"] = req.Command
|
||||
return commandRepo.Update(req.ID, upMap)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,6 +122,9 @@ func (u *LogService) PageOperationLog(req dto.SearchOpLogWithPage) (int64, inter
|
|||
if len(req.Status) != 0 {
|
||||
options = append(options, repo.WithByStatus(req.Status))
|
||||
}
|
||||
if len(req.Node) != 0 {
|
||||
options = append(options, repo.WithByNode(req.Node))
|
||||
}
|
||||
|
||||
total, ops, err := logRepo.PageOperationLog(
|
||||
req.Page,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package i18n
|
|||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
|
|
@ -65,10 +64,9 @@ func GetErrMsg(key string, maps map[string]interface{}) string {
|
|||
}
|
||||
|
||||
func GetMsgByKey(key string) string {
|
||||
content, err := global.I18n.Localize(&i18n.LocalizeConfig{
|
||||
content, _ := global.I18n.Localize(&i18n.LocalizeConfig{
|
||||
MessageID: key,
|
||||
})
|
||||
fmt.Println(err)
|
||||
return content
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ func Init() {
|
|||
migrations.UpdateXpackHideMemu,
|
||||
migrations.AddSystemIP,
|
||||
migrations.InitScriptLibrary,
|
||||
migrations.AddOperationNode,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
|
|
|||
|
|
@ -339,3 +339,13 @@ var InitScriptLibrary = &gormigrate.Migration{
|
|||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var AddOperationNode = &gormigrate.Migration{
|
||||
ID: "20250321-add-operation-node",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.OperationLog{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,8 +32,10 @@ func OperationLog() gin.HandlerFunc {
|
|||
source := loadLogInfo(c.Request.URL.Path)
|
||||
pathItem := strings.TrimPrefix(c.Request.URL.Path, "/api/v2")
|
||||
pathItem = strings.TrimPrefix(pathItem, "/api/v2/core")
|
||||
currentNode := c.Request.Header.Get("CurrentNode")
|
||||
record := &model.OperationLog{
|
||||
Source: source,
|
||||
Node: currentNode,
|
||||
IP: c.ClientIP(),
|
||||
Method: strings.ToLower(c.Request.Method),
|
||||
Path: pathItem,
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ const systemRules: TokenRule[] = [
|
|||
const taskRules: TokenRule[] = [
|
||||
{
|
||||
type: 'bracket-text',
|
||||
pattern: /\[([^\]]+)\]/g,
|
||||
pattern: /\[([^\,]]+)\]/g,
|
||||
color: '#B87A2B',
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -31,11 +31,12 @@
|
|||
v-if="version !== 'Waiting' && !globalStore.hasNewVersion"
|
||||
type="primary"
|
||||
:underline="false"
|
||||
class="ml-2"
|
||||
@click="onLoadUpgradeInfo"
|
||||
>
|
||||
{{ $t('commons.button.update') }}
|
||||
</el-link>
|
||||
<el-tag v-if="version === 'Waiting'" round style="margin-left: 10px">
|
||||
<el-tag v-if="version === 'Waiting'" round class="ml-2.5">
|
||||
{{ $t('setting.upgrading') }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -7,15 +7,19 @@
|
|||
@mouseenter="collapseButtonShow = true"
|
||||
@mouseleave="collapseButtonShow = false"
|
||||
>
|
||||
<el-affix v-if="collapseButtonShow" :offset="124" class="affix">
|
||||
<el-button
|
||||
size="small"
|
||||
circle
|
||||
:style="{ 'margin-left': menuStore.isCollapse ? '60px' : '165px', position: 'absolute' }"
|
||||
:icon="menuStore.isCollapse ? 'ArrowRight' : 'ArrowLeft'"
|
||||
plain
|
||||
@click="handleCollapse()"
|
||||
></el-button>
|
||||
<el-affix v-if="collapseButtonShow" :offset="18" class="affix">
|
||||
<el-tooltip
|
||||
:content="menuStore.isCollapse ? $t('commons.button.expand') : $t('commons.button.collapse')"
|
||||
>
|
||||
<el-button
|
||||
size="small"
|
||||
circle
|
||||
:style="{ 'margin-left': menuStore.isCollapse ? '60px' : '165px', position: 'absolute' }"
|
||||
:icon="menuStore.isCollapse ? 'ArrowRight' : 'ArrowLeft'"
|
||||
plain
|
||||
@click="handleCollapse()"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</el-affix>
|
||||
<Sidebar @menu-click="handleMenuClick" :menu-router="!classObj.openMenuTabs" @open-task="openTask" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
@sort-change="search"
|
||||
@search="search"
|
||||
:data="data"
|
||||
:heightDiff="300"
|
||||
>
|
||||
<el-table-column type="selection" fix />
|
||||
<el-table-column
|
||||
|
|
|
|||
|
|
@ -387,7 +387,6 @@ const forDetail = async (row: Cronjob.Record) => {
|
|||
};
|
||||
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, '');
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
:pagination-config="paginationConfig"
|
||||
:data="data"
|
||||
@search="search"
|
||||
:heightDiff="370"
|
||||
:heightDiff="300"
|
||||
>
|
||||
<el-table-column type="selection" fix />
|
||||
<el-table-column :label="$t('commons.table.name')" show-overflow-tooltip prop="name" min-width="60">
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
|||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
loading.value = true;
|
||||
dialogData.value.rowData.groupList = dialogData.value.rowData.groupList || [];
|
||||
dialogData.value.rowData.groups = dialogData.value.rowData.groupList.join(',');
|
||||
if (dialogData.value.title === 'create') {
|
||||
await addScript(dialogData.value.rowData)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
<template>
|
||||
<div v-loading="loading">
|
||||
<LayoutContent :title="props.database + ' ' + $t('commons.button.set')" backName="PostgreSQL">
|
||||
<LayoutContent backName="PostgreSQL">
|
||||
<template #leftToolBar>
|
||||
<el-text class="mx-1">
|
||||
{{ props.database }}
|
||||
</el-text>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button type="primary" :plain="activeName !== 'conf'" @click="jumpToConf">
|
||||
{{ $t('database.confChange') }}
|
||||
</el-button>
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ const buttons = [
|
|||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
label: i18n.global.t('commons.button.unbind'),
|
||||
click: (row: Database.DatabaseInfo) => {
|
||||
onDelete(row);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
<template>
|
||||
<div v-show="settingShow" v-loading="loading">
|
||||
<LayoutContent :title="database + ' ' + $t('commons.button.set')" :reload="true">
|
||||
<LayoutContent :reload="true">
|
||||
<template #leftToolBar>
|
||||
<el-text class="mx-1">
|
||||
{{ database }}
|
||||
</el-text>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button type="primary" :plain="activeName !== 'conf'" @click="changeTab('conf')">
|
||||
{{ $t('database.confChange') }}
|
||||
</el-button>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</el-button>
|
||||
</template>
|
||||
<template #rightToolBar>
|
||||
<el-select v-model="searchGroup" @change="search()" clearable class="p-w-200 mr-2.5">
|
||||
<el-select v-model="searchGroup" @change="search()" clearable class="p-w-200">
|
||||
<template #prefix>{{ $t('logs.resource') }}</template>
|
||||
<el-option :label="$t('commons.table.all')" value=""></el-option>
|
||||
<el-option :label="$t('logs.detail.apps')" value="apps"></el-option>
|
||||
|
|
@ -25,12 +25,18 @@
|
|||
<el-option :label="$t('logs.detail.logs')" value="logs"></el-option>
|
||||
<el-option :label="$t('logs.detail.settings')" value="settings"></el-option>
|
||||
</el-select>
|
||||
<el-select v-model="searchStatus" @change="search()" clearable class="p-w-200 mr-2.5">
|
||||
<template #prefix>{{ $t('commons.table.status') }}</template>
|
||||
<el-select v-model="searchStatus" @change="search()" clearable class="p-w-200">
|
||||
<template #prefix>{{ $t('xpack.node.node') }}</template>
|
||||
<el-option :label="$t('commons.table.all')" value=""></el-option>
|
||||
<el-option :label="$t('commons.status.success')" value="Success"></el-option>
|
||||
<el-option :label="$t('commons.status.failed')" value="Failed"></el-option>
|
||||
</el-select>
|
||||
<el-select v-model="searchNode" @change="search()" clearable class="p-w-200">
|
||||
<template #prefix>{{ $t('xpack.node.node') }}</template>
|
||||
<el-option :label="$t('commons.table.all')" value=""></el-option>
|
||||
<el-option :label="$t('xpack.node.master')" value="local" />
|
||||
<el-option v-for="(node, index) in nodes" :key="index" :label="node.name" :value="node.name" />
|
||||
</el-select>
|
||||
<TableSearch @search="search()" v-model:searchName="searchName" />
|
||||
<TableRefresh @search="search()" />
|
||||
<TableSetting title="operation-log-refresh" @search="search()" />
|
||||
|
|
@ -52,7 +58,11 @@
|
|||
<span v-if="globalStore.language === 'en'">{{ row.detailEN }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column v-if="globalStore.isMasterProductPro" :label="$t('xpack.node.node')" prop="node">
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.node === 'local' ? $t('xpack.node.master') : row.node }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.table.status')" prop="status">
|
||||
<template #default="{ row }">
|
||||
<Status :status="row.status" :msg="row.message" />
|
||||
|
|
@ -81,6 +91,7 @@ import { onMounted, reactive, ref } from '@vue/runtime-core';
|
|||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { GlobalStore } from '@/store';
|
||||
import { listNodeOptions } from '@/api/modules/setting';
|
||||
|
||||
const loading = ref();
|
||||
const data = ref();
|
||||
|
|
@ -94,6 +105,8 @@ const paginationConfig = reactive({
|
|||
const searchName = ref<string>('');
|
||||
const searchGroup = ref<string>('');
|
||||
const searchStatus = ref<string>('');
|
||||
const searchNode = ref<string>('');
|
||||
const nodes = ref();
|
||||
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
|
|
@ -104,6 +117,7 @@ const search = async () => {
|
|||
pageSize: paginationConfig.pageSize,
|
||||
status: searchStatus.value,
|
||||
source: searchGroup.value,
|
||||
node: searchNode.value,
|
||||
};
|
||||
loading.value = true;
|
||||
await getOperationLogs(params)
|
||||
|
|
@ -140,6 +154,23 @@ const loadDetail = (log: string) => {
|
|||
return log;
|
||||
};
|
||||
|
||||
const loadNodes = async () => {
|
||||
await listNodeOptions()
|
||||
.then((res) => {
|
||||
if (!res) {
|
||||
nodes.value = [];
|
||||
return;
|
||||
}
|
||||
nodes.value = res.data || [];
|
||||
if (nodes.value.length === 0) {
|
||||
globalStore.currentNode = 'local';
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
nodes.value = [];
|
||||
});
|
||||
};
|
||||
|
||||
const replacements = {
|
||||
'[enable]': 'commons.button.enable',
|
||||
'[Enable]': 'commons.button.enable',
|
||||
|
|
@ -172,6 +203,9 @@ const onSubmitClean = async () => {
|
|||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (globalStore.isMasterProductPro) {
|
||||
loadNodes();
|
||||
}
|
||||
search();
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,12 @@
|
|||
{{ $t('commons.button.watch') }}
|
||||
</el-checkbox>
|
||||
</el-button>
|
||||
<el-radio-group class="ml-2" @change="search()" v-model="itemType">
|
||||
<el-radio-group
|
||||
v-if="globalStore.currentNode === 'local'"
|
||||
class="ml-2"
|
||||
@change="search()"
|
||||
v-model="itemType"
|
||||
>
|
||||
<el-radio-button :label="$t('logs.agent')" value="Agent" />
|
||||
<el-radio-button :label="$t('logs.core')" value="Core" />
|
||||
</el-radio-group>
|
||||
|
|
@ -39,6 +44,8 @@ import LogFile from '@/components/log/file/index.vue';
|
|||
import LogRouter from '@/views/log/router/index.vue';
|
||||
import { nextTick, onMounted, reactive, ref } from 'vue';
|
||||
import { getSystemFiles } from '@/api/modules/log';
|
||||
import { GlobalStore } from '@/store';
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
const loading = ref();
|
||||
const isWatch = ref();
|
||||
|
|
|
|||
|
|
@ -297,6 +297,8 @@ const agreeWithLogin = () => {
|
|||
|
||||
const login = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl || isLoggingIn) return;
|
||||
errAuthInfo.value = false;
|
||||
errCaptcha.value = false;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
if (isIntl.value) {
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@
|
|||
inactive-value="Disable"
|
||||
/>
|
||||
<span class="input-help">{{ $t('setting.apiInterfaceHelper') }}</span>
|
||||
<div v-if="form.apiInterfaceStatus === 'enable'">
|
||||
<div v-if="form.apiInterfaceStatus === 'Enable'">
|
||||
<div>
|
||||
<el-button link type="primary" @click="onChangeApiInterfaceStatus">
|
||||
{{ $t('commons.button.view') }}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div @keydown.esc="toggleFullscreen()">
|
||||
<div tabindex="0">
|
||||
<el-tabs
|
||||
type="card"
|
||||
class="terminal-tabs"
|
||||
|
|
@ -164,12 +164,12 @@ const mobile = computed(() => {
|
|||
return globalStore.isMobile();
|
||||
});
|
||||
|
||||
function toggleFullscreen() {
|
||||
const toggleFullscreen = () => {
|
||||
if (screenfull.isEnabled) {
|
||||
screenfull.toggle();
|
||||
}
|
||||
globalStore.isFullScreen = !screenfull.isFullscreen;
|
||||
}
|
||||
};
|
||||
const loadTooltip = () => {
|
||||
return i18n.global.t('commons.button.' + (globalStore.isFullScreen ? 'quitFullscreen' : 'fullscreen'));
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue