mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-01 00:27:35 +02:00
Add files via upload
This commit is contained in:
+155
-13
@@ -40460,30 +40460,88 @@ input.theme-checkbox:disabled {
|
||||
.asset-editor-group-heading p { min-width: 0; margin: 0; color: var(--text-secondary, #64748b); font-size: 12px; font-weight: 400; line-height: 1.45; }
|
||||
.asset-editor-wide { grid-column: 1 / -1; }
|
||||
.asset-editor-submit-busy { pointer-events: none; opacity: .7; }
|
||||
.asset-detail-modal { overflow: hidden; }
|
||||
.asset-detail-modal { overflow: hidden; background: rgba(15,23,42,.48); }
|
||||
.asset-detail-modal .asset-detail-content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: min(620px, 92vw);
|
||||
width: min(680px, 94vw);
|
||||
max-width: none;
|
||||
height: 100%;
|
||||
max-height: none;
|
||||
margin: 0;
|
||||
border-width: 0 0 0 1px;
|
||||
border-radius: 18px 0 0 18px;
|
||||
animation: assetDrawerIn .22s ease-out;
|
||||
border-color: var(--border-color, #e5e7eb);
|
||||
border-radius: 16px 0 0 16px;
|
||||
background: var(--bg-primary, #fff);
|
||||
box-shadow: -12px 0 32px rgba(15,23,42,.14);
|
||||
animation: assetDrawerIn .20s ease-out;
|
||||
}
|
||||
.asset-detail-modal .modal-header {
|
||||
align-items: flex-start;
|
||||
padding: 20px 24px 16px;
|
||||
border-radius: 16px 0 0 0;
|
||||
background: var(--bg-primary, #fff);
|
||||
box-shadow: none;
|
||||
}
|
||||
.asset-detail-modal .modal-header h2 { font-size: 19px; letter-spacing: 0; }
|
||||
.asset-detail-modal .modal-header .asset-page-subtitle { max-width: 520px; margin-top: 6px; font-size: 13px; color: var(--text-secondary, #64748b); overflow-wrap: anywhere; }
|
||||
.asset-detail-modal .modal-close { margin-top: -2px; }
|
||||
.asset-detail-modal .modal-body {
|
||||
padding: 18px 24px 22px;
|
||||
background: transparent;
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
.asset-detail-modal .modal-footer {
|
||||
flex-shrink: 0;
|
||||
padding: 14px 24px;
|
||||
border-top: 1px solid var(--border-color, #e5e7eb);
|
||||
background: var(--bg-primary, #fff);
|
||||
box-shadow: 0 -6px 18px rgba(15,23,42,.04);
|
||||
}
|
||||
.asset-detail-modal .modal-header { border-radius: 18px 0 0 0; }
|
||||
.asset-detail-modal .modal-body { padding: 20px; }
|
||||
.asset-detail-modal .modal-footer { flex-shrink: 0; }
|
||||
@keyframes assetDrawerIn { from { opacity: .7; transform: translateX(100%); } to { opacity: 1; transform: translateX(0); } }
|
||||
.asset-detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; overflow: hidden; border: 1px solid var(--border-color, #e5e7eb); border-radius: 12px; background: var(--border-color, #e5e7eb); }
|
||||
.asset-detail-item { min-width: 0; padding: 15px 16px; background: var(--card-bg, #fff); }
|
||||
.asset-detail-grid { display: flex; flex-direction: column; gap: 14px; }
|
||||
.asset-detail-overview {
|
||||
padding: 16px;
|
||||
border: 1px solid var(--border-color, #e5e7eb);
|
||||
border-radius: 8px;
|
||||
background: var(--card-bg, #fff);
|
||||
box-shadow: none;
|
||||
}
|
||||
.asset-detail-overview-main { min-width: 0; }
|
||||
.asset-detail-target { color: var(--text-primary, #111827); font-size: 18px; font-weight: 700; line-height: 1.35; overflow-wrap: anywhere; }
|
||||
.asset-detail-meta-line { margin-top: 6px; color: var(--text-secondary, #64748b); font-size: 13px; line-height: 1.45; overflow-wrap: anywhere; }
|
||||
.asset-detail-overview-badges { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
|
||||
.asset-detail-overview-badge {
|
||||
display: inline-flex;
|
||||
max-width: 100%;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 3px 8px;
|
||||
border: 1px solid var(--border-color, #e5e7eb);
|
||||
border-radius: 6px;
|
||||
background: var(--bg-secondary, #f8fafc);
|
||||
color: var(--text-primary, #111827);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.asset-detail-overview-badge span { color: var(--text-secondary, #64748b); font-weight: 500; }
|
||||
.asset-detail-overview-badge--success { color: #047857; }
|
||||
.asset-detail-overview-badge--neutral { color: #475569; }
|
||||
.asset-detail-overview-badge--critical,.asset-detail-overview-badge--high { color: #b91c1c; }
|
||||
.asset-detail-overview-badge--medium { color: #b45309; }
|
||||
.asset-detail-overview-badge--low,.asset-detail-overview-badge--info,.asset-detail-overview-badge--normal { color: #2563eb; }
|
||||
.asset-detail-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
|
||||
.asset-detail-item { min-width: 0; padding: 14px 15px; border: 1px solid var(--border-color, #e5e7eb); border-radius: 8px; background: var(--card-bg, #fff); box-shadow: none; }
|
||||
.asset-detail-item:hover { border-color: var(--border-color, #e5e7eb); box-shadow: none; transform: none; }
|
||||
.asset-detail-item--wide { grid-column: 1 / -1; }
|
||||
.asset-detail-item > span { display: block; margin-bottom: 7px; color: var(--text-secondary, #64748b); font-size: 12px; }
|
||||
.asset-detail-item > div { min-height: 20px; color: var(--text-primary, #111827); overflow-wrap: anywhere; }
|
||||
.asset-detail-item code { display: block; padding: 9px; border-radius: 7px; background: var(--bg-secondary, #f4f6f8); white-space: pre-wrap; }
|
||||
.asset-detail-item > span { display: block; margin-bottom: 7px; color: var(--text-secondary, #64748b); font-size: 12px; font-weight: 650; }
|
||||
.asset-detail-item > div { min-height: 20px; color: var(--text-primary, #111827); font-size: 14px; line-height: 1.45; overflow-wrap: anywhere; }
|
||||
.asset-detail-item .muted { color: var(--text-muted, #94a3b8); }
|
||||
.asset-detail-item--code { background: var(--card-bg, #fff); }
|
||||
.asset-detail-item code { display: block; max-height: 170px; padding: 10px; overflow: auto; border: 1px solid var(--border-color, #e5e7eb); border-radius: 6px; background: var(--bg-secondary, #f4f6f8); color: var(--text-primary, #111827); font-size: 12px; line-height: 1.55; white-space: pre-wrap; }
|
||||
|
||||
/* Asset management dark theme */
|
||||
html[data-theme="dark"] .asset-stat-card,
|
||||
@@ -40617,11 +40675,69 @@ html[data-theme="dark"] .theme-checkbox:indeterminate {
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .asset-detail-grid {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .asset-detail-modal {
|
||||
background: rgba(2,6,23,.62);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .asset-detail-modal .asset-detail-content {
|
||||
border-color: var(--border-color);
|
||||
background: var(--border-color);
|
||||
background: var(--bg-primary);
|
||||
box-shadow: -12px 0 34px rgba(0,0,0,.32);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .asset-detail-modal .modal-header {
|
||||
background: var(--bg-primary);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .asset-detail-modal .modal-footer {
|
||||
border-color: var(--border-color);
|
||||
background: var(--bg-primary);
|
||||
box-shadow: 0 -6px 18px rgba(0,0,0,.16);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .asset-detail-overview {
|
||||
border-color: var(--border-color);
|
||||
background: var(--card-bg);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .asset-detail-overview-badge {
|
||||
border-color: var(--border-color);
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .asset-detail-overview-badge span {
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .asset-detail-overview-badge--success { color: #6ee7b7; }
|
||||
html[data-theme="dark"] .asset-detail-overview-badge--neutral { color: #cbd5e1; }
|
||||
html[data-theme="dark"] .asset-detail-overview-badge--critical,
|
||||
html[data-theme="dark"] .asset-detail-overview-badge--high { color: #fca5a5; }
|
||||
html[data-theme="dark"] .asset-detail-overview-badge--medium { color: #fcd34d; }
|
||||
|
||||
html[data-theme="dark"] .asset-detail-item {
|
||||
border-color: var(--border-color);
|
||||
background: var(--card-bg);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .asset-detail-item:hover {
|
||||
border-color: var(--border-color);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .asset-detail-item--code {
|
||||
background: var(--card-bg);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .asset-detail-item code {
|
||||
border-color: var(--border-color);
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
@@ -40660,5 +40776,31 @@ html[data-theme="dark"] .asset-form-error { color: #fca5a5; }
|
||||
.asset-recent-card { min-height: 230px; }
|
||||
}
|
||||
@media (max-width: 1100px) { .asset-advanced-filters { grid-template-columns: repeat(3,minmax(140px,1fr)); } }
|
||||
@media (max-width: 760px) {
|
||||
.asset-detail-modal .asset-detail-content {
|
||||
width: 100%;
|
||||
border-left: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.asset-detail-modal .modal-header {
|
||||
padding: 18px 18px 15px;
|
||||
border-radius: 0;
|
||||
}
|
||||
.asset-detail-modal .modal-body {
|
||||
padding: 16px 18px 20px;
|
||||
}
|
||||
.asset-detail-modal .modal-footer {
|
||||
padding: 14px 18px;
|
||||
}
|
||||
.asset-detail-target {
|
||||
font-size: 19px;
|
||||
}
|
||||
.asset-detail-fields {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.asset-detail-item--wide {
|
||||
grid-column: auto;
|
||||
}
|
||||
}
|
||||
@media (max-width: 640px) { .asset-editor-grid,.asset-detail-grid { grid-template-columns: 1fr; } .asset-total-card,.asset-recent-card,.asset-panel { padding: 18px; } .asset-type-grid,.asset-coverage-metrics { grid-template-columns: 1fr; } .asset-type-item + .asset-type-item { border-top: 1px solid var(--border-color,#e5e7eb); border-left: 0; } .asset-trend-header { grid-template-columns: 1fr auto; grid-template-areas: "heading summary"; column-gap: 12px; } .asset-chart-legend { display: none; } .asset-chart-summary { justify-content: flex-end; padding: 0; } .asset-chart-summary span { display: none; } .asset-protocol-compact .asset-bar-row,.asset-protocol-compact .asset-protocol-columns { grid-template-columns: 72px 1fr 42px; gap: 8px; padding-right: 4px; padding-left: 4px; } .asset-protocol-compact .asset-bar-row small,.asset-protocol-compact .asset-protocol-columns span:last-child { display: none; } .asset-editor-content,.asset-import-content,.asset-bulk-edit-content { width: 100%; max-height: 100vh; min-height: 100vh; margin: 0; border-radius: 0; } .asset-editor-content .modal-header,.asset-editor-body,.asset-editor-content .modal-footer { padding-left: 16px; padding-right: 16px; } .asset-editor-subtitle { max-width: 270px; } .asset-editor-primary { padding: 14px; } .asset-editor-group-heading { align-items: flex-start; flex-wrap: wrap; gap: 3px 8px; } .asset-editor-group-heading p { width: 100%; } .asset-editor-section-group + .asset-editor-section-group { margin-top: 20px; padding-top: 20px; } .asset-editor-wide,.asset-detail-item--wide { grid-column: auto; } .asset-toolbar-main > input { min-width: 100%; } .asset-advanced-filters { grid-template-columns: 1fr 1fr; } .asset-saved-views { align-items: stretch; flex-direction: column; } .asset-saved-views select { width: 100%; min-width: 0; } .asset-custom-select--saved-view { width: 100%; min-width: 0; flex-basis: auto; } .asset-import-template-actions { align-items: flex-start; flex-direction: column; padding-left: 0; } .asset-import-preview-table th:nth-child(3),.asset-import-preview-table td:nth-child(3),.asset-import-preview-table th:nth-child(4),.asset-import-preview-table td:nth-child(4) { display: none; } .asset-batch-selection { flex-wrap: wrap; } .asset-batch-primary-actions { display: grid; grid-template-columns: 1fr 1fr; } .asset-batch-primary-actions > .btn-primary { grid-column: 1 / -1; grid-row: 1; } .asset-batch-more { width: 100%; } .asset-batch-more > summary { width: 100%; } .asset-batch-menu { right: auto; left: 0; width: min(260px,calc(100vw - 48px)); } }
|
||||
@media (max-width: 640px) { .asset-coverage-metrics > div + div,.asset-coverage-metrics > div:nth-child(4) { border-top: 1px solid var(--border-color,#e5e7eb); border-left: 0; } }
|
||||
|
||||
@@ -440,6 +440,8 @@
|
||||
"open": "Open",
|
||||
"unbindProjectTitle": "Unbind project",
|
||||
"unbind": "Unbind",
|
||||
"unbindAssetConfirm": "Unbind “{{target}}” from this project? The asset will not be deleted.",
|
||||
"unbindAssetDone": "Asset unbound from project",
|
||||
"confirmUnbindConversation": "Unbind this conversation from current project?",
|
||||
"unbindFailed": "Unbind failed",
|
||||
"factMetaCategory": "Category: {{value}}",
|
||||
|
||||
@@ -428,6 +428,8 @@
|
||||
"open": "打开",
|
||||
"unbindProjectTitle": "解除项目绑定",
|
||||
"unbind": "解绑",
|
||||
"unbindAssetConfirm": "确定将“{{target}}”从当前项目解绑吗?资产不会被删除。",
|
||||
"unbindAssetDone": "已从项目解绑资产",
|
||||
"confirmUnbindConversation": "解除该对话与当前项目的绑定?",
|
||||
"unbindFailed": "解绑失败",
|
||||
"factMetaCategory": "分类: {{value}}",
|
||||
|
||||
+32
-4
@@ -1630,8 +1630,36 @@ function importFofaRowAsset(index) {
|
||||
return importFofaAssetsByIndexes([Number(index)]);
|
||||
}
|
||||
|
||||
function assetDetailItem(label, value, wide) {
|
||||
return `<div class="asset-detail-item${wide ? ' asset-detail-item--wide' : ''}"><span>${escapeHtml(label)}</span><div>${value || '<span class="muted">-</span>'}</div></div>`;
|
||||
function assetDetailItem(label, value, wide, options) {
|
||||
const itemClass = ['asset-detail-item'];
|
||||
if (wide) itemClass.push('asset-detail-item--wide');
|
||||
if (options?.className) itemClass.push(options.className);
|
||||
return `<div class="${itemClass.join(' ')}"><span>${escapeHtml(label)}</span><div>${value || '<span class="muted">-</span>'}</div></div>`;
|
||||
}
|
||||
|
||||
function assetDetailBadge(label, value, modifier) {
|
||||
if (!value) return '';
|
||||
const badgeClass = modifier ? ` asset-detail-overview-badge--${modifier}` : '';
|
||||
return `<span class="asset-detail-overview-badge${badgeClass}"><span>${escapeHtml(label)}</span>${escapeHtml(value)}</span>`;
|
||||
}
|
||||
|
||||
function assetDetailOverview(asset) {
|
||||
const service = [asset.protocol, asset.port ? ':' + asset.port : ''].join('');
|
||||
const location = [asset.country, asset.province, asset.city].filter(Boolean).join(' / ');
|
||||
const statusLabel = asset.status === 'inactive' ? assetT('assets.statusInactive', '停用') : assetT('assets.statusActive', '活跃');
|
||||
const risk = assetRiskPresentation(asset.risk_level);
|
||||
return `<section class="asset-detail-overview">
|
||||
<div class="asset-detail-overview-main">
|
||||
<div class="asset-detail-target">${escapeHtml(assetTargetLabel(asset))}</div>
|
||||
<div class="asset-detail-meta-line">${escapeHtml([asset.ip, asset.domain, service].filter(Boolean).join(' · ') || '-')}</div>
|
||||
</div>
|
||||
<div class="asset-detail-overview-badges">
|
||||
${assetDetailBadge(assetT('assets.status', '状态'), statusLabel, asset.status === 'inactive' ? 'neutral' : 'success')}
|
||||
${assetDetailBadge(assetT('assets.risk', '风险'), risk.label, risk.level)}
|
||||
${assetDetailBadge(assetT('assets.source', '来源'), asset.source || '', 'source')}
|
||||
${assetDetailBadge(assetT('assets.location', '地区'), location, 'location')}
|
||||
</div>
|
||||
</section>`;
|
||||
}
|
||||
|
||||
function openAssetDetail(index) {
|
||||
@@ -1662,7 +1690,7 @@ function openAssetDetailRecord(asset) {
|
||||
assetDetailItem(assetT('assets.environment', '环境'), escapeHtml(asset.environment || '')),
|
||||
assetDetailItem(assetT('assets.criticality', '重要性'), escapeHtml(asset.criticality || '')),
|
||||
assetDetailItem(assetT('assets.source', '来源'), escapeHtml(asset.source || '')),
|
||||
assetDetailItem(assetT('assets.sourceQuery', '来源查询'), asset.source_query ? `<code>${escapeHtml(asset.source_query)}</code>` : ''),
|
||||
assetDetailItem(assetT('assets.sourceQuery', '来源查询'), asset.source_query ? `<code>${escapeHtml(asset.source_query)}</code>` : '', true, { className: 'asset-detail-item--code' }),
|
||||
assetDetailItem(assetT('assets.tagsLabel', '标签'), tags, true),
|
||||
assetDetailItem(assetT('assets.firstSeen', '首次发现'), escapeHtml(asset.first_seen_at ? new Date(asset.first_seen_at).toLocaleString() : '')),
|
||||
assetDetailItem(assetT('assets.lastSeen', '最近发现'), escapeHtml(asset.last_seen_at ? new Date(asset.last_seen_at).toLocaleString() : '')),
|
||||
@@ -1670,7 +1698,7 @@ function openAssetDetailRecord(asset) {
|
||||
assetDetailItem(assetT('assets.relatedVulnerabilities', '相关漏洞'), String(Number(asset.vulnerability_count || 0)))
|
||||
];
|
||||
const grid = document.getElementById('asset-detail-grid');
|
||||
if (grid) grid.innerHTML = values.join('');
|
||||
if (grid) grid.innerHTML = assetDetailOverview(asset) + `<div class="asset-detail-fields">${values.join('')}</div>`;
|
||||
if (typeof applyRBACToUI === 'function') applyRBACToUI(document.getElementById('asset-detail-modal'));
|
||||
if (typeof openAppModal === 'function') openAppModal('asset-detail-modal');
|
||||
else document.getElementById('asset-detail-modal').style.display = 'flex';
|
||||
|
||||
@@ -1096,7 +1096,7 @@ async function loadProjectAssets(page) {
|
||||
<td>${escapeHtml(asset.source || '-')}</td>
|
||||
<td>${escapeHtml(updated)}</td>
|
||||
<td><span class="asset-status asset-status--${escapeHtml(asset.status || 'active')}">${escapeHtml(status)}</span></td>
|
||||
<td class="col-actions"><div class="projects-table-actions"><button type="button" class="projects-action-btn projects-action-btn--view" onclick="openProjectAssetDetail(${index})">${escapeHtml(tpFmt('common.view', '查看'))}</button></div></td>
|
||||
<td class="col-actions"><div class="projects-table-actions"><button type="button" class="projects-action-btn projects-action-btn--mute" data-require-permission="asset:write" onclick="unbindAssetFromProject(${index})" title="${escapeHtml(tp('projects.unbindProjectTitle'))}">${escapeHtml(tp('projects.unbind'))}</button></div></td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
renderProjectAssetsPagination();
|
||||
@@ -1147,6 +1147,29 @@ function openProjectAssetDetail(index) {
|
||||
if (asset && typeof window.openAssetDetailRecord === 'function') window.openAssetDetailRecord(asset);
|
||||
}
|
||||
|
||||
async function unbindAssetFromProject(index) {
|
||||
const asset = currentProjectAssets[Number(index)];
|
||||
if (!asset || !asset.id || !currentProjectId) return;
|
||||
const target = asset.host || asset.domain || asset.ip || asset.id;
|
||||
const message = tpFmt('projects.unbindAssetConfirm', `确定将“${target}”从当前项目解绑吗?资产不会被删除。`, { target });
|
||||
if (!confirm(message)) return;
|
||||
try {
|
||||
const res = await apiFetch('/api/assets/project-binding', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ asset_ids: [asset.id], project_id: '' })
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
if (typeof showInlineToast === 'function') {
|
||||
showInlineToast(tpFmt('projects.unbindAssetDone', '已从项目解绑资产', { target }));
|
||||
}
|
||||
await loadProjectAssets(projectAssetsPagination.page);
|
||||
await refreshProjectHeaderStats();
|
||||
} catch (error) {
|
||||
alert(`${tp('projects.unbindFailed')}: ${error.message || error}`);
|
||||
}
|
||||
}
|
||||
|
||||
let _selectedGraphFactKey = null;
|
||||
let _selectedGraphEdgeId = null;
|
||||
let _currentGraphData = null;
|
||||
@@ -2879,6 +2902,7 @@ window.unbindConversationFromProject = unbindConversationFromProject;
|
||||
window.loadProjectConversations = loadProjectConversations;
|
||||
window.loadProjectAssets = loadProjectAssets;
|
||||
window.openProjectAssetDetail = openProjectAssetDetail;
|
||||
window.unbindAssetFromProject = unbindAssetFromProject;
|
||||
window.loadProjectFactGraph = loadProjectFactGraph;
|
||||
window.filterProjectFactGraph = filterProjectFactGraph;
|
||||
window.centerProjectFactGraph = centerProjectFactGraph;
|
||||
|
||||
Reference in New Issue
Block a user