修复
1、修复流量包里的复制按钮不工作问题 2、修复新建任务关联资产不同步到任务测试范围问题
This commit is contained in:
1 parent
3675bd815c
commit
cf0989c67d
10 files changed
+297
-32
No files matched your search
@@ -2613,8 +2613,8 @@ function CreateTaskSheet({
|
||||
portalContainer={sheetContentRef}
|
||||
/>
|
||||
<FieldDescription>
|
||||
将所选企业的域名、IP、CIDR、ICP 和企业关键词作为 Agent
|
||||
可选范围上下文;不会自动生成意图或强制改变执行目标。
|
||||
创建任务时会将所选企业当前已有资产加入“测试资产”,并将域名、IP、CIDR、ICP 和企业关键词提供给 Agent
|
||||
作为范围上下文;不会自动生成意图或强制改变执行目标。
|
||||
</FieldDescription>
|
||||
</Field>
|
||||
<Field>
|
||||
|
||||
@@ -39,7 +39,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { api } from "@/lib/api";
|
||||
import type { TrafficDetail, TrafficExchange, TrafficHost, TrafficResp } from "@/lib/types";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn, copyText } from "@/lib/utils";
|
||||
|
||||
function fmtTime(ts: string) {
|
||||
return new Date(ts).toLocaleString("zh-CN", {
|
||||
@@ -199,12 +199,12 @@ function HttpCodeBlock({ raw }: { raw: string }) {
|
||||
}, [copied]);
|
||||
|
||||
const copyPacket = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(value);
|
||||
const ok = await copyText(value);
|
||||
if (ok) {
|
||||
setCopied(true);
|
||||
} catch {
|
||||
toast.error("复制失败,请使用 Ctrl/Cmd+A 后复制");
|
||||
return;
|
||||
}
|
||||
toast.error("复制失败,请使用 Ctrl/Cmd+A 后复制");
|
||||
};
|
||||
|
||||
const renderLine = (line: string, index: number) => {
|
||||
|
||||
+63
-16
@@ -42,9 +42,35 @@ const mockCompanies = structuredClone(D.companies);
|
||||
const mockAssets = structuredClone(D.assets);
|
||||
const mockTaskAssetIDs = new Map(D.tasks.map((task, index) => [task.id, index + 1]));
|
||||
const mockTaskScopes = new Map<string, TaskScopeRow[]>();
|
||||
type MockTaskAssetSource = Pick<Asset, "task_source" | "task_source_summary" | "task_source_node_id">;
|
||||
const mockTaskAssetSources = new Map<string, MockTaskAssetSource>();
|
||||
let nextMockTaskAssetID = D.tasks.length + 1;
|
||||
let mockActiveTask = D.ACTIVE_TASK;
|
||||
|
||||
function mockTaskAssetSourceKey(taskID: string, assetID: number): string {
|
||||
return JSON.stringify([taskID, assetID]);
|
||||
}
|
||||
|
||||
function setMockTaskAssetSource(taskID: string, assetID: number, source: MockTaskAssetSource) {
|
||||
mockTaskAssetSources.set(mockTaskAssetSourceKey(taskID, assetID), source);
|
||||
}
|
||||
|
||||
function mockAssetForTask(taskID: string, asset: Asset): Asset {
|
||||
const source = mockTaskAssetSources.get(mockTaskAssetSourceKey(taskID, asset.id));
|
||||
return source ? { ...asset, ...source } : asset;
|
||||
}
|
||||
|
||||
function deleteMockTaskAssetSources(taskID: string, assetID?: number) {
|
||||
if (assetID !== undefined) {
|
||||
mockTaskAssetSources.delete(mockTaskAssetSourceKey(taskID, assetID));
|
||||
return;
|
||||
}
|
||||
for (const key of mockTaskAssetSources.keys()) {
|
||||
const [linkedTaskID] = JSON.parse(key) as [string, number];
|
||||
if (linkedTaskID === taskID) mockTaskAssetSources.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
function mockTaskAssetID(taskID: string): number | undefined {
|
||||
const numeric = Number(taskID);
|
||||
if (Number.isInteger(numeric) && numeric > 0) return numeric;
|
||||
@@ -289,7 +315,10 @@ function route(m: string, path: string, seg: string[], q: URLSearchParams, b: Re
|
||||
const now = new Date();
|
||||
const profileIDs = [...((b.llm_profile_ids as number[] | undefined) ?? [])];
|
||||
const sourceTaskIDs = [...((b.source_task_ids as string[] | undefined) ?? [])];
|
||||
const companyIDs = [...((b.company_ids as number[] | undefined) ?? [])];
|
||||
const companyIDs = [...new Set((b.company_ids as number[] | undefined) ?? [])];
|
||||
if (companyIDs.some((companyID) => !mockCompanies.some((company) => company.id === companyID))) {
|
||||
throw new Error("关联企业不存在或无效");
|
||||
}
|
||||
const categoryID = typeof b.category_id === "number" ? b.category_id : undefined;
|
||||
const category = categoryID === undefined ? undefined : mockTaskCategories.find((item) => item.id === categoryID);
|
||||
if (categoryID !== undefined && !category) throw new Error("任务分类不存在");
|
||||
@@ -318,7 +347,19 @@ function route(m: string, path: string, seg: string[], q: URLSearchParams, b: Re
|
||||
source_task_ids: sourceTaskIDs,
|
||||
company_ids: companyIDs,
|
||||
};
|
||||
mockTaskAssetIDs.set(id, nextMockTaskAssetID++);
|
||||
const numericTaskID = nextMockTaskAssetID++;
|
||||
mockTaskAssetIDs.set(id, numericTaskID);
|
||||
const selectedCompanies = new Set(companyIDs);
|
||||
for (const asset of mockAssets) {
|
||||
if (asset.company_id === undefined || !selectedCompanies.has(asset.company_id)) continue;
|
||||
if (!asset.task_ids.includes(numericTaskID)) asset.task_ids.push(numericTaskID);
|
||||
const company = mockCompanies.find((candidate) => candidate.id === asset.company_id);
|
||||
setMockTaskAssetSource(id, asset.id, {
|
||||
task_source: "company",
|
||||
task_source_summary: `任务创建时关联企业:${company?.name ?? `#${asset.company_id}`}`,
|
||||
task_source_node_id: undefined,
|
||||
});
|
||||
}
|
||||
for (const item of mockTasks) item.active = false;
|
||||
mockTasks.unshift(created);
|
||||
mockActiveTask = id;
|
||||
@@ -462,9 +503,14 @@ function route(m: string, path: string, seg: string[], q: URLSearchParams, b: Re
|
||||
}
|
||||
if (seg[0] === "tasks" && seg.length === 2 && m === "DELETE") {
|
||||
const id = seg[1];
|
||||
const numericTaskID = mockTaskAssetID(id);
|
||||
const index = mockTasks.findIndex((item) => item.id === id);
|
||||
if (index >= 0) mockTasks.splice(index, 1);
|
||||
mockTaskAssetIDs.delete(id);
|
||||
deleteMockTaskAssetSources(id);
|
||||
if (numericTaskID !== undefined) {
|
||||
for (const asset of mockAssets) asset.task_ids = asset.task_ids.filter((taskID) => taskID !== numericTaskID);
|
||||
}
|
||||
|
||||
let findingsDeleted = 0;
|
||||
if (b.delete_findings) {
|
||||
@@ -701,7 +747,7 @@ function route(m: string, path: string, seg: string[], q: URLSearchParams, b: Re
|
||||
});
|
||||
const limit = Number(q.get("limit") ?? 50);
|
||||
const offset = Number(q.get("offset") ?? 0);
|
||||
const page = list.slice(offset, offset + limit);
|
||||
const page = list.slice(offset, offset + limit).map((asset) => (taskID ? mockAssetForTask(taskID, asset) : asset));
|
||||
return { count: page.length, total: list.length, assets: page };
|
||||
}
|
||||
if (path === "/assets" && m === "DELETE") {
|
||||
@@ -792,9 +838,11 @@ function route(m: string, path: string, seg: string[], q: URLSearchParams, b: Re
|
||||
asset.task_ids.push(numericTaskID);
|
||||
mutation.assets_linked++;
|
||||
}
|
||||
asset.task_source = "manual";
|
||||
asset.task_source_summary = "用户在测试资产页手工新增";
|
||||
asset.task_source_node_id = undefined;
|
||||
setMockTaskAssetSource(seg[1], asset.id, {
|
||||
task_source: "manual",
|
||||
task_source_summary: "用户在测试资产页手工新增",
|
||||
task_source_node_id: undefined,
|
||||
});
|
||||
}
|
||||
mockTaskScopes.set(seg[1], currentScopes);
|
||||
return mutation;
|
||||
@@ -812,9 +860,11 @@ function route(m: string, path: string, seg: string[], q: URLSearchParams, b: Re
|
||||
asset.task_ids.push(numericTaskID);
|
||||
mutation.attached++;
|
||||
}
|
||||
asset.task_source = "manual";
|
||||
asset.task_source_summary = sourceSummary;
|
||||
asset.task_source_node_id = undefined;
|
||||
setMockTaskAssetSource(seg[1], asset.id, {
|
||||
task_source: "manual",
|
||||
task_source_summary: sourceSummary,
|
||||
task_source_node_id: undefined,
|
||||
});
|
||||
}
|
||||
return mutation;
|
||||
}
|
||||
@@ -824,11 +874,7 @@ function route(m: string, path: string, seg: string[], q: URLSearchParams, b: Re
|
||||
if (numericTaskID === undefined || !asset) throw new Error("任务或资产不存在");
|
||||
if (!asset.task_ids.includes(numericTaskID)) throw new Error("资产未关联当前任务");
|
||||
asset.task_ids = asset.task_ids.filter((id) => id !== numericTaskID);
|
||||
if (asset.task_ids.length === 0) {
|
||||
asset.task_source = undefined;
|
||||
asset.task_source_summary = undefined;
|
||||
asset.task_source_node_id = undefined;
|
||||
}
|
||||
deleteMockTaskAssetSources(seg[1], asset.id);
|
||||
return { detached: asset.id };
|
||||
}
|
||||
if (seg[0] === "tasks" && seg[2] === "intent-assets" && seg.length === 3 && m === "GET") {
|
||||
@@ -843,8 +889,9 @@ function route(m: string, path: string, seg: string[], q: URLSearchParams, b: Re
|
||||
}
|
||||
const sourceTaskID = mockTaskAssetID(seg[1]) ?? 0;
|
||||
const assets: IntentAsset[] = mappings.flatMap((mapping) => {
|
||||
const asset = mockAssets.find((item) => item.id === mapping.assetID);
|
||||
if (!asset) return [];
|
||||
const storedAsset = mockAssets.find((item) => item.id === mapping.assetID);
|
||||
if (!storedAsset) return [];
|
||||
const asset = mockAssetForTask(seg[1], storedAsset);
|
||||
return [
|
||||
{
|
||||
intent_id: mapping.intentID,
|
||||
|
||||
@@ -13,6 +13,7 @@ const TASK_ASSET_SOURCE_LABELS: Record<string, string> = {
|
||||
agent: "Agent 发现",
|
||||
anchor: "黑板锚点",
|
||||
api: "资产 API",
|
||||
company: "企业关联",
|
||||
legacy: "历史关联",
|
||||
manual: "人工加入",
|
||||
system: "系统关联",
|
||||
|
||||
@@ -35,7 +35,7 @@ export interface Task {
|
||||
llm_failover_state?: "default" | "ready" | "chain_exhausted" | string;
|
||||
llm_failover_reason?: string;
|
||||
source_task_ids?: string[]; // directly related tasks inherited as read-only context
|
||||
company_ids?: number[]; // associated company asset scopes available as task context
|
||||
company_ids?: number[]; // associated company scopes; current company assets join the task at creation
|
||||
coverage_enabled?: boolean; // 资产覆盖度功能开关(创建时定,默认开);false=不计算/不展示覆盖度
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user