npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

vue-safe-force-graph

v2.0.85

Published

force_graph in security area

Downloads

707

Readme

vue-safe-force-graph

vue-safe-force-graph 是一个使用 Vue3、d3.js 、 canvas 开发的可视化溯源关系图。

安装

npm install vue-safe-force-graph

使用方式

可以在组件中引入 或者可以在全局引入注册组件。

方式一:组件中引入

在vue组件中引入Graph组件和样式:

import { Graph } from 'vue-safe-force-graph'
import 'vue-safe-force-graph/lib/style.css'

完整index.vue示例如下:

<template>
  <div class="graph-container">
    <Graph
      :graph-api="graphApi"
      :module="module"
      :start-node="startNode"
      :enter-start-node="true"
      :has-level="true"
      :snapshot_id="snapshotId"
      :vcode="vcode"
      :handle-select-node-event="handleSelectNode"
      :custom-context-menu-event="handleEvent"
      :singles-node-max-num="500"
      @handle-data-loaded="handleDataLoaded"
    />
  </div>
</template>
<script setup>

import {
  getGraphConfig, getGraphRequest, getNodesDetail, getSnapshotDetail,
} from 'src/api/graph'
import { ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import request from '@/utils/request'
import { Graph } from 'vue-safe-force-graph'
import 'vue-safe-force-graph/lib/style.css'

const module = ref('graph')
const startNode = ref([])
const vcode = ref(null)
const snapshotId = ref(null)
const forceGraph = ref(null)

function addSnapshot(params) {
  return request({
    url: `/graph/v1/snapshot/add?module=${module.value}`,
    method: 'post',
    data: params
  })
}

const graphApi = {
  getGraphConfig: getGraphConfig,
  getGraphRequest: getGraphRequest,
  getNodesDetail: getNodesDetail,
  addSnapshot1: addSnapshot,
  getSnapshotDetail: getSnapshotDetail,
}

const route = useRoute()

snapshotId.value = route.query.snapshot_id
vcode.value = route.query.vcode

if (route.query.type && route.query.id) {
  startNode.value = [
    {
      type: route.query.type,
      id: route.query.id
    }
  ]
}

const handleHighLight = (type, prop, value) => {
  console.log('forceGraph', forceGraph.value)
  forceGraph.value.handleCustomSelect(type, prop, value)
}

const handleEvent = (data) => {
  console.log('监听菜单事件', data)
}
const handleSelectNode = (data) => {
  console.log('handleSelectNode', data)
}
function handleDataLoaded(data) {
  console.log('handleDataLoaded', data)
}
</script>
<style lang="scss">
.graph-container {
  height: calc(100vh - 66px);
}
</style>

方式二:全局引入

在main.js中引入并注册。

// main.js
import { createApp } from 'vue'
import Graph from 'vue-safe-force-graph'
import 'vue-safe-force-graph/lib/style.css'
const app = createApp(App)
app.use(Graph)

在自己的业务组件中使用,全局注册的组件名为ForceGraphByCanvas,示例如下 :

<template>
  <ForceGraphByCanvas
      :graph-api="graphApi"
      module="md5graph"
      :start-node="startNode"
      :enter-start-node="true"
      :has-level="true"
      :snapshot_id="snapshotId"
      :vcode="vcode"
      :handle-select-node-event="handleSelectNode"
      :custom-context-menu-event="handleEvent"
      @handle-data-loaded="handleDataLoaded"
    />
</template>

<script setup>
// 此处js代码参考上面组件引入的实例
</script>

组件属性

| 属性名 | 说明 | 类型 | 默认值 | 是否必填 | | ------------------------ | ------------------------------------------------------------ | -------------- | ------------------------------------------------------------ | -------- | | graph-api | 图数据相关接口,包含config、节点数据、节点详情接口 | Object | 无 | 是 | | module | 当前图模块 用于 获取config接口时的传参。 this.graphApi?.getGraphConfig({name: this.module}).then(() => {}) | String | 无 | 是 | | start-node | 开始节点,如 startNode = [{type: "ip",id: "193.36.119.50"}] | Array | 无 | 是 | | enter-start-node | 起始节点时是否出现查询添加接口 | Boolean | false | 否 | | has-level | 是否有level等级概念 | Boolean | true | 否 | | snapshot_id | 快照id | String | 无 | 否 | | vcode | 快照密码 | String | 无 | 否 | | dark-mode | 是否为深色模式 | Boolean | false | 否 | | showPoint | 图左侧的legend图例部分,是否需要收藏 备注 标签 | Boolean | true | 否 | | isShowRightMenu | 是否展示右侧详情 部分 | Boolean | true | 否 | | hideButtonList | 需要隐藏的顶部按钮, 如 hideButtonList = ["新增节点"]。全部可选项为[ "新增节点", "删除", "保存快照", "保存图片", "数据导出", "数据导入", "撤销", "恢复", "自动布局", "全局固定", "全局解除固定", "树型布局", "斥力布局","展开折叠", "功能介绍", ] | Array | 无 | 否 | | isRenderStartNode | 是否渲染startNode。 | Boolean | true | 否 | | isDefaultSelectStartNode | 是否默认选中开始节点startNode | Boolean | true | 否 | | graphUrlPath | 用于生成快照后的图页面的地址路径。 例如当生成快照成功后,会生成一个链接,https://xxx.b.net/${graphUrlPath}?snapshot_id=xxx | String | '/graph' | 否 | | levelList | level危险级别 | Array | [ { level: '未知', // 级别。必传 levelName: '未知', // 必传。级别的名称 用于展示文案 className: 'unknown', // class样式名。必传 iconType: '', // 默认使用图标,传dot使用圆点样式 normalColor: '#666', // 颜色色值。必传 darkColor: '#c6cdd5', // 暗黑模式颜色色值,非必传。不传使用normalColor dangerNumber: 0, // 是否是 危险的局别。 用于统计危险数,对危险的节点增加样式等。和危险数的排序 }, { level: '危险', levelName: '危险', className: 'warn', iconType: '', // 默认使用图标,传dot使用圆点样式 normalColor: '#CF1322', darkColor: '#fb2400', dangerNumber: 1, }, { level: '普通', levelName: '普通', className: 'normal', iconType: '', // 默认使用图标,传dot使用圆点样式 normalColor: '#007EE2', darkColor: '#0094ff', dangerNumber: -1, }, { level: '安全', levelName: '安全', className: 'safe', iconType: '', // 默认使用图标,传dot使用圆点样式 normalColor: '#009A75', darkColor: '#00da90', dangerNumber: -2, }, ] | 否 | | showLevelCount | 是否在左侧图例legend的leve图标l旁边展示相关节点的数量。 | Boolean | false | 否 | | title | 在图的左上角设置标题,默认无 | String | Html | 无 | 否 | | isShowNodeMessage | 是否展示 渲染多少个节点的 message提示 | Boolean | true | 否 | | nodeTypeScale | 图中关系节点图标大小的缩放比例。如缩小0.5倍则传数字0.5 | Number | 1 | 否 | | singlesNodeMaxNum | 单个关系类型下的最大节点数量。如果节点数量超过此值,默认聚合隐藏起来 | Number | 100 | 否 | | handleSelectNodeEvent | 选择节点事件的props(兼容angular调用,vue可以使用事件方式) | Function | 无 | 否 | | customContextMenuEvent | 自定义右键菜单的点击事件,相关配置参考下面的自定义右键菜单。会返回数据 { eventName: 'api.name', customConfig: 'api.customConfig', nodes: 'nodes', allNodes: 'this.history.dataNow.nodes', }(兼容angular调用,vue可以使用事件方式) | Function | 无 | 否 | | handleDataLoaded | 图加载数据完成后 的事件通知。会返回图中所有的节点数据。(兼容angular调用,vue可以使用事件方式) | Function | 无 | 否 | | nodeWidth | 节点的宽度 | Number | 24 | 否 | | startNodeWidth | 起始节点的宽度 | Number | 24 | 否 | | linkDistance | 用于调整**d3.forceLink 的距离**:以确保link长度,值越大距离越长。d3文档 (当配置nodeWidth后可能会用到,比如nodewidth太大时,需要增加link距离) | Number | 120 | 否 | | strength | d3js的manyBody.strength属性。调整 d3.forceManyBody 的强度:增加或减少节点之间的排斥力,值为负值,负数越大斥力越大,如0表示无斥力,-600中等斥力。d3文档(当配置nodeWidth后可能会用到,比如nodewidth太大时,需要增加节点之间的斥力) | Number | -600 | 否 | | customTooltip | 自定义tooltip内容,传入一个函数,函数中能拿到节点数据,函数需返回html模板 | Function | | 否 | | edgeLabelMaxLength | 边上文字的最大长度,超过则省略展示,鼠标悬浮展示全部 | Number | 10 | 否 |

自定义右键菜单

在config接口中配置节点的右键菜单项,图组件拿到数据后渲染到页面上, 并绑定点击事件。点击后emit出去相关的数据。使用者监听该事件,然后自行处理后续流程。

菜单项配置在下面详情接口中,data => records => info => extraNodeContextMenus字段,如

{
    'code': '0',
    'msg': 'ok',
    'data': {
      'records': [
        {
          'summary': '',
          'tags': [],
          'info': {
            // 额外右键菜单项
            'extraNodeContextMenus': [
              {
                name: 'dealwith1', // 菜单唯一标识,用于点击事件传出去
                namec: '菜单1 无children', // 菜单展示的文案
                icon: 'button_edit_label', // 右键菜单的图标,不传的话默认一个图标
                customConfig: { // 透传自定义配置
                  input: 'xxxxx',
                  a: 'ceshi'
                }
              },
              {
                name: 'dealwith2',
                namec: '菜单2 无children',
                customConfig: {
                  input: 'xxxxx',
                  a: 'ceshi'
                },
                // children 二级菜单
                children: [
                  {
                    name: 'xxx',
                    namec: 'xxx',
                    customConfig: {
                      input: '333',
                      a: 'ces3333hi'
                    }
                  }
                ]
              },
              {
                name: 'dealwxxith3',
                namec: 'xxx',
                children: [
                  {
                    name: 'xxx',
                    namec: 'xxx',
                    children: [
                      {
                        name: 'threelevel',
                        namec: '三级菜单'
                      }
                    ]
                  }
                ]
              }
            ],
            'id': '1234',
            'name': '123',
            'type': 'url',
            'basic': {
              'level': '未知',
              'check_time': '',
              'desc': ''
            },
            'extends': null
          },
          'brief': []
        },
        {
          'summary': '',
          'tags': [],
          'info': {
            'id': '234234',
            'name': '234234',
            'type': 'url',
            'basic': {
              'level': '未知',
              'check_time': '',
              'desc': ''
            },
            'extends': null
          },
          'brief': []
        }
      ]
    }
  }

事件

| 事件名 | 说明 | 示例 | | ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | handle-data-loaded | 图加载数据完成后 的事件通知。会返回图中所有的节点数据 | <ForceGraphByCanvas :graph-api="graphApi" :module="module" :start-node="startNode" @handle-data-loaded="handleDataLoaded" @custom-contextmenu-click="handleContextmenu" @handle-select-node="handleSelectNode" /> | | custom-contextmenu-click | 自定义右键菜单的点击事件。返回 eventName 事件名 、customConfig当前点击的nodes 节点的整个数据, 所有节点allNodes | 如上 | | handle-select-node | 选择节点事件 | 如上 | | | | |

方法

| 方法名 | 说明 | 参数 | | ------------------ | ------------------------------------------------------------ | ---- | | controlOperation | 操作控制,传入node数据和对应api操作。如显示隐藏按钮,示例如下 | | | handleCustomSelect | 选择节点 | | | | | |

controlOperation示例
<ForceGraphByCanvas
  ref="forceGraph"
  graph-url-path="/hmGraphByCanvas"
  :graph-api="graphApi"
  :module="module"
  :start-node="startNode"
  @handle-data-loaded="handleDataLoaded"
/>

/*
* 参数格式 { nodes: [{ id: '8c5c0cda7d0f3ec28e818b441037cf2d' }], api: { name: 'show_node' }}
*  */
function showNode() {
  forceGraph.value.controlOperation({ nodes: [{ id: '8c5c0cda7d0f3ec28e818b441037cf2d' }], api: { name: 'show_node' }})
}
function hideNode() {
  forceGraph.value.controlOperation({ nodes: [{ id: '8c5c0cda7d0f3ec28e818b441037cf2d' }], api: { name: 'hide_node' }})
}

// 全部展示数据节点边的label
function allShowEdgeLabel() {
  forceGraph.value.controlOperation({ api: { name: 'all_show_edges_label' }})
}
// 全部隐藏数据节点边的label
function allHideEdgeLabel() {
  forceGraph.value.controlOperation({ api: { name: 'all_hide_edges_label' }})
}
controlOperation其他api name
  • edit_label修改label
  • single_edge_select 单边顶点选择
  • hold_aggregation单边顶点聚合
  • release_aggregation聚合顶点释放
  • node_star收藏
  • un_node_star取消收藏
  • show_label show_names 显示节点名称
  • un_show_names un_show_label 隐藏节点名称
  • fix_nodes固定节点
  • un_fix_nodes取消固定节点
  • delete_node删除节点
handleCustomSelect
const handleHighLight = () => {
  forceGraph.value.handleCustomSelect('include', 'id', ['xxxs0'])
}

图标加载方式

图标icon图片有2中加载方式:1从cdn中加载,2打包为base64到npm包中加载。

图谱中的图标icon默认是从cdn链接中加载的。如果您存在私有化部署不能访问公网的场景,可以联系作者打包base64版本。

其他配置项

1.关系节点增加统计数字

在config配置中的的plugs对应的新增一个 show_total 字段,布尔值 true 或者 false。

2.icon自定义图标的图片

数据节点的图标。 节点详情detail接口的 info下 新增 icon_img字段 存在,则使用此 icon_img 图片。 可以是图片链接或者是base64。

关系节点的图标。 在config接口的plugs字段下对应的位置,加一个icon_img字段,用于关系节点的图标。

3.节点线上 增加描述文案

在获取节点和link的接口中,在links字段下面,新增一个字段 edge_label,字符串,如果有值则展示此文案,没有则不展示。

config配置
// config配置
{
    "code": "0",
    "msg": "ok",
    "request_id": "QJDysikHdHqtwhMRQFek",
    "data": {
        "plugs": {
            "md5": [
                {
                    "show_total": true, // 是否展示增加统计数字
                    "icon_img": "https://files.codelife.cc/icons/google.svg", // 自定义关系节点图标的图片
                    "canloop": true,
                    "icon": "file",
                    "name": "xxx",
                    "namec": "释放文件",
                    "path": "xxxx",
                    "relay_name": "释放文件"
                },
            ]
        }
    }
}
detail配置
{
    "code": "0",
    "msg": "ok",
    "data": {
        "records": [
            {
                "summary": "",
                "tags": null,
                "info": {
                    "id": "sfdsdfsf",
                    "name": "“xxx",
                    "type": "report",
                    "icon_img": "自定义节点图片", // 自定义图片
                    "basic": {
                        "title": "“黑球”行动再升级,SMBGhost漏洞攻击进入实战",
                        "url": "xxxx",
                        "update_time": "2021-04-29 05:11:53"
                    },
                    "extends": null
                },
                "brief": []
            }
        ]
    }
}
获取节点数据接口示例
{
  "code": "0",
  "msg": "ok",
  "data": {
    "nodes": [
      {
        "id": "a",
        "name": "a",
        "type": "md5",
        "origin": [],
        "feature": {}
      },
      {
        "id": "b",
        "name": "b",
        "type": "md5",
        "origin": [],
        "feature": {}
      }
    ],
    "links": [
      {
        "source": "a",
        "target": "b",
        "type": "domain-md5",
        "desc": "",
        "feature": {},
        "edge_label": "边的描述文案" // 新增:边的描述文案
      }
    ]
  }
}

开发相关

To run the development server

npm run dev

To build the library

npm run lib

To publish a new version

npm run patch npm run lib npm publish

开发人员手册

开发请看这里

如有问题请联系 jiangzhenxiang