From b1749ace16a2fc3ccb92da71d67a424456235ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AC=E6=98=8E?= <83812544+Ed1s0nZ@users.noreply.github.com> Date: Tue, 21 Jul 2026 16:18:20 +0800 Subject: [PATCH] Add files via upload --- web/static/css/c2.css | 578 +++++++++++++++++++++++++++++++++---- web/static/css/style.css | 80 ++++- web/static/i18n/en-US.json | 20 +- web/static/i18n/zh-CN.json | 20 +- web/static/js/c2.js | 266 +++++++++++++++-- web/templates/index.html | 68 ++++- 6 files changed, 940 insertions(+), 92 deletions(-) diff --git a/web/static/css/c2.css b/web/static/css/c2.css index d500fe27..8c8575fe 100644 --- a/web/static/css/c2.css +++ b/web/static/css/c2.css @@ -249,7 +249,8 @@ html[data-theme="dark"] .c2-checkbox-label { height: 1px; padding: 0; margin: -1px; - overflow: hidden; + overflow-x: auto; + overflow-y: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; @@ -2121,43 +2122,324 @@ html[data-theme="dark"] #page-c2-payloads .c2-form-select-option.is-selected { } /* ============================================================================ - Events + Events — audit log ============================================================================ */ .c2-events-page-wrap { display: flex; flex-direction: column; - gap: 0; + gap: 16px; + min-height: 0; + flex: 1; } -.c2-events-toolbar { +.c2-events-panel { display: flex; - align-items: center; - gap: 12px; - padding: 8px 4px 10px; - border-bottom: 1px solid var(--c2-border); - margin-bottom: 0; + flex-direction: column; + min-height: 0; + flex: 1; + background: var(--c2-surface); + border: 1px solid var(--c2-border); + border-radius: var(--c2-radius); + box-shadow: var(--c2-shadow-sm); + overflow: hidden; } -.c2-events-select-all-label { +.c2-events-list-container { + flex: 1; + min-height: 0; + overflow-x: auto; + overflow-y: hidden; + background: transparent; + border: none; + border-radius: 0; + box-shadow: none; +} + +.c2-events-empty { + padding: 48px 24px; + text-align: center; + color: var(--c2-text-dim); + font-size: 14px; +} + +.c2-events-table { + display: flex; + flex-direction: column; + height: 100%; + width: 100%; + border-collapse: separate; + border-spacing: 0; + table-layout: fixed; + min-width: 920px; +} + +.c2-events-table thead { + display: block; + flex: 0 0 auto; +} + +.c2-events-table tbody { + display: block; + flex: 1 1 auto; + min-height: 0; + overflow-y: auto; + overflow-x: hidden; + scrollbar-width: thin; + scrollbar-color: color-mix(in srgb, var(--c2-text-dim) 70%, transparent) transparent; +} + +.c2-events-table tr { + display: table; + width: 100%; + table-layout: fixed; +} + +.c2-events-table tbody::-webkit-scrollbar { + width: 8px; +} + +.c2-events-table tbody::-webkit-scrollbar-track { + background: var(--c2-surface); +} + +.c2-events-table tbody::-webkit-scrollbar-thumb { + border: 2px solid transparent; + border-radius: 999px; + background: color-mix(in srgb, var(--c2-text-dim) 70%, transparent); + background-clip: content-box; +} + +.c2-events-table tbody::-webkit-scrollbar-thumb:hover { + background: color-mix(in srgb, var(--c2-text-muted) 78%, transparent); + background-clip: content-box; +} + +.c2-events-table thead th { + z-index: 2; + text-align: left; + padding: 12px 16px; + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--c2-text-dim); + background: var(--c2-surface-alt); + font-weight: 700; + border-bottom: 1px solid var(--c2-border); + white-space: nowrap; + box-shadow: 0 1px 0 var(--c2-border); +} + +.c2-events-table th:nth-child(1), +.c2-events-table td:nth-child(1) { width: 44px; } +.c2-events-table th:nth-child(2), +.c2-events-table td:nth-child(2) { width: 210px; } +.c2-events-table th:nth-child(3), +.c2-events-table td:nth-child(3) { width: 112px; } +.c2-events-table th:nth-child(4), +.c2-events-table td:nth-child(4) { width: 140px; } +.c2-events-table th:nth-child(5), +.c2-events-table td:nth-child(5) { width: auto; } +.c2-events-table th:nth-child(6), +.c2-events-table td:nth-child(6) { width: 150px; } +.c2-events-table th:nth-child(7), +.c2-events-table td:nth-child(7) { width: 150px; } +.c2-events-table th:nth-child(8), +.c2-events-table td:nth-child(8) { width: 72px; } + +.c2-events-table td { + padding: 12px 16px; + border-top: 1px solid var(--c2-border); + font-size: 13px; + vertical-align: middle; + background: var(--c2-surface); +} + +.c2-events-table tbody tr:first-child td { + border-top: none; +} + +.c2-events-summary { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 10px; + flex-shrink: 0; +} + +.c2-events-stat { display: inline-flex; align-items: center; gap: 8px; - font-size: 13px; + padding: 6px 12px; + border-radius: var(--c2-radius-sm); + background: var(--c2-surface); + border: 1px solid var(--c2-border); + font-size: 12px; color: var(--c2-text-muted); - cursor: pointer; - user-select: none; } -.c2-events-select-all-label input { +.c2-events-stat strong { + font-size: 15px; + font-weight: 700; + color: var(--c2-text); + font-variant-numeric: tabular-nums; +} + +.c2-events-stat--info strong { color: var(--c2-accent); } +.c2-events-stat--warn strong { color: var(--c2-amber); } +.c2-events-stat--critical strong { color: var(--c2-red); } + +.c2-events-filter-card { + display: flex; + flex-direction: column; + gap: 14px; + padding: 16px 18px; + background: var(--c2-surface); + border: 1px solid var(--c2-border); + border-radius: var(--c2-radius); + box-shadow: var(--c2-shadow-sm); + flex-shrink: 0; +} + +.c2-events-time-presets { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; +} + +.c2-events-time-presets-label { + font-size: 12px; + font-weight: 600; + color: var(--c2-text-dim); + margin-right: 4px; +} + +.c2-events-time-preset-btn { + padding: 5px 12px; + border-radius: 999px; + border: 1px solid var(--c2-border); + background: var(--c2-surface-alt); + color: var(--c2-text-muted); + font-size: 12px; + font-weight: 500; cursor: pointer; + transition: background 0.12s, border-color 0.12s, color 0.12s; +} + +.c2-events-time-preset-btn:hover { + border-color: var(--c2-border-hover); + color: var(--c2-text); +} + +.c2-events-time-preset-btn.is-active { + background: var(--c2-accent-dim); + border-color: rgba(59, 130, 246, 0.45); + color: var(--c2-accent); + font-weight: 600; +} + +.c2-events-filter-fields { + display: grid; + grid-template-columns: minmax(148px, 0.9fr) minmax(148px, 0.9fr) minmax(180px, 1.4fr) auto; + gap: 12px 14px; + align-items: end; +} + +.c2-events-field { + display: flex; + flex-direction: column; + gap: 6px; + min-width: 0; +} + +.c2-events-field > span { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.4px; + color: var(--c2-text-dim); +} + +#page-c2-events .c2-events-field .form-control { + width: 100%; + height: 36px; + min-height: 36px; + padding: 0 12px; + font-size: 13px; + line-height: 36px; + box-sizing: border-box; +} + +#page-c2-events .c2-events-field select.form-control { + min-width: 148px; + padding: 0 36px 0 12px; + line-height: 34px; + cursor: pointer; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2364748b' d='M2.5 4.5L6 8l3.5-3.5'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 12px center; + background-size: 12px; +} + +.c2-events-filter-actions { + display: flex; + flex-wrap: nowrap; + align-items: center; + gap: 8px; + height: 36px; +} + +.c2-events-filter-actions .btn-small { + height: 36px; + min-height: 36px; + padding: 0 14px; + font-size: 13px; + line-height: 1; + box-sizing: border-box; + display: inline-flex; + align-items: center; + justify-content: center; + white-space: nowrap; +} + +.c2-events-table-col-check { + width: 44px; + text-align: center; +} + +.c2-events-table-col-actions { + width: 1%; + white-space: nowrap; + text-align: right; +} + +.c2-events-row { + cursor: pointer; + transition: background 0.12s ease; +} + +.c2-events-row:hover td { + background: var(--c2-surface-alt); +} + +.c2-events-row--critical td:first-child { + box-shadow: inset 3px 0 0 var(--c2-red); +} + +.c2-events-row--warn td:first-child { + box-shadow: inset 3px 0 0 var(--c2-amber); +} + +.c2-events-row--info td:first-child { + box-shadow: inset 3px 0 0 var(--c2-accent); } .c2-event-check-label { - flex-shrink: 0; display: flex; - align-items: flex-start; - padding-top: 4px; + justify-content: center; + align-items: center; cursor: pointer; } @@ -2165,60 +2447,248 @@ html[data-theme="dark"] #page-c2-payloads .c2-form-select-option.is-selected { cursor: pointer; } -.c2-event-row-delete { - flex-shrink: 0; - align-self: flex-start; - margin-top: 2px; - padding: 4px 10px; - font-size: 14px; - line-height: 1.2; +.c2-events-col-time { + white-space: nowrap; + color: var(--c2-text-muted); + font-size: 12px; + font-variant-numeric: tabular-nums; } -.c2-event-list { - background: var(--c2-surface); - border-radius: var(--c2-radius); +.c2-events-col-message { + max-width: 360px; + font-weight: 500; + color: var(--c2-text); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.c2-events-col-mono { + font-family: var(--c2-mono); + font-size: 12px; + color: var(--c2-text-muted); + max-width: 120px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.c2-events-cell-muted { + color: var(--c2-text-dim); +} + +.c2-event-level-badge { + display: inline-flex; + align-items: center; + padding: 3px 10px; + border-radius: 999px; + font-size: 11px; + font-weight: 600; + line-height: 1.4; + white-space: nowrap; +} + +.c2-event-level-badge--info { + background: var(--c2-accent-dim); + color: var(--c2-accent); +} + +.c2-event-level-badge--warn { + background: var(--c2-amber-dim); + color: #b45309; +} + +.c2-event-level-badge--critical { + background: var(--c2-red-dim); + color: var(--c2-red); +} + +.c2-event-category-badge { + display: inline-flex; + align-items: center; + padding: 3px 10px; + border-radius: var(--c2-radius-xs); + font-size: 11px; + font-weight: 600; + line-height: 1.4; + background: var(--c2-surface-alt); + color: var(--c2-text-muted); border: 1px solid var(--c2-border); - max-height: calc(100vh - 200px); - overflow-y: auto; } -.c2-event-item { - display: flex; +.c2-event-category-badge--listener { color: #7c3aed; background: rgba(124, 58, 237, 0.1); border-color: rgba(124, 58, 237, 0.25); } +.c2-event-category-badge--session { color: #059669; background: rgba(5, 150, 105, 0.1); border-color: rgba(5, 150, 105, 0.25); } +.c2-event-category-badge--task { color: #2563eb; background: rgba(37, 99, 235, 0.1); border-color: rgba(37, 99, 235, 0.25); } +.c2-event-category-badge--payload { color: #d97706; background: rgba(217, 119, 6, 0.1); border-color: rgba(217, 119, 6, 0.25); } +.c2-event-category-badge--opsec { color: #dc2626; background: rgba(220, 38, 38, 0.1); border-color: rgba(220, 38, 38, 0.25); } + +.c2-events-delete-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + padding: 0; + border: 1px solid transparent; + border-radius: var(--c2-radius-xs); + background: transparent; + color: var(--c2-text-dim); + cursor: pointer; + transition: background 0.12s, color 0.12s, border-color 0.12s; +} + +.c2-events-delete-btn:hover { + background: var(--c2-red-dim); + border-color: rgba(239, 68, 68, 0.25); + color: var(--c2-red); +} + +/* Event detail modal */ +.c2-event-modal-header { align-items: flex-start; - gap: 14px; - padding: 14px 20px; - border-bottom: 1px solid var(--c2-border); - transition: background 0.1s; } -.c2-event-item:hover { background: var(--c2-surface-alt); } -.c2-event-item:last-child { border-bottom: none; } - -.c2-event-level { - width: 8px; - height: 8px; - border-radius: 50%; - margin-top: 6px; - flex-shrink: 0; +.c2-event-modal-heading { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; } -.c2-event-level.info { background: var(--c2-accent); } -.c2-event-level.warn { background: var(--c2-amber); } -.c2-event-level.critical { background: var(--c2-red); } +.c2-event-modal-heading h3 { + margin: 0; +} -.c2-event-content { flex: 1; } +.c2-event-detail { + display: flex; + flex-direction: column; + gap: 16px; +} -.c2-event-message { +.c2-event-detail-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; +} + +.c2-event-kv { + display: flex; + flex-direction: column; + gap: 4px; + padding: 10px 12px; + border-radius: var(--c2-radius-sm); + background: var(--c2-surface-alt); + border: 1px solid var(--c2-border); +} + +.c2-event-kv__label { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.4px; + color: var(--c2-text-dim); +} + +.c2-event-kv__value { font-size: 13px; color: var(--c2-text); - margin-bottom: 3px; - font-weight: 500; + word-break: break-all; } -.c2-event-meta { - font-size: 11px; - color: var(--c2-text-muted); +.c2-event-kv__value--mono { font-family: var(--c2-mono); + font-size: 12px; +} + +.c2-event-message-block, +.c2-event-data-block { + border: 1px solid var(--c2-border); + border-radius: var(--c2-radius-sm); + overflow: hidden; +} + +.c2-event-message-block__label, +.c2-event-data-block__header { + padding: 8px 12px; + font-size: 11px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.4px; + color: var(--c2-text-dim); + background: var(--c2-surface-alt); + border-bottom: 1px solid var(--c2-border); +} + +.c2-event-data-block__header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +.c2-event-message-block__body { + padding: 12px 14px; + font-size: 14px; + line-height: 1.6; + color: var(--c2-text); + white-space: pre-wrap; + word-break: break-word; +} + +.c2-event-data-pre { + margin: 0; + padding: 12px 14px; + max-height: 280px; + overflow: auto; + font-family: var(--c2-mono); + font-size: 12px; + line-height: 1.5; + color: var(--c2-text); + background: transparent; +} + +#page-c2-events .page-content.c2-events-page-wrap { + display: flex; + flex-direction: column; + min-height: 0; + flex: 1; + overflow: hidden; +} + +#page-c2-events #c2-events-pagination { + flex-shrink: 0; + margin: 0; +} + +#page-c2-events #c2-events-pagination .monitor-pagination { + margin-top: 0; + border: none; + border-top: 1px solid var(--c2-border); + border-radius: 0; + background: var(--c2-surface-alt); + box-shadow: none; +} + +#page-c2-events #c2-events-pagination .pagination-page-size select { + border-radius: var(--c2-radius-xs); +} + +#page-c2-events #c2-events-pagination .pagination-controls .btn-secondary { + border-radius: var(--c2-radius-xs); +} + +@media (max-width: 900px) { + .c2-events-filter-fields { + grid-template-columns: 1fr; + } + + .c2-events-filter-actions { + justify-content: flex-start; + } + + .c2-event-detail-grid { + grid-template-columns: 1fr; + } } /* ============================================================================ diff --git a/web/static/css/style.css b/web/static/css/style.css index cd894d58..4557a3f8 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -32253,11 +32253,52 @@ html[data-theme="dark"] .chat-files-toast.chat-toast--error { #project-panel-vulns .projects-table-wrap { overflow-x: hidden; } +#project-panel-assets .projects-table-wrap { + overflow: hidden; +} +#project-panel-assets .data-table--projects { + display: flex; + height: 100%; + flex-direction: column; +} +#project-panel-assets .data-table--projects thead { + display: block; + flex: 0 0 auto; +} +#project-panel-assets .data-table--projects tbody { + display: block; + flex: 1 1 auto; + min-height: 0; + overflow-y: auto; + overflow-x: hidden; + scrollbar-width: thin; + scrollbar-color: color-mix(in srgb, var(--text-muted, #94a3b8) 52%, transparent) transparent; +} +#project-panel-assets .data-table--projects tr { + display: table; + width: 100%; + table-layout: fixed; +} +#project-panel-assets .data-table--projects tbody::-webkit-scrollbar { + width: 8px; +} +#project-panel-assets .data-table--projects tbody::-webkit-scrollbar-track { + background: var(--bg-primary, #fff); +} +#project-panel-assets .data-table--projects tbody::-webkit-scrollbar-thumb { + border: 2px solid transparent; + border-radius: 999px; + background: color-mix(in srgb, var(--text-muted, #94a3b8) 62%, transparent); + background-clip: content-box; +} +#project-panel-assets .data-table--projects tbody::-webkit-scrollbar-thumb:hover { + background: color-mix(in srgb, var(--text-secondary, #64748b) 82%, transparent); + background-clip: content-box; +} #project-panel-facts .projects-table-wrap { overflow-x: auto; } #project-panel-facts .projects-table-wrap .data-table--projects thead th, -#project-panel-assets .projects-table-wrap .data-table--projects thead th, #project-panel-conversations .projects-table-wrap .data-table--projects thead th, #project-panel-vulns .projects-table-wrap .data-table--projects thead th { position: sticky; @@ -32265,6 +32306,9 @@ html[data-theme="dark"] .chat-files-toast.chat-toast--error { z-index: 2; box-shadow: 0 1px 0 var(--border-color, #e2e8f0); } +#project-panel-assets .projects-table-wrap .data-table--projects thead th { + box-shadow: 0 1px 0 var(--border-color, #e2e8f0); +} .projects-panel-toolbar--hint { margin-bottom: 14px; padding: 0; @@ -41171,14 +41215,18 @@ html[data-theme="dark"] .workflow-toolbar #workflow-connect-btn[aria-pressed="tr .asset-batch-permission-action { display: contents; } .asset-list-meta { margin-left: auto; } .asset-list-card { overflow: hidden; border: 1px solid var(--border-color, #e5e7eb); border-radius: 12px; background: var(--card-bg, #fff); box-shadow: 0 1px 3px rgba(15,23,42,.03); } -#page-asset-library .asset-list-card { overflow: visible; } +#page-asset-library .asset-list-card { overflow: hidden; } #page-asset-library .asset-list-card { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; } .asset-table-wrap { overflow: auto; border: 1px solid var(--border-color, #e5e7eb); border-radius: 12px; background: var(--card-bg, #fff); } .asset-list-card .asset-table-wrap { border: 0; border-radius: 0; } -#page-asset-library .asset-list-card .asset-table-wrap { --table-scroll-header-height: 41px; flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden; scrollbar-gutter: stable; } -#page-asset-library .asset-list-card .asset-table-wrap::-webkit-scrollbar, -.info-collect-results-table-wrap::-webkit-scrollbar { width: 10px; } -#page-asset-library .asset-list-card .asset-table-wrap::-webkit-scrollbar-track { margin-top: var(--table-scroll-header-height); background: transparent; } +#page-asset-library .asset-list-card .asset-table-wrap { --table-scroll-header-height: 41px; --asset-table-header-bg: var(--card-bg, #fff); flex: 1 1 auto; min-height: 0; overflow: hidden; scrollbar-gutter: auto; } +#page-asset-library .asset-table { display: flex; height: 100%; flex-direction: column; } +#page-asset-library .asset-table thead { display: block; flex: 0 0 auto; } +#page-asset-library .asset-table tbody { display: block; flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden; scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--text-muted, #94a3b8) 52%, transparent) transparent; } +#page-asset-library .asset-table tr { display: table; width: 100%; table-layout: fixed; } +#page-asset-library .asset-table tbody::-webkit-scrollbar, +.info-collect-results-table-wrap::-webkit-scrollbar { width: 8px; } +#page-asset-library .asset-table tbody::-webkit-scrollbar-track { background: var(--card-bg, #fff); } .info-collect-results-table-wrap::-webkit-scrollbar-track { background: linear-gradient( to bottom, @@ -41186,14 +41234,14 @@ html[data-theme="dark"] .workflow-toolbar #workflow-connect-btn[aria-pressed="tr var(--bg-primary) var(--table-scroll-header-height) 100% ); } -#page-asset-library .asset-list-card .asset-table-wrap::-webkit-scrollbar-thumb, +#page-asset-library .asset-table tbody::-webkit-scrollbar-thumb, .info-collect-results-table-wrap::-webkit-scrollbar-thumb { - border: 3px solid transparent; + border: 2px solid transparent; border-radius: 999px; background: color-mix(in srgb, var(--text-muted, #94a3b8) 62%, transparent); background-clip: content-box; } -#page-asset-library .asset-list-card .asset-table-wrap::-webkit-scrollbar-thumb:hover, +#page-asset-library .asset-table tbody::-webkit-scrollbar-thumb:hover, .info-collect-results-table-wrap::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--text-secondary, #64748b) 82%, transparent); background-clip: content-box; @@ -41203,7 +41251,7 @@ html[data-theme="dark"] .workflow-toolbar #workflow-connect-btn[aria-pressed="tr .asset-list-card #asset-pagination.pagination-fixed .pagination { border-radius: 0; } .asset-table { width: 100%; min-width: 0; table-layout: fixed; border-collapse: collapse; } .asset-table th,.asset-table td { padding: 13px 14px; border-bottom: 1px solid var(--border-color, #edf0f4); text-align: left; vertical-align: middle; } -.asset-table th { position: sticky; top: 0; box-sizing: border-box; height: var(--table-scroll-header-height, auto); background: var(--card-bg, #fff); color: var(--text-secondary, #64748b); font-size: 12px; font-weight: 600; z-index: 1; } +.asset-table th { box-sizing: border-box; height: var(--table-scroll-header-height, auto); background: var(--asset-table-header-bg, var(--card-bg, #fff)); color: var(--text-secondary, #64748b); font-size: 12px; font-weight: 600; z-index: 1; } .asset-table td { font-size: 13px; } .asset-table td:nth-child(2) { width: 220px; max-width: 280px; } .asset-table th:nth-child(2),.asset-table td:nth-child(2) { width: auto; } @@ -41769,6 +41817,18 @@ html[data-theme="dark"] #page-asset-library .asset-table-wrap { border-color: #223047 !important; } +html[data-theme="dark"] #page-asset-library .asset-table-wrap { + --asset-table-header-bg: #172033; +} + +html[data-theme="dark"] #page-asset-library .asset-table tbody::-webkit-scrollbar-track { + background: #0f172a; +} + +html[data-theme="dark"] #project-panel-assets .data-table--projects tbody::-webkit-scrollbar-track { + background: #0f172a; +} + html[data-theme="dark"] #page-projects .projects-panel-toolbar--hint { background: transparent !important; border-color: transparent !important; diff --git a/web/static/i18n/en-US.json b/web/static/i18n/en-US.json index cd94a139..b96bbb23 100644 --- a/web/static/i18n/en-US.json +++ b/web/static/i18n/en-US.json @@ -3988,7 +3988,25 @@ "paginationPrev": "Previous", "paginationPage": "Page {{current}} / {{total}}", "paginationNext": "Next", - "paginationLast": "Last" + "paginationLast": "Last", + "statTotal": "Filtered", + "timePresets": "Quick", + "preset15m": "Last 15 min", + "preset1h": "Last 1 hour", + "preset24h": "Last 24 hours", + "preset7d": "Last 7 days", + "presetAll": "All", + "filterAllLevels": "All levels", + "filterAllCategories": "All categories", + "filterSession": "Session ID", + "filterSessionPlaceholder": "s_xxxxxxxx", + "applyFilters": "Apply filters", + "resetFilters": "Reset", + "colSession": "Session", + "colTask": "Task", + "colId": "Event ID", + "detailTitle": "Event details", + "detailData": "Additional data" }, "profiles": { "title": "Malleable Profile", diff --git a/web/static/i18n/zh-CN.json b/web/static/i18n/zh-CN.json index e3b0dece..c415e7c1 100644 --- a/web/static/i18n/zh-CN.json +++ b/web/static/i18n/zh-CN.json @@ -3976,7 +3976,25 @@ "paginationPrev": "上一页", "paginationPage": "第 {{current}} / {{total}} 页", "paginationNext": "下一页", - "paginationLast": "末页" + "paginationLast": "末页", + "statTotal": "当前筛选", + "timePresets": "快捷", + "preset15m": "最近15分钟", + "preset1h": "最近1小时", + "preset24h": "最近24小时", + "preset7d": "最近7天", + "presetAll": "全部", + "filterAllLevels": "全部级别", + "filterAllCategories": "全部类别", + "filterSession": "会话 ID", + "filterSessionPlaceholder": "s_xxxxxxxx", + "applyFilters": "应用筛选", + "resetFilters": "重置", + "colSession": "会话", + "colTask": "任务", + "colId": "事件 ID", + "detailTitle": "事件详情", + "detailData": "附加数据" }, "profiles": { "title": "流量伪装", diff --git a/web/static/js/c2.js b/web/static/js/c2.js index 546aa6bc..08f1c9e6 100644 --- a/web/static/js/c2.js +++ b/web/static/js/c2.js @@ -18,6 +18,8 @@ eventsPage: 1, eventsPageSize: 10, eventsTotal: 0, + eventsLevelCounts: { info: 0, warn: 0, critical: 0 }, + eventsFilter: { level: '', category: '', session_id: '', since: '', timePreset: 'all' }, profiles: [], selectedSessionId: null, selectedListenerId: null, @@ -3343,25 +3345,218 @@ // 事件审计 // ============================================================================ + function eventLevelLabel(level) { + const key = 'c2.events.' + (level || 'info'); + const translated = c2t(key); + return translated !== key ? translated : (level || 'info'); + } + + function eventCategoryLabel(category) { + const key = 'c2.events.' + (category || ''); + const translated = c2t(key); + return translated !== key ? translated : (category || '-'); + } + + function eventLevelBadgeClass(level) { + if (level === 'warn') return 'c2-event-level-badge--warn'; + if (level === 'critical') return 'c2-event-level-badge--critical'; + return 'c2-event-level-badge--info'; + } + + function eventCategoryBadgeClass(category) { + return 'c2-event-category-badge c2-event-category-badge--' + (category || 'default'); + } + + function readEventsFilterFromDom() { + const levelEl = document.getElementById('c2-events-filter-level'); + const catEl = document.getElementById('c2-events-filter-category'); + const sessionEl = document.getElementById('c2-events-filter-session'); + C2.eventsFilter = C2.eventsFilter || { level: '', category: '', session_id: '', since: '', timePreset: 'all' }; + C2.eventsFilter.level = levelEl ? levelEl.value.trim() : ''; + C2.eventsFilter.category = catEl ? catEl.value.trim() : ''; + C2.eventsFilter.session_id = sessionEl ? sessionEl.value.trim() : ''; + } + + function buildEventsQueryParams(page, pageSize) { + const params = new URLSearchParams(); + params.set('page', String(page)); + params.set('page_size', String(pageSize)); + const f = C2.eventsFilter || {}; + if (f.level) params.set('level', f.level); + if (f.category) params.set('category', f.category); + if (f.session_id) params.set('session_id', f.session_id); + if (f.since) params.set('since', f.since); + return params.toString(); + } + + function eventsTimePresetToSince(preset) { + if (!preset || preset === 'all') return ''; + const now = Date.now(); + const map = { '15m': 15 * 60 * 1000, '1h': 60 * 60 * 1000, '24h': 24 * 60 * 60 * 1000, '7d': 7 * 24 * 60 * 60 * 1000 }; + const ms = map[preset]; + if (!ms) return ''; + return new Date(now - ms).toISOString(); + } + + function syncEventsTimePresetButtons() { + const wrap = document.getElementById('c2-events-time-presets'); + if (!wrap) return; + const active = (C2.eventsFilter && C2.eventsFilter.timePreset) || 'all'; + wrap.querySelectorAll('.c2-events-time-preset-btn').forEach(btn => { + btn.classList.toggle('is-active', btn.getAttribute('data-preset') === active); + }); + } + + function bindEventsFilterUiOnce() { + if (document.documentElement.dataset.c2EventsFilterBound === '1') return; + document.documentElement.dataset.c2EventsFilterBound = '1'; + + const presets = document.getElementById('c2-events-time-presets'); + if (presets) { + presets.addEventListener('click', (e) => { + const btn = e.target.closest('.c2-events-time-preset-btn'); + if (!btn) return; + const preset = btn.getAttribute('data-preset') || 'all'; + C2.eventsFilter = C2.eventsFilter || { level: '', category: '', session_id: '', since: '', timePreset: 'all' }; + C2.eventsFilter.timePreset = preset; + C2.eventsFilter.since = eventsTimePresetToSince(preset); + syncEventsTimePresetButtons(); + C2.loadEvents(1); + }); + } + + const sessionInput = document.getElementById('c2-events-filter-session'); + if (sessionInput) { + sessionInput.addEventListener('keydown', (e) => { + if (e.key === 'Enter') C2.applyEventsFilters(); + }); + } + } + + C2.applyEventsFilters = function() { + readEventsFilterFromDom(); + C2.loadEvents(1); + }; + + C2.resetEventsFilters = function() { + C2.eventsFilter = { level: '', category: '', session_id: '', since: '', timePreset: 'all' }; + const levelEl = document.getElementById('c2-events-filter-level'); + const catEl = document.getElementById('c2-events-filter-category'); + const sessionEl = document.getElementById('c2-events-filter-session'); + if (levelEl) levelEl.value = ''; + if (catEl) catEl.value = ''; + if (sessionEl) sessionEl.value = ''; + syncEventsTimePresetButtons(); + C2.loadEvents(1); + }; + + C2.renderEventsSummary = function() { + const summary = document.getElementById('c2-events-summary'); + if (!summary) return; + const total = C2.eventsTotal || 0; + const counts = C2.eventsLevelCounts || { info: 0, warn: 0, critical: 0 }; + const set = (id, val) => { + const el = document.getElementById(id); + if (el) el.textContent = String(val); + }; + set('c2-events-stat-total', total); + set('c2-events-stat-info', counts.info || 0); + set('c2-events-stat-warn', counts.warn || 0); + set('c2-events-stat-critical', counts.critical || 0); + summary.hidden = total === 0; + }; + + C2.viewEvent = function(id) { + const event = (C2.events || []).find(e => e.id === id); + if (!event) return; + const modal = document.getElementById('c2-modal'); + const content = document.getElementById('c2-modal-content'); + if (!content || !modal) return; + + const modalBox = modal.querySelector('.c2-modal'); + if (modalBox) modalBox.classList.add('c2-modal--wide'); + + const hasData = event.data && typeof event.data === 'object' && Object.keys(event.data).length > 0; + const levelCls = eventLevelBadgeClass(event.level); + const catCls = eventCategoryBadgeClass(event.category); + + content.innerHTML = ` +
+
+

${escapeHtml(c2t('c2.events.detailTitle'))}

+ ${escapeHtml(eventLevelLabel(event.level))} + ${escapeHtml(eventCategoryLabel(event.category))} +
+ +
+
+
+
+
+ ${escapeHtml(c2t('c2.events.time'))} + ${escapeHtml(formatTime(event.createdAt))} +
+
+ ${escapeHtml(c2t('c2.events.colSession'))} + ${escapeHtml(event.sessionId || '-')} +
+
+ ${escapeHtml(c2t('c2.events.colTask'))} + ${escapeHtml(event.taskId || '-')} +
+
+ ${escapeHtml(c2t('c2.events.colId'))} + ${escapeHtml(event.id || '-')} +
+
+
+
${escapeHtml(c2t('c2.events.message'))}
+
${escapeHtml(event.message || '-')}
+
+ ${hasData ? ` +
+
+ ${escapeHtml(c2t('c2.events.detailData'))} + +
+
${escapeHtml(JSON.stringify(event.data, null, 2))}
+
+ ` : ''} +
+
+ + `; + openAppModal('c2-modal'); + if (typeof applyTranslations === 'function') applyTranslations(content); + }; + C2.loadEvents = function(page) { + bindEventsFilterUiOnce(); + readEventsFilterFromDom(); const p = page != null ? page : (C2.eventsPage || 1); C2.eventsPage = p; const ps = C2.eventsPageSize || 10; - apiRequest('GET', `${API_BASE}/events?page=${encodeURIComponent(String(p))}&page_size=${encodeURIComponent(String(ps))}`).then(data => { + const qs = buildEventsQueryParams(p, ps); + apiRequest('GET', `${API_BASE}/events?${qs}`).then(data => { if (data.error) { showToast(String(data.error), 'error'); return; } C2.events = data.events || []; C2.eventsTotal = typeof data.total === 'number' ? data.total : (C2.events.length || 0); + C2.eventsLevelCounts = data.level_counts || { info: 0, warn: 0, critical: 0 }; const maxPage = Math.max(1, Math.ceil(C2.eventsTotal / ps)); if (p > maxPage) { C2.loadEvents(maxPage); return; } C2.renderEvents(); + C2.renderEventsSummary(); C2.renderEventsPagination(); C2.syncEventsToolbar(); + syncEventsTimePresetButtons(); }).catch(err => { showToast(err.message || String(err), 'error'); }); @@ -3497,7 +3692,7 @@ } if (C2.events.length === 0) { - container.innerHTML = '
' + escapeHtml(c2t('c2.events.empty')) + '
'; + container.innerHTML = '
' + escapeHtml(c2t('c2.events.empty')) + '
'; if (selAll) selAll.disabled = true; C2.syncEventsToolbar(); return; @@ -3505,24 +3700,57 @@ if (selAll) selAll.disabled = false; const delTitle = escapeHtml(c2t('c2.events.deleteOne')); - container.innerHTML = C2.events.map(e => { - const eid = escapeHtml(e.id || ''); - return ` -
- -
-
-
${escapeHtml(e.message)}
-
- ${formatTime(e.createdAt)} · ${escapeHtml(e.category || '')}${e.sessionId ? ' · ' + escapeHtml(String(e.sessionId).substring(0, 8)) : ''} -
-
- -
+ const dash = ''; + const deleteIcon = ''; + + container.innerHTML = ` + + + + + + + + + + + + + + + ${C2.events.map(e => { + const eid = escapeHtml(e.id || ''); + const levelCls = eventLevelBadgeClass(e.level); + const catCls = eventCategoryBadgeClass(e.category); + const sessionShort = e.sessionId ? escapeHtml(String(e.sessionId).substring(0, 10)) + (String(e.sessionId).length > 10 ? '\u2026' : '') : ''; + const taskShort = e.taskId ? escapeHtml(String(e.taskId).substring(0, 10)) + (String(e.taskId).length > 10 ? '\u2026' : '') : ''; + const msg = escapeHtml(e.message || ''); + const rowLevel = escapeHtml(e.level || 'info'); + return ` + + + + + + + + + + `; + }).join('')} + +
+ + ${escapeHtml(c2t('c2.events.time'))}${escapeHtml(c2t('c2.events.level'))}${escapeHtml(c2t('c2.events.category'))}${escapeHtml(c2t('c2.events.message'))}${escapeHtml(c2t('c2.events.colSession'))}${escapeHtml(c2t('c2.events.colTask'))}${escapeHtml(c2t('common.actions'))}
+ + ${escapeHtml(formatTime(e.createdAt))}${escapeHtml(eventLevelLabel(e.level))}${escapeHtml(eventCategoryLabel(e.category))}${msg || dash}${sessionShort || dash}${taskShort || dash} + +
`; - }).join(''); C2.syncEventsToolbar(); if (typeof applyTranslations === 'function') applyTranslations(container); diff --git a/web/templates/index.html b/web/templates/index.html index 10b83a31..124191e1 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -2928,14 +2928,68 @@
-
- + +
+
+ 快捷 + + + + + +
+
+ + + +
+ + +
+
+
+
+
+
-
-