update
This commit is contained in:
Generated
+26
@@ -1475,6 +1475,23 @@
|
|||||||
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
|
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/lodash": {
|
||||||
|
"version": "4.17.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz",
|
||||||
|
"integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/lodash-es": {
|
||||||
|
"version": "4.17.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz",
|
||||||
|
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/lodash": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/mime": {
|
"node_modules/@types/mime": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
||||||
@@ -4328,6 +4345,13 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash-es": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||||
@@ -7385,8 +7409,10 @@
|
|||||||
"xterm-addon-web-links": "^0.9.0"
|
"xterm-addon-web-links": "^0.9.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/splitpanes": "^2.2.6",
|
"@types/splitpanes": "^2.2.6",
|
||||||
"@vitejs/plugin-vue": "^4.2.0",
|
"@vitejs/plugin-vue": "^4.2.0",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
"vite": "^5.2.0",
|
"vite": "^5.2.0",
|
||||||
"vue-tsc": "^2.2.8"
|
"vue-tsc": "^2.2.8"
|
||||||
|
|||||||
@@ -29,8 +29,10 @@
|
|||||||
"xterm-addon-web-links": "^0.9.0"
|
"xterm-addon-web-links": "^0.9.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/splitpanes": "^2.2.6",
|
"@types/splitpanes": "^2.2.6",
|
||||||
"@vitejs/plugin-vue": "^4.2.0",
|
"@vitejs/plugin-vue": "^4.2.0",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
"vite": "^5.2.0",
|
"vite": "^5.2.0",
|
||||||
"vue-tsc": "^2.2.8"
|
"vue-tsc": "^2.2.8"
|
||||||
|
|||||||
@@ -434,6 +434,12 @@
|
|||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "Settings",
|
"title": "Settings",
|
||||||
|
"category": {
|
||||||
|
"security": "Security Settings",
|
||||||
|
"appearance": "Appearance Settings",
|
||||||
|
"workspace": "Workspace Settings",
|
||||||
|
"system": "System Settings"
|
||||||
|
},
|
||||||
"changePassword": {
|
"changePassword": {
|
||||||
"title": "Change Password",
|
"title": "Change Password",
|
||||||
"currentPassword": "Current Password:",
|
"currentPassword": "Current Password:",
|
||||||
|
|||||||
@@ -434,6 +434,12 @@
|
|||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "设置",
|
"title": "设置",
|
||||||
|
"category": {
|
||||||
|
"security": "安全设置",
|
||||||
|
"appearance": "外观设置",
|
||||||
|
"workspace": "工作区设置",
|
||||||
|
"system": "系统设置"
|
||||||
|
},
|
||||||
"changePassword": {
|
"changePassword": {
|
||||||
"title": "修改密码",
|
"title": "修改密码",
|
||||||
"currentPassword": "当前密码:",
|
"currentPassword": "当前密码:",
|
||||||
|
|||||||
@@ -11,7 +11,12 @@ export const useAuditLogStore = defineStore('auditLog', () => {
|
|||||||
const currentPage = ref(1);
|
const currentPage = ref(1);
|
||||||
const logsPerPage = ref(50); // Default page size
|
const logsPerPage = ref(50); // Default page size
|
||||||
|
|
||||||
const fetchLogs = async (page: number = 1, filters: { actionType?: AuditLogActionType, startDate?: number, endDate?: number } = {}) => {
|
// Updated fetchLogs to accept searchTerm and actionType directly
|
||||||
|
const fetchLogs = async (
|
||||||
|
page: number = 1,
|
||||||
|
searchTerm?: string,
|
||||||
|
actionType?: AuditLogActionType | '' // Allow empty string from select
|
||||||
|
) => {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
error.value = null;
|
error.value = null;
|
||||||
currentPage.value = page;
|
currentPage.value = page;
|
||||||
@@ -21,10 +26,11 @@ export const useAuditLogStore = defineStore('auditLog', () => {
|
|||||||
const params: Record<string, any> = {
|
const params: Record<string, any> = {
|
||||||
limit: logsPerPage.value,
|
limit: logsPerPage.value,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
...filters // Spread filter parameters
|
// Add new filter parameters if they have values
|
||||||
|
...(searchTerm && { search: searchTerm }),
|
||||||
|
...(actionType && { action_type: actionType }),
|
||||||
};
|
};
|
||||||
// Remove undefined filter values
|
// No need to remove undefined keys here as we conditionally add them
|
||||||
Object.keys(params).forEach(key => params[key] === undefined && delete params[key]);
|
|
||||||
|
|
||||||
const response = await apiClient.get<AuditLogApiResponse>('/audit-logs', { params }); // 使用 apiClient
|
const response = await apiClient.get<AuditLogApiResponse>('/audit-logs', { params }); // 使用 apiClient
|
||||||
logs.value = response.data.logs;
|
logs.value = response.data.logs;
|
||||||
|
|||||||
@@ -5,7 +5,29 @@
|
|||||||
{{ $t('auditLog.title') }}
|
{{ $t('auditLog.title') }}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<!-- TODO: Add filtering options (Action Type, Date Range) -->
|
<!-- Filtering Controls -->
|
||||||
|
<div class="flex flex-wrap items-center gap-4 mb-4 p-4 border border-border rounded-lg bg-header/50">
|
||||||
|
<div class="flex-grow min-w-[200px]">
|
||||||
|
<label for="search-term" class="block text-sm font-medium text-text-secondary mb-1">{{ $t('common.search') }}</label>
|
||||||
|
<input type="text" id="search-term" v-model="searchTerm" :placeholder="$t('auditLog.searchPlaceholder')"
|
||||||
|
class="w-full px-3 py-2 border border-border rounded-md shadow-sm bg-background text-foreground focus:outline-none focus:ring-1 focus:ring-primary focus:border-primary text-sm">
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow min-w-[200px]">
|
||||||
|
<label for="action-type" class="block text-sm font-medium text-text-secondary mb-1">{{ $t('auditLog.table.actionType') }}</label>
|
||||||
|
<select id="action-type" v-model="selectedActionType"
|
||||||
|
class="w-full px-3 py-2 border border-border rounded-md shadow-sm bg-background text-foreground focus:outline-none focus:ring-1 focus:ring-primary focus:border-primary appearance-none bg-no-repeat bg-right pr-8 text-sm"
|
||||||
|
style="background-image: url('data:image/svg+xml,%3csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 16 16\'%3e%3cpath fill=\'none\' stroke=\'%236c757d\' stroke-linecap=\'round\' stroke-linejoin=\'round\' stroke-width=\'2\' d=\'M2 5l6 6 6-6\'/%3e%3c/svg%3e'); background-position: right 0.75rem center; background-size: 16px 12px;">
|
||||||
|
<option value="">{{ $t('common.all') }}</option>
|
||||||
|
<option v-for="type in allActionTypes" :key="type" :value="type">{{ translateActionType(type) }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="self-end">
|
||||||
|
<button @click="applyFilters" class="px-4 py-2 bg-button text-button-text rounded hover:bg-button-hover text-sm font-medium">
|
||||||
|
{{ $t('common.filter') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- End Filtering Controls -->
|
||||||
|
|
||||||
<div v-if="store.isLoading" class="p-4 text-center text-text-secondary italic"> <!-- Loading state -->
|
<div v-if="store.isLoading" class="p-4 text-center text-text-secondary italic"> <!-- Loading state -->
|
||||||
{{ $t('common.loading') }}
|
{{ $t('common.loading') }}
|
||||||
@@ -76,14 +98,35 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, computed } from 'vue';
|
import { ref, onMounted, computed } from 'vue'; // Removed watch
|
||||||
import { useAuditLogStore } from '../stores/audit.store';
|
import { useAuditLogStore } from '../stores/audit.store';
|
||||||
import { AuditLogEntry, AuditLogActionType } from '../types/server.types';
|
import { AuditLogEntry, AuditLogActionType } from '../types/server.types';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
// Removed lodash-es import
|
||||||
|
|
||||||
const store = useAuditLogStore();
|
const store = useAuditLogStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
// --- Filtering State ---
|
||||||
|
const searchTerm = ref('');
|
||||||
|
const selectedActionType = ref<AuditLogActionType | ''>(''); // Allow empty string for 'All'
|
||||||
|
|
||||||
|
// Define all possible action types for the dropdown
|
||||||
|
const allActionTypes: AuditLogActionType[] = [
|
||||||
|
'LOGIN_SUCCESS', 'LOGIN_FAILURE', 'LOGOUT', 'PASSWORD_CHANGED',
|
||||||
|
'2FA_ENABLED', '2FA_DISABLED', 'PASSKEY_REGISTERED', 'PASSKEY_DELETED',
|
||||||
|
'CONNECTION_CREATED', 'CONNECTION_UPDATED', 'CONNECTION_DELETED', 'CONNECTION_TESTED',
|
||||||
|
'CONNECTIONS_IMPORTED', 'CONNECTIONS_EXPORTED',
|
||||||
|
'PROXY_CREATED', 'PROXY_UPDATED', 'PROXY_DELETED',
|
||||||
|
'TAG_CREATED', 'TAG_UPDATED', 'TAG_DELETED',
|
||||||
|
'SETTINGS_UPDATED', 'IP_WHITELIST_UPDATED',
|
||||||
|
'NOTIFICATION_SETTING_CREATED', 'NOTIFICATION_SETTING_UPDATED', 'NOTIFICATION_SETTING_DELETED',
|
||||||
|
'API_KEY_CREATED', 'API_KEY_DELETED',
|
||||||
|
'SFTP_ACTION',
|
||||||
|
'SERVER_STARTED', 'SERVER_ERROR', 'DATABASE_MIGRATION'
|
||||||
|
];
|
||||||
|
// --- End Filtering State ---
|
||||||
|
|
||||||
const logs = computed(() => store.logs);
|
const logs = computed(() => store.logs);
|
||||||
const totalLogs = computed(() => store.totalLogs);
|
const totalLogs = computed(() => store.totalLogs);
|
||||||
const currentPage = computed(() => store.currentPage);
|
const currentPage = computed(() => store.currentPage);
|
||||||
@@ -91,7 +134,20 @@ const logsPerPage = computed(() => store.logsPerPage);
|
|||||||
|
|
||||||
const totalPages = computed(() => Math.ceil(totalLogs.value / logsPerPage.value));
|
const totalPages = computed(() => Math.ceil(totalLogs.value / logsPerPage.value));
|
||||||
|
|
||||||
|
// Function to apply filters and fetch logs
|
||||||
|
const applyFilters = () => {
|
||||||
|
// Pass undefined if filter is empty, otherwise pass the value
|
||||||
|
store.fetchLogs(
|
||||||
|
1, // Reset to page 1 when applying filters
|
||||||
|
searchTerm.value || undefined,
|
||||||
|
selectedActionType.value || undefined
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Removed watch for filters
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
// Fetch initial logs without filters
|
||||||
store.fetchLogs();
|
store.fetchLogs();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user