diff --git a/doc/.vuepress/config.ts b/doc/.vuepress/config.ts index d35320f2d..8f7c6f178 100644 --- a/doc/.vuepress/config.ts +++ b/doc/.vuepress/config.ts @@ -73,6 +73,7 @@ export default defineUserConfig({ // sidebar sidebar: sidebar.en, + sidebarDepth: 1, // page meta editLinkText: 'Edit this page on GitHub', diff --git a/doc/.vuepress/configs/sidebar/en.ts b/doc/.vuepress/configs/sidebar/en.ts index 54fced15d..e21e4c30d 100644 --- a/doc/.vuepress/configs/sidebar/en.ts +++ b/doc/.vuepress/configs/sidebar/en.ts @@ -10,10 +10,11 @@ export const en: SidebarConfig = { ], }, { - text: '团队管理相关', + text: '团队管理', children: [ '/en/guide/团队管理/团队管理.md', '/en/guide/团队管理/成员权限.md', + '/en/guide/团队管理/节点管理.md', ] }, { @@ -23,7 +24,7 @@ export const en: SidebarConfig = { ] }, { - text: '分析方案 & 模板', + text: '分析方案', children: [ '/en/guide/分析方案/基础属性配置.md', '/en/guide/分析方案/代码检查配置.md', @@ -45,7 +46,13 @@ export const en: SidebarConfig = { { text: '后台管理', children: [ - '/en/guide/后台管理/后台管理说明.md', + '/en/guide/后台管理/用户管理.md', + '/en/guide/后台管理/团队管理.md', + '/en/guide/后台管理/项目管理.md', + '/en/guide/后台管理/分析记录管理.md', + '/en/guide/后台管理/节点管理.md', + '/en/guide/后台管理/工具管理.md', + '/en/guide/后台管理/OAuth管理.md', ] }, { @@ -108,11 +115,11 @@ export const en: SidebarConfig = { '/en/quickStarted/': [ { text: '快速入门', - link: '/zh/quickStarted/deploySever.md', + link: '/en/quickStarted/deploySever.md', // children: [ // { // text: '快速入门', - // link: '/zh/quickStarted/deploySever.md', + // link: '/en/quickStarted/deploySever.md', // }, // ], }, diff --git a/doc/.vuepress/configs/sidebar/zh.ts b/doc/.vuepress/configs/sidebar/zh.ts index 372b2119e..68f594c80 100644 --- a/doc/.vuepress/configs/sidebar/zh.ts +++ b/doc/.vuepress/configs/sidebar/zh.ts @@ -10,10 +10,11 @@ export const zh: SidebarConfig = { ], }, { - text: '团队管理相关', + text: '团队管理', children: [ '/zh/guide/团队管理/团队管理.md', '/zh/guide/团队管理/成员权限.md', + '/zh/guide/团队管理/节点管理.md', ] }, { @@ -23,7 +24,7 @@ export const zh: SidebarConfig = { ] }, { - text: '分析方案 & 模板', + text: '分析方案', children: [ '/zh/guide/分析方案/基础属性配置.md', '/zh/guide/分析方案/代码检查配置.md', @@ -45,7 +46,13 @@ export const zh: SidebarConfig = { { text: '后台管理', children: [ - '/zh/guide/后台管理/后台管理说明.md', + '/zh/guide/后台管理/用户管理.md', + '/zh/guide/后台管理/团队管理.md', + '/zh/guide/后台管理/项目管理.md', + '/zh/guide/后台管理/分析记录管理.md', + '/zh/guide/后台管理/节点管理.md', + '/zh/guide/后台管理/工具管理.md', + '/zh/guide/后台管理/OAuth管理.md', ] }, { diff --git "a/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/OAuth\347\256\241\347\220\206.md" "b/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/OAuth\347\256\241\347\220\206.md" new file mode 100644 index 000000000..d2cc8fbad --- /dev/null +++ "b/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/OAuth\347\256\241\347\220\206.md" @@ -0,0 +1,18 @@ +# OAuth管理 + +- 可**创建**、**编辑**、**清除**主流代码托管平台的Oauth应用配置,为使用者提供OAuth授权支持。 + +- 支持平台及如何创建OAuth应用: + + - 腾讯工蜂:[创建 OAuth 应用程序](https://code.tencent.com/help/oauth2/) + - GitHub:[创建 OAuth 应用程序](https://docs.github.com/cn/developers/apps/building-oauth-apps/creating-an-oauth-app) + - Gitee:[创建 OAuth 应用程序](https://gitee.com/api/v5/oauth_doc#/list-item-3) + - GitLab:[创建 OAuth 应用程序](https://docs.gitlab.com/ee/integration/oauth_provider.html) + +![OAuth管理](../../../images/manage_oauth_01.png) + +![OAuth管理](../../../images/manage_oauth_02.png) + +::: tip +配置OAuth应用时,回调地址栏需填入当前TCA平台配置的域名或IP地址(如当前页面非80端口,需要显式指定端口号),作为Git平台上OAuth应用的回调地址。 +::: diff --git "a/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\210\206\346\236\220\350\256\260\345\275\225\347\256\241\347\220\206.md" "b/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\210\206\346\236\220\350\256\260\345\275\225\347\256\241\347\220\206.md" new file mode 100644 index 000000000..82db6162f --- /dev/null +++ "b/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\210\206\346\236\220\350\256\260\345\275\225\347\256\241\347\220\206.md" @@ -0,0 +1,7 @@ +# 分析记录管理 + +- 可查看平台**全部分析记录**。 + +- 可点击查阅**分析记录详情**。 + +![分析记录列表](../../../images/manage_job_01.png) \ No newline at end of file diff --git "a/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\220\216\345\217\260\347\256\241\347\220\206\350\257\264\346\230\216.md" "b/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\220\216\345\217\260\347\256\241\347\220\206\350\257\264\346\230\216.md" deleted file mode 100644 index 87e094fb9..000000000 --- "a/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\220\216\345\217\260\347\256\241\347\220\206\350\257\264\346\230\216.md" +++ /dev/null @@ -1,53 +0,0 @@ -# 后台管理说明 - -仅**超级管理员**可进入后台管理页面 - -包含以下管理页面:`用户管理`、`分析记录管理`、`节点管理`、`工具管理` - -## 用户管理 - -- 可**查看**、**编辑**、**创建**平台用户。 - -- 可配置用户的**登录密码**、**用户级别**、**超级管理员**等。 - -![用户管理](../../../images/manage_01.png) - -## 分析记录管理 - -- 可查看平台**全部分析记录**。 - -- 可点击查阅**分析记录详情**。 - -![分析记录管理](../../../images/manage_02.png) - -## 节点管理 - -- 可查看**常驻节点状态**。 - -- 可**查看**、**编辑**、**删除**常驻节点。 - -- 可配置节点**工具进程**。 - -- 可配置**标签** - -![节点管理](../../../images/manage_03.png) - -## 工具管理 - -- 可查看**全部工具**(包含平台提供工具、团队自定义工具)。 - -- 可**查看**、**编辑**工具。 - -- 可变更工具**权限状态**。 - -![工具管理](../../../images/manage_04.png) - -::: tip -工具的权限状态仅能由**平台管理员**进行变更调整,需谨慎调整 - -- **团队内可用**:即工具配置了可用团队白名单的团队可以使用该工具,默认创建工具的团队已在白名单内 - -- **全平台可用**:即不同团队都可见可用该工具 - -- **支持自定义规则,全平台可用**:即该工具不同团队都可见可用,且支持用户添加团队所需的自定义规则,该自定义规则存在团队隔离,仅团队内可以,其他团队不可使用 -::: diff --git "a/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" "b/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" new file mode 100644 index 000000000..68a879e2b --- /dev/null +++ "b/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" @@ -0,0 +1,9 @@ +# 团队管理 + +- 可查看平台创建的团队列表,并提供了相应筛选 + +- 可**禁用**、**恢复**团队 + +![团队列表](../../../images/manage_org_01.png) + +![团队操作](../../../images/manage_org_02.png) \ No newline at end of file diff --git "a/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206.md" "b/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206.md" new file mode 100644 index 000000000..0b62da6b1 --- /dev/null +++ "b/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206.md" @@ -0,0 +1,19 @@ +# 工具管理 + +- 可查看**全部工具**(包含平台提供工具、团队自定义工具)。 + +- 可**查看**、**编辑**工具。 + +- 可变更工具**权限状态**。 + +![工具管理](../../../images/manage_tool_01.png) + +::: tip +工具的权限状态仅能由**平台管理员**进行变更调整,需谨慎调整 + +- **团队内可用**:即工具配置了可用团队白名单的团队可以使用该工具,默认创建工具的团队已在白名单内 + +- **全平台可用**:即不同团队都可见可用该工具 + +- **支持自定义规则,全平台可用**:即该工具不同团队都可见可用,且支持用户添加团队所需的自定义规则,该自定义规则存在团队隔离,仅团队内可以,其他团队不可使用 +::: \ No newline at end of file diff --git "a/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\347\224\250\346\210\267\347\256\241\347\220\206.md" "b/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\347\224\250\346\210\267\347\256\241\347\220\206.md" new file mode 100644 index 000000000..b51f8f02b --- /dev/null +++ "b/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\347\224\250\346\210\267\347\256\241\347\220\206.md" @@ -0,0 +1,9 @@ +# 用户管理 + +- 可**查看**、**编辑**、**创建**平台用户。 + +- 可配置用户的**登录密码**、**用户级别**、**超级管理员**等。 + +![用户列表](../../../images/manage_user_01.png) + +![用户编辑](../../../images/manage_user_02.png) \ No newline at end of file diff --git "a/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" "b/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" new file mode 100644 index 000000000..3a23e9ddc --- /dev/null +++ "b/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" @@ -0,0 +1,14 @@ +# 节点管理 + +- 可查看**常驻节点状态**,包含**公共节点**和**团队节点**。 + +- 可**查看**、**编辑**、**删除**常驻节点。 + +- 可配置节点**工具进程**。 + +- 可配置**节点标签** + +![节点管理](../../../images/manage_node_01.png) +![节点管理](../../../images/manage_node_02.png) +![节点管理](../../../images/manage_node_03.png) +![节点管理](../../../images/manage_node_04.png) \ No newline at end of file diff --git "a/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\351\241\271\347\233\256\347\256\241\347\220\206.md" "b/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\351\241\271\347\233\256\347\256\241\347\220\206.md" new file mode 100644 index 000000000..3f6e4fbf2 --- /dev/null +++ "b/doc/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\351\241\271\347\233\256\347\256\241\347\220\206.md" @@ -0,0 +1,7 @@ +# 项目管理 + +- 可查看平台创建的项目列表,并提供了提供相应筛选 + +- 可**禁用**、**恢复**项目 + +![项目列表](../../../images/manage_team_01.png) \ No newline at end of file diff --git "a/doc/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" "b/doc/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" index 234c9e649..274a04e62 100644 --- "a/doc/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" +++ "b/doc/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" @@ -1,4 +1,4 @@ -# 团队管理 +# 团队说明 ![成员权限](../../../images/team_member.png) diff --git "a/doc/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\346\210\220\345\221\230\346\235\203\351\231\220.md" "b/doc/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\346\210\220\345\221\230\346\235\203\351\231\220.md" index 5967036a1..d4dfc5d66 100644 --- "a/doc/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\346\210\220\345\221\230\346\235\203\351\231\220.md" +++ "b/doc/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\346\210\220\345\221\230\346\235\203\351\231\220.md" @@ -1,6 +1,6 @@ # 成员权限 -## 团队成员管理 +## 团队成员 ![成员权限](../../../images/team_member.png) @@ -10,7 +10,7 @@ **团队普通成员**:可以创建项目,可以访问自己有权限的项目。创建项目的人会自动成为这个项目的项目管理员。 -## 项目成员管理 +## 项目成员 项目成员分为**项目管理员**和**项目普通成员**。 diff --git "a/doc/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" "b/doc/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" new file mode 100644 index 000000000..e9684cd13 --- /dev/null +++ "b/doc/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" @@ -0,0 +1,105 @@ +# 节点与标签 + +除了使用**公共节点**执行代码分析外,团队还可以利用**团队标签**注册并使用**团队节点**。 + +## 名词释义与特点 + +- 团队节点是**团队注册并管理**的**私有**节点。 + +- 团队节点**仅会运行**当前团队所属的分析任务。 + +- 团队标签是用于关联节点机器与分析项目。 + ::: tip + 当一个分析项目在方案中配置运行环境为团队标签后,该项目创建的任务就会下发到团队标签关联的节点机器上运行 + ::: + +## 适用场景 + +1. 业务项目**不想**在公共机器上**排队**等待 + +2. 业务项目**代码比较敏感**,不能在公共机器上运行 + +3. 业务项目需要**依赖特定**的**机器环境**(比如CPU架构、操作系统等) + +4. ... + +以上场景,均可考虑使用团队节点,业务团队提供机器资源接入作为团队节点,仅分析自己业务的代码库,**保证执行效率**,**保护源码不泄漏**,**支持项目特殊依赖**等 + +## 团队节点注册 + +- 根据环境下载客户端二进制文件或拉取源码,参考[客户端](../客户端/配置说明.md)。 + +- 通过终端启动客户端: + + - 客户端二进制启动 + + ```bash + ./codepuppy start -t TOKEN --org-sid ORG_SID + ``` + + - 客户端源码启动 + + ```bash + python3 codepuppy.py start -t TOKEN --org-sid ORG_SID + ``` + + ::: tip + 1. TOKEN 可以从平台**个人中心-个人令牌**页面获取 + 2. ORG_SID 可以从**页面链接**中获取 + ::: + +## 团队节点管理 + +完成团队节点注册后,可以在当前团队下看到对应的节点信息,同时**需要进行配置** + +::: warning +- 团队节点**首次注册**时,需要手动在平台上配置**所属标签**、**节点可用性**、**工具进程**等。 +- 将节点的**节点可用性**调整为**活跃**后,运行客户端节点的终端会输出**心跳上报成功**的日志 +::: + +- 首次注册团队节点,节点状态为不可用 + + ![注册团队节点](../../../images/org_node_manager_1.png) + +- 调整后的节点 + + ![注册团队节点](../../../images/org_node_manager_2.png) + +- 配置节点关联的工具进程: + + ![配置工具进程](../../../images/org_node_process.png) + +::: tip +1. 团队节点使用的**所属标签**均为当前团队内创建的标签,可参见[团队标签管理](#团队标签管理) +2. 团队标签可以参考`CodeDog`标签为不同的系统类型(Linux、MacOS、Windows)建立标签,比如`专属标签-Linux`、`专属标签-Mac`等 +::: + +### 团队节点执行任务范围 + +::: warning 使用团队节点运行分析任务的前提 +对应分析项目使用的分析方案中,需要配置分析方案中的**运行环境**为该团队节点配置的所属标签。 +::: + +::: warning 团队节点执行的任务范围取决于该节点的负责人 +- 如果节点负责人为团队管理员,该节点可以执行当前团队所有项目的分析任务 +- 如果节点负责人为项目管理员,该节点只能运行指定项目下的分析任务 +- 如果节点负责人为部分代码库的管理员,该节点只能运行对应代码库的分析任务 +::: + + +## 团队标签管理 + +您可以创建一个团队标签,并配置到您的团队节点和您的分析方案中 + +- 创建团队标签。 + + ![创建团队标签](../../../images/org_tag_manager.png) + +- 配置团队节点所属标签。 + + ![节点配置团队标签](../../../images/org_tag_node.png) + +- 配置分析方案运行环境。 + + ![方案配置团队标签](../../../images/org_tag_scheme.png) + diff --git "a/doc/en/guide/\345\256\242\346\210\267\347\253\257/\351\205\215\347\275\256\350\257\246\346\203\205.md" "b/doc/en/guide/\345\256\242\346\210\267\347\253\257/\351\205\215\347\275\256\350\257\246\346\203\205.md" new file mode 100644 index 000000000..8cbe12b5a --- /dev/null +++ "b/doc/en/guide/\345\256\242\346\210\267\347\253\257/\351\205\215\347\275\256\350\257\246\346\203\205.md" @@ -0,0 +1,26 @@ +## 在线分析 + +在线分析即是通过Server端将分析任务注册到执行队列中,并将任务分配到平台配置的常驻节点上,在常驻节点执行分析,分析完毕后将分析结果上报入库。 + +::: tip +平台需要存在常驻节点,请查阅 [常驻节点分析](./常驻节点分析.md) + +否则任务因没有机器而无法完成分配,超时后任务会注销。 +::: + +## 客户端分析 + +客户端分析即是本地分析,可直接配置本地的客户端配置文件,或在平台上配置好对应信息后,下载配置文件,替换客户端配置问题,并启动客户端分析。分析完毕后会将数据上报入库。 + +- 下载配置文件 + + ![下载配置文件](../../../images/start_scan_03.png) + +- 替换客户端配置文件,并启动客户端分析。 + +::: tip +本地需要下载客户端,请查阅 + +- [部署与配置客户端](../../quickStarted/deployClient.md) +- [客户端使用说明文档](./本地分析.md) +::: \ No newline at end of file diff --git "a/doc/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206\350\257\264\346\230\216.md" "b/doc/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206\350\257\264\346\230\216.md" index 329e9608e..45082ec7f 100644 --- "a/doc/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206\350\257\264\346\230\216.md" +++ "b/doc/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206\350\257\264\346\230\216.md" @@ -20,6 +20,10 @@ 需平台管理员在**后台管理**-**工具管理**中找到对应工具,并将其权限状态调整为**支持自定义规则**。 ::: +## 自定义工具 +### 工具白名单 +默认自定义工具只能当前团队内使用,添加 `工具白名单` 后可以让其他团队使用。 + ## 使用场景说明 ::: tip diff --git "a/doc/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\345\267\245\345\205\267.md" "b/doc/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\345\267\245\345\205\267.md" index 199aff88c..249135666 100644 --- "a/doc/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\345\267\245\345\205\267.md" +++ "b/doc/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\345\267\245\345\205\267.md" @@ -1,6 +1,6 @@ # 自定义工具 -腾讯云代码分析平台支持用户自助添加管理工具。 +腾讯云代码分析平台支持用户自助添加代码分析工具。 适用场景:自定义规则无法满足团队业务复杂需求,需要更多的代码逻辑来匹配目标代码的情况。通常需要团队业务方自行实现对应代码分析工具。 @@ -10,62 +10,63 @@ 2. 提交工具到 git 代码库 3. 在页面创建新工具 4. 为工具添加规则 -5. 在项目分析方案中添加规则 +5. 将工具配置到执行节点 +6. 在项目分析方案中添加规则 ## 自定义工具步骤说明 ### 第一步,编写代码,实现分析工具逻辑 根据需要匹配的目标代码场景,编写对应的工具逻辑。 -可以参考 Python 写的 [Demo 项目](https://github.com/TCATools/demo_tool) +可以参考 Python 实现的 [Demo 项目](https://github.com/TCATools/demo_tool)。 **必要:** -- **运行方式**:支持命令行执行,比如 python run.py 或 run.exe,执行命令的工作目录为工具代码的根目录 +- **运行方式**:支持命令行执行,比如 python run.py 或 run.exe,执行命令的工作目录为工具代码的根目录。 -- **运行环境说明**: - - 平台已内置**Python 环境**和**Java 环境**。 - - ::: tip - 已内置 Python 环境(可以指定 Python 版本),如果依赖第三方 pip 包,不能保证已内置 - - 已内置 Java 环境(内置 JDK_8_HOME 环境变量,并在 PATH 环境变量中添加了 JDK_8_HOME 的 bin 目录,可以按需获取使用) - ::: - - **以上内置环境无法支持?** - - - 建议把依赖包内置在工具 git 库中,自行加载所需环境(比如执行时,自动将内置依赖目录,添加到 PATH 环境变量) - - - 也可以将工具打包成可执行程序(比如用 pyinstaller 打包 python 代码) +- **运行环境说明**: + - 建议将工具打包编译成可执行程序,拉取下来直接可以执行。 + - 如果工具需要在特定的环境中运行,比如python、java环境,平台提供了丰富的工具依赖包,可以在`工具管理`-`工具依赖`中查看,创建工具时可供选择,执行时会自动配置好依赖环境。 + - 如果现有的工具依赖包未支持所需依赖,也可以创建新的工具依赖使用。 - **平台已提供的环境变量** - 使用方式请参考 [Demo 项目](https://github.com/TCATools/demo_tool) - - ```python - # TCA 已提供的环境变量 + - 获取及使用方式请参考 [Demo 项目](https://github.com/TCATools/demo_tool)。 + ``` SOURCE_DIR:要扫描的代码目录路径 - DIFF_FILES: 增量文件列表 - TASK_REQUEST: 任务参数json文件路径 - - # 获取环境变量demo(Python 示例代码): - import os - source_dir = os.environ.get("SOURCE_DIR", None) + DIFF_FILES: 值为一个json文件路径,文件内容为增量扫描的文件列表(增量扫描时可用) + SCAN_FILES: 值为一个json文件路径,文件内容为需要扫描的文件列表(增量或全量扫描均可用) + TASK_REQUEST: 值为一个json文件路径,文件内容为当前扫描任务参数 ``` - + +- **工具命令声明** + + 在工具仓库根目录下,添加一个`tool.json`文件,声明工具的检查和扫描命令,比如: + ```json + { + "check_cmd": "python src/main.py check", + "run_cmd": "python src/main.py scan" + } + ``` + 参数说明: + - `check_cmd`: + - 功能:判断当前执行环境是否满足工具要求(如果不需要检查,也可以没有这个命令)。 + 比如某些工具只能在linux下执行,需要判断当前是否为linux环境。 + - 输出:将判断结果输出到`check_result.json`文件中,文件内容为`{"usable": true}`或`{"usable": false}`。 + - `run_cmd`: + - 功能:扫描代码,执行自定义检查器逻辑(该命令必须存在)。 + - 输出:按照指定格式,输出结果到`result.json`文件中。 + - **工具输出格式要求** - 将结果输出到当前工作目录下的`result.json`文件中(Python 示例代码) - + - 将扫描结果输出到当前工作目录下的`result.json`文件中(Python 示例代码) ```python import json with open("result.json", "w") as fp: json.dump(result, fp, indent=2) ``` - `result.json` 文件格式如下: - + - `result.json` 文件格式如下: ```json [ { @@ -90,25 +91,29 @@ **`refs`** 字段说明: - 非必需项,可无。该字段记录问题回溯路径信息。比如当前文件的回溯路径其他的 3 行代码,可以将这三行的路径及提示信息,按顺序添加到 refs 数组中。 + 非必需项,可无。该字段记录问题回溯路径信息。比如当前行的代码问题,是经过上下文的三行代码执行路径而导致的,可以将这三行的位置及提示信息,按顺序添加到 refs 数组中。 ### 第二步,提交工具到 git 代码库 -- 创建代码库,将工具源代码或编译打包后的可执行文件提交到代码库中 +- 创建代码库,将工具源代码或编译打包后的可执行文件,提交到代码仓库中(建议提交到master分支,TCA默认拉取的是master分支)。 - 建议代码库中加入 README.md 文件,说明工具功能和维护人 +- 建议代码库中加入 README.md 文件,说明工具功能和维护人。 -- 后续需要修改规则实现逻辑,可以直接更新代码库,TCA 平台在执行该工具时,会自动拉取最新工具版本 +- 后续需要修改工具实现逻辑,可以直接更新代码库,TCA 平台在执行该工具时,会自动拉取最新工具代码版本。 ### 第三步,在工具管理页面中创建工具 - 进入工具管理页面,点击创建工具 + ![enter image description here](../../../images/customtool_01.png) + - 填写工具信息 + ![enter image description here](../../../images/customtool_02.png) + **部分参数说明:** - - **工具仓库地址**,即前述步骤中提交的工具 git 代码库地址 + - **工具仓库地址**,即前述步骤中提交的工具 git 代码库地址,默认拉取的是master分支,如果是其他分支,需要在仓库地址后加上`#分支名`,比如:`https://github.com/xxx/xxx.git#main` - **工具认证**,授权拉取工具仓库的权限 @@ -116,23 +121,30 @@ - **环境变量**,工具执行所需的环境变量 - ::: tip - 我们已提供以下公共环境变量 + - **License**,如果是开源工具,填写工具遵循的开源协议,或者填写自研共建 - ```python - python_version = 可选,如果工具是python执行,可以指定python版本,可选值:2,3,3.8(3指的是3.7) - ``` + - **是否为编译型工具**,表示在使用该工具对用户代码进行分析时,是否要求代码需要编译或可执行编译 - ::: - - **License**,如果是开源工具,填写工具遵循的开源协议,或者填写自研共建 +- 添加工具依赖 + + ![enter image description here](../../../images/customtool_03.png) + + 添加完成后,会展示已添加的依赖方案: + + ![enter image description here](../../../images/customtool_04.png) + +**工具依赖说明:** +- 比如当前的demo工具,只需要依赖python3运行,而且支持在linux x86_64、linux arm64、mac和windows下执行,那么只需要配置一个依赖方案(如上图),并配置为默认方案。在不同的操作系统中,会自动加载对应操作系统的python环境。 +- 如果需要根据扫描项目设置的环境变量,加载不同的依赖配置,则可以配置不同的判断条件,使用多个依赖方案。 - - **是否为编译型工具**,表示在使用该工具对用户代码进行分析时,是否要求代码需要编译或可执行编译 ### 第四步,为工具添加规则 - 完成工具创建后,进入规则列表,为工具添加规则 + ![enter image description here](../../../images/customtool_05.png) + - 填写规则信息 **部分参数说明:** @@ -148,22 +160,24 @@ ### 第五步,将工具配置到执行节点 ::: tip -需要联系平台管理员协助操作,在节点管理-工具进程配置中找到对应工具,将其配置到对应机器上。 +需要联系平台管理员协助操作,在`管理入口`-`节点管理`中进入需要配置的机器节点的`工具进程配置`中,找到对应工具,勾选工具进程。 -完成节点配置工具进程后,才能在项目中采用该工具进行分析 +完成节点配置工具进程后,才能在项目中采用该工具进行分析。 ::: +![enter image description here](../../../images/customtool_06.png) + ### 第六步,完成上述操作,在项目中使用工具规则 -- 进入到项目中,在分析方案-代码检查进行规则配置 +- 进入到项目中,在`分析方案`-`代码检查`进行规则配置。 -- 点击添加规则,找到对应工具规则进行添加 +- 点击添加规则,找到对应工具规则进行添加。 -- 添加完成后,启动分析,建议启动一次全量分析 +- 添加完成后,启动分析,为了将规则应用到所有代码文件,建议启动一次全量分析(增量分析只会分析自上次扫描后变更的文件)。 ## 自定义工具权限说明 -- **默认自定义工具仅团队管理员可操作,团队内所有成员可使用,** +- **默认自定义工具仅团队管理员可操作,团队内所有成员可使用。** - 团队管理员才能创建工具,添加工具规则等,具备该工具全部权限 diff --git "a/doc/en/guide/\346\234\215\345\212\241\347\253\257/deploy_without_migrate.md" "b/doc/en/guide/\346\234\215\345\212\241\347\253\257/deploy_without_migrate.md" new file mode 100644 index 000000000..1eef61455 --- /dev/null +++ "b/doc/en/guide/\346\234\215\345\212\241\347\253\257/deploy_without_migrate.md" @@ -0,0 +1,15 @@ +在实际的生产环境的部署过程中,团队的MySQL的管理员可能不会给到应用账号create等比较敏感的权限,这种情况下,我们可以通过手动迁移数据的方式起到和等同Django migrate的效果。 + +操作步骤: + +1. 进入Server服务工作目录后(假设工作目录为 ``/data/CodeAnalysis/server/``,以下路径均为工作目录内的相对路径) +2. 在开发环境一个有全部权限的MySQL地址,初始化数据(MySQL版本运行版本:5.7) + - 执行``vi ./scripts/config.sh``:填写一个有全部权限的MySQL数据库地址和Redis信息以及根据需要调整配置信息,主要的工程配置已提供默认值,字段说明可以查看[文档](../server/README.md) + - 执行``bash ./scripts/deploy.sh init``:初始化DB、安装依赖和运行初始化脚本 + - 使用MySQLDump工具导出表结构与数据:``mysqldump -u user -p –databases codedog_main codedog_analysis codedog_file codedog_login > codedog_all.sql`` +3. 在生产环境建数据库,详情见:``server/sql/init.sql`` +4. 连接MySQL,导入数据: + - 临时关闭外键检查: ``SET SESSION FOREIGN_KEY_CHECKS=0``,否则会因为数据中有外键关联导致导入失败 + - 导入表结构与数据: ``source /youdir/codedog_all.sql;`` + - 开启外键检查: ``SET SESSION FOREIGN_KEY_CHECKS=1`` +5. 启动服务: 直接执行 ``bash ./scripts/deploy.sh start``,无需执行 ``init``方法,否则会导致数据重复写入 diff --git "a/doc/en/guide/\346\234\215\345\212\241\347\253\257/server.md" "b/doc/en/guide/\346\234\215\345\212\241\347\253\257/server.md" index 6ffc73417..8d1b25a01 100644 --- "a/doc/en/guide/\346\234\215\345\212\241\347\253\257/server.md" +++ "b/doc/en/guide/\346\234\215\345\212\241\347\253\257/server.md" @@ -130,3 +130,4 @@ File存储引擎配置 - SCMPROXY_HOST:ScmProxy服务的HOST,默认为``0.0.0.0`` - SCMPROXY_PORT:ScmProxy服务监听端口,默认为``8009`` - SCMPROXY_SENTRY_URL:ScmProxy服务异常日志上报至sentry配置 +- SCMPROXY: 通过本环境变量去指定其他服务调用ScmProxy服务的地址,默认值为``127.0.0.1:8009`` diff --git a/doc/en/quickStarted/codeDeploy.md b/doc/en/quickStarted/codeDeploy.md new file mode 100644 index 000000000..95cd0ae1c --- /dev/null +++ b/doc/en/quickStarted/codeDeploy.md @@ -0,0 +1,90 @@ +# 源码部署 +兼容旧版的部署方式 +#### 依赖环境 + +- 系统环境 + - Linux + - 最低配置:2核4G内存、100G硬盘存储空间 + +- 环境准备 +> 目前TCA脚本已封装好Python、Mariadb、Redis与Nginx安装步骤,可以按“操作说明”内容进行操作 + + - **Python 3.7**,[安装指引](./references/install_python37_on_centos.md) + + - **MySQL服务(MySQL5.7.8以上版本或Mariadb 10.5以上版本)**,[安装指引](./references/install_mysql_on_centos.md) + + - **Redis服务(4.0版本以上)**,[安装指引](./references/install_redis_on_centos.md) + + - **Nginx服务** + + :::warning + 仅适用于本地部署体验,生产环境建议使用专业的 MySQL、Redis 等服务 + ::: + +- 权限准备 + + - 环境权限:安装 Server 依赖软件(python、nginx、yum 等软件包)需要使用 ROOT 权限 + - 启动 Server服务时可以使用非 ROOT 用户运行 + - 数据库权限:Server 服务执行数据库初始化需要依赖 ``CREATE、ALTER、INDEX、DELETE、LOCK TABLES、SELECT、INSERT、REFERENCES、UPDATE`` 权限 +- 端口使用:需要开放80端口的访问权限(80为TCA平台默认访问端口),或调整 Web 服务默认的访问端口地址 + +#### 操作说明 + +##### 首次启动操作 + +1. 进入CodeAnalysis工作目录(例如``~/CodeAnalysis``),以下路径均为目录内的相对路径 +2. 安装基础软件与部署TCA(可根据脚本选项确定是否要安装相关基础软件),执行 + ```bash + $ bash ./quick_install.sh local deploy + ``` + 执行该命令会做以下事情: + - 检测本地Python3.7、Mariadb/MySQL、Redis与Nginx,如果不存在会提示安装(install) + - 部署TCA Server、Web与Client,并进行初始化(install) + - 启动TCA Server、Web与Client(start) + - 检测TCA的运行状态(check) + + >注:在运行过程中,脚本会检测本地是否安装了相关基础软件(Python3.7、MySQL/Mariadb、Redis、Nignx),如果未安装会输出以下类似提示语: + >``` + >Do you want to install [Redis] by this script? + >Please enter:[Y/N] + >``` + >如果确定通过脚本安装可以输入`Y`。 +3. 执行完成,无其他报错,即可登录: + - TCA 平台初始登录账号是``CodeDog``,密码是``admin``, + +##### 更新操作 +1. 更新代码 +2. 执行以下命令: + - `bash ./quick_install.sh local install tca`:更新相关配置 + - `bash ./quick_install.sh local start`:启动服务(会自动关闭之前的服务) + - `bash ./quick_install.sh local check`:检查服务是否启动失败 + +注: +1. `local install`命令行参数说明: + - `base`:安装Python、Mariadb/MySQL、Redis与Nginx + - `tca`:初始化或更新TCA Server、Web、Client相关配置和数据 + - `server`:初始化或更新TCA Server相关配置和数据 + - `web`:初始化或更新TCA Web相关配置和数据 + - `client`:初始化或更新TCA Client相关配置和数据 + - 不填参数,默认会执行`base`、`tca`相关操作 + +##### 启动和停止服务 + +- 启动所有服务:`bash ./quick_install.sh local start` +- 启动Main相关服务:`bash ./quick_install.sh local start main` + - `local start`支持启动指定服务,如上述的启动Main服务,还支持`mysql/redis/analysis/file/login/scmproxy/nginx/client/all` +- 停止所有服务:`.bash /quick_install.sh local stop` +- 停止Main相关服务:`bash ./quick_install.sh local stop main` + - `local stop`支持停止指定服务,如上述的停止Main服务,还支持`analysis/file/login/scmproxy/nginx/client/all` + +注: +1. 启动时会自动关闭之前已经运行的服务 +2. `local start`支持启动指定服务,如上述的启动Main服务,还支持`mysql/redis/main/analysis/file/login/scmproxy/nginx/all` + - `mysql`和`redis`默认会使用`systemctl`进行启动,如果`systemctl`无法使用,则会直接使用`nohup`方式运行相关服务 + +##### 检查服务运行状态 +检查服务运行状态:`bash ./quick_install.sh local check` + - 目前支持检查server与web,暂不支持client + +##### 获取服务输出日志 +打印TCA Server各个服务的日志路径: `bash ./quick_install.sh local log` \ No newline at end of file diff --git a/doc/en/quickStarted/deploySever.md b/doc/en/quickStarted/deploySever.md index 7518d5bec..7ee1a6e54 100644 --- a/doc/en/quickStarted/deploySever.md +++ b/doc/en/quickStarted/deploySever.md @@ -1,176 +1,130 @@ -# 部署 Server 和 Web +# 部署 TCA +TCA提供部署脚本,支持一键式快速部署Server、Web、Client。 +脚本共提供三种部署方式:Docker部署(推荐)、[Docker-Compose部署](./dockercomposeDeploy.md)、[源码部署](./codeDeploy.md),可根据您的具体使用场景任意选择其一进行部署。 -## 通过源代码 +## Docker快速部署 + +:::warning +仅适用于Docker部署体验,生产环境建议使用专业的 MySQL、Redis 等服务 +::: ### 依赖环境 - 系统环境 - - - Linux - + - Linux、macOS、Windows - 最低配置:2核4G内存、100G硬盘存储空间 - - 环境准备 - - - **Python 3.7**,[安装指引](./references/install_python37_on_centos.md) - - - **MySQL服务(5.7.8以上的版本)**,[安装指引](./references/install_mysql_on_centos.md) - - - **Redis服务(4.0版本以上)**,[安装指引](./references/install_redis_on_centos.md) - - - **Nginx服务**,可以使用包管理工具进行安装 - - - CentOS 系统:`yum install nginx` - - - Ubuntu 系统:`apt-get install nginx` - - :::warning - 仅适用于本地部署体验,生产环境建议使用专业的 MySQL、Redis 等服务 - ::: - + - Docker - 权限准备 + - 需要开放80、8000端口的访问权限(80为TCA平台访问端口,8000为TCA Server访问端口) - - 安装 Server 依赖软件(python、nginx、yum 等软件包)需要使用 ROOT 权限 - - 启动 Server服务时可以使用非 ROOT 用户运行 - - - 需要开放80端口的访问权限(80为TCA平台默认访问端口),或调整 Web 服务默认的访问端口地址 - - - Server 服务执行数据库初始化需要依赖 ``CREATE、ALTER、INDEX、DELETE、LOCK TABLES、SELECT、INSERT、REFERENCES、UPDATE`` 权限 - -### 部署步骤 - -#### 部署 Server - -1. 进入 Server 服务工作目录(例如 ``~/CodeAnalysis/server/``),以下路径均为目录内的相对路径 - -2. 配置 MySQL 和 Redis 服务,初始化数据(MySQL版本运行版本:5.7) - - - 填写数据库和Redis信息以及根据需要调整配置信息,主要的工程配置已提供默认值,字段说明详见[TCA Server](../guide/服务端/server.md)。 - - ```bash - vi ./scripts/config.sh - ``` +### 部署对象 +Server、Web 与 Client - - 初始化DB、安装依赖和运行初始化脚本 +### 操作说明 +#### 首次启动操作 +1. 进入CodeAnalysis工作目录(例如``~/CodeAnalysis``),以下路径均为目录内的相对路径 +2. 执行命令: ```bash - bash ./scripts/deploy.sh init + bash ./quick_install.sh docker deploy ``` +::: tip +通过Docker部署默认会在当前根目录下的挂载三个路径: +- `.docker_temp/logs`:容器内的`/var/log/tca/`,存放TCA平台的日输出文件; +- `.docker_temp/data`:容器内的`/var/opt/tca/`, 存放TCA平台的服务数据,主要是Mariadb、Redis; +- `.docker_temp/configs`:容器内的``/etc/tca``,存放TCA平台的配置文件,主要是`config.sh` +::: - - 将安装好的``celery``与``gunicorn``可执行文件建立软链接到``/usr/local/bin``路径下 - - ```bash - # /path/to/需要替换为celery可执行命令实际的路径,一般在python安装路径的bin目录下,如/usr/local/python3/bin/ - ln -s /path/to/celery /usr/local/bin/celery - # /path/to/需要替换为gunicorn可执行命令实际的路径,一般在python安装路径的bin目录下,如/usr/local/python3/bin/ - ln -s /path/to/gunicorn /usr/local/bin/gunicorn - ``` - - - 使环境变量生效,避免出现 `unknown command` 错误 - - ```bash - export PATH=/usr/local/bin:$PATH - ``` +#### 更新操作 +1. 更新代码 +2. 执行以下命令: + - `TCA_IMAGE_BUILD=true ./quick_install.sh docker deploy`:重新构建并启动tca容器 +::: tip +`TCA_IMAGE_BUILD=true`表示从本地构建TCA镜像运行 +::: -3. 启动/停止服务 +#### 运行容器 +如果已经在机器上执行过``docker deploy``,并保留容器数据的,可以执行以下命令启动容器,继续运行TCA - ```bash - # 启动服务 - bash ./scripts/deploy.sh start - # 停止服务 - bash ./scripts/deploy.sh stop - ``` +```bash +bash ./quick_install.sh docker start +``` -#### 部署Web +#### 停止容器 +如果容器正在运行,希望停止容器,可以运行 -1. 在完成 Server 部署后,进入 Web 服务工作目录(例如 ``~/CodeAnalysis/web/tca-deploy-source``),以下路径均为目录内的相对路径 +```bash +bash ./quick_install.sh docker stop +``` -2. 部署/更新前端服务 +# 使用TCA +成功部署TCA后,请开始您的代码分析。 +## 进入平台页面 - ```bash - # 部署、更新都使用此命令 - bash ./scripts/deploy.sh init -d - - # 注意 - # 前端 nginx 服务 SERVER_NAME 默认通过 `curl ifconfig.me` 获取 - # 可能是公网出口IP地址,如用户需要自定义 SERVER_NAME,可通过如下方式 - export LOCAL_IP=xxx - bash ./scripts/deploy.sh init -d - ``` +在浏览器输入`http://部署机器IP/`,点击立即体验,完成登录后即可跳转到团队列表页 :::tip +默认平台登录账号/密码:CodeDog/admin -- `./scripts/config.sh` 已配置默认环境变量,用户可根据需要调整环境变量再部署前端服务,具体可查阅脚本内容。 -- TCA 平台初始登录账号是``CodeDog``,密码是``admin``, +如部署过程中,已调整默认账号密码,请按照调整后的账号密码进行登录 ::: -## 通过Docker-Compose - -### 依赖环境 - -- 系统环境 - - - Linux、macOS、Windows +## 创建团队及项目 - - 最低配置:2核4G内存、100G硬盘存储空间 +- 完成团队创建 -- 环境准备 +- 完成项目创建 - - Docker +## 登记代码库 - - Docker-Compose 1.26 以上版本 +登记代码库,输入代码库地址以及凭证信息等,完成代码库登记。 - :::warning - Compose file format需要为3.0及以上,Docker版本要求可以参考[官方文档](https://docs.docker.com/compose/compose-file/compose-file-v3/#compose-and-docker-compatibility-matrix) - ::: +![registerCodeRepo](../../images/registerCodeRepo.png) -- 权限准备 +## 创建分析项目 - - 需要开放80、8000端口的访问权限(80为TCA平台访问端口,8000为TCA Server访问端口) +![开始分析](../../images/start_scan_02.png) -### 部署步骤 +::: tip +1. 用户可选择使用分析方案模板,或创建分析方案的方式,利用方案的分析配置进行代码分析。 +2. 点击确认时,平台会首先创建该代码库的分析方案,然后根据代码库分支、当前分析方案创建分支项目。 +::: -#### 方案一:一键部署(持续完善中) +### 分析方案说明 -拉取代码进入源码根目录,执行``./quick_install.sh``命令,即可自动安装 Docker、Docker-Compose 和启动 Server 与 Web 服务 +- 分析方案是用于对代码库进行分析的一套配置集合。 -:::tip +- 更多分析方案配置可查阅[帮助文档-分析方案](../guide/分析方案/基础属性配置.md) -- ``quick_install.sh`` 脚本中会自动下载 [Docker 安装脚本](https://get.docker.com) 、启动 Docker 服务、下载 ``docker-compose`` 可执行文件以及执行 ``compose_init.sh`` 脚本启动 Server、Web 服务 +![creataAnalysePlan](../../images/creataAnalysePlan.png) -- 如果提示脚本没有执行权限,可以在源码目录下执行命令:``chmod +x compose_init.sh quick_install.sh`` +::: tip +本次部署会默认启动运行环境为「Codedog_Linux」的客户端,若需扩展更多运行环境,详见客户端[常驻节点分析](../guide/客户端/常驻节点分析.md) ::: -#### 方案二:手动部署 +![planPage](../../images/planPage.png) -1. 安装 Docker,安装教程:[官方文档](https://docs.docker.com/engine/install/) +## 执行代码分析 -2. 安装 Docker-Compose,安装教程:[官方文档](https://docs.docker.com/compose/install/) +初始化创建项目后,可通过 `在线分析` 或 `客户端分析` 来启动代码分析。 -3. 拉取代码并进入源码根目录后,执行 ``./compose_init.sh`` 命令,即可启动 Server 与 Web 服务 - -:::tip +![代码分析](../../images/start_scan_06.png) -- 如果提示脚本没有执行权限,可以在源码执行命令:``chmod +x compose_init.sh`` - -- 首次启动会构建相关镜像,耗时会比较久 - -- ``compose_init.sh`` 脚本会包含各个服务的初始化操作 +::: tip +- TCA推荐使用`在线分析`,您可根据具体使用场景选择其一。 +- `在线分析`表示配置代码库链接后,TCA客户端拉取代码后进行分析;`客户端分析`在配置本地待扫描代码路径后,无需代码拉取直接分析本地代码。 +- `在线分析`与`客户端分析`具体详情及配置参考[TCA客户端配置详情](../guide/客户端/配置详情.md) ::: -#### 启动/停止服务 - -进入源码目录后,执行 ``docker-compose up -d`` 命令,即可启动 Server 与 Web服务。执行 ``docker-compose stop`` 命令,即可停止 Server 与 Web 服务。 +## 查看分析历史 -### 常见问题 +分析结束后,数据会上报到服务端。可进入分析历史页面查看分析记录以及分析结果。 -- Q:如何查看服务启动的日志? +![分析历史](../../images/start_scan_05.png) - A:可以先找服务名称,执行 ``docker-compose logs -f xxx``,xxx即服务的名称,比如``main-server``、``main-worker``等 +## 查看分析概览 -- Q:TCA 初始登录账号密码是什么? - - A:初始登录账号是``CodeDog``,密码是``admin``,如果想要自定义,在初始化前,可以在``server/dockerconfs/.env.local``对``TCA_DEFAULT_ADMIN``和``TCA_DEFAULT_PASSWORD``变量值进行调整。如果初始化完成后需要调整,则需要登录到平台的``用户管理``页面进行调整。 +分析结束后,进入分支概览可以查看该分支指定分析方案的概览数据以及 [问题列表](../guide/代码检查/分析结果查看.md) 等。 -**详细Q&A文档可以查阅[TCA使用常见问题](./FAQ.md)** +![分支概览](../../images/start_scan_04.png) \ No newline at end of file diff --git a/doc/en/quickStarted/dockercomposeDeploy.md b/doc/en/quickStarted/dockercomposeDeploy.md new file mode 100644 index 000000000..e9062bb65 --- /dev/null +++ b/doc/en/quickStarted/dockercomposeDeploy.md @@ -0,0 +1,49 @@ +# Docker-Compose快速部署 +#### 部署对象 +Server、Web 与 Client + +:::warning +仅适用于Docker-Compose部署体验,生产环境建议使用专业的 MySQL、Redis 等服务 +兼容之前的部署方式 +::: + +#### 操作说明 +##### 首次启动操作 + +1. 进入CodeAnalysis工作目录(例如``~/CodeAnalysis``),以下路径均为目录内的相对路径 +2. 执行命令: + - `bash ./quick_install.sh docker-compose deploy`:启动tca_server容器 + +注意:通过Docker-Compose部署默认会在当前根目录下的挂载三个路径: + +- `.docker_data/logs`:存放TCA平台的各个服务日志输出目录; +- `.docker_data/mysql`:存放TCA平台的MySQL数据 +- `.docker_data/redis`:存放TCA平台的Redis数据 +- `.docker_data/filedata`:存放TCA平台文件服务器的文件 + +##### 更新操作 +1. 更新代码 +2. 执行以下命令: + - `bash ./quick_install.sh docker-compose build`:重新构建TCA相关镜像 + - `bash ./quick_install.sh docker-compose deploy`: 重新部署TCA相关容器与初始化(或刷新数据) + +##### 运行操作 +如果已经在机器上执行过``docker-compose deploy``,并保留容器数据的,可以执行以下命令启动容器,继续运行TCA + +```bash +bash ./quick_install.sh docker-compose start +``` + +##### 停止操作 +如果容器正在运行,希望停止容器,可以执行以下命令 + +```bash +bash ./quick_install.sh docker-compose stop +``` + +##### 构建镜像操作 +如果希望构建镜像,可以执行以下命令 + +``` +bash ./quick_install.sh docker-compose build +``` \ No newline at end of file diff --git a/doc/en/quickStarted/intro.md b/doc/en/quickStarted/intro.md index 71b8cd469..055ce7737 100644 --- a/doc/en/quickStarted/intro.md +++ b/doc/en/quickStarted/intro.md @@ -1,48 +1,36 @@ -# 腾讯云代码分析 +# 平台概述 **腾讯云代码分析**(Tencent Cloud Code Analysis,简称TCA,内部曾用研发代号 **CodeDog** )是集众多分析工具的云原生、分布式、高性能的代码综合分析跟踪平台,包含服务端、Web端和客户端三个组件,已集成一批自研工具,同时也支持动态集成业界各编程语言的分析工具。 -通过以下步骤,您可以将腾讯云代码分析部署到本地,快速启动并运行您的代码分析项目。 +### 使用TCA Action快速体验 +使用TCA Action,只需要在代码仓库中添加`.github/workflows/tca.yml`文件,就可以直接在GitHub工作流中快速体验代码分析。请参考:[TCA-action指引](https://github.com/TCATools/TCA-action/blob/main/README.md) -:::tip - -- 通过部署 TCA Server 和 Web 得到腾讯云代码分析平台,并在平台完成相关项目的创建; -- 完成项目创建后,您可以通过部署并配置腾讯云代码分析客户端,将客户端在**本地**或作为**在线常驻节点**执行代码分析。 -- 如果您在部署或使用腾讯云代码分析的过程中遇到了问题,可以参考[常见问题](FAQ.md)。 -::: +### 部署TCA -## 部署 Server 和 Web +拉取 [代码库](https://github.com/Tencent/CodeAnalysis) 后,您可以通过以下三种方式部署腾讯云代码分析平台: -拉取 [代码库](https://github.com/Tencent/CodeAnalysis) 后,您可以通过以下两种方式部署腾讯云代码分析的 Server 和 Web 服务: +- [通过 Docker 部署](./deploySever.md#通过docker) -- [通过源代码](./deploySever.md#通过源代码) +- [通过源代码](./codeDeploy.md#通过源代码) -- [通过 Docker-Compose 部署](./deploySever.md#通过docker-compose) +- [通过 Docker-Compose 部署](./dockercomposeDeploy.md#通过docker-compose) -## 创建首个代码分析项目 +### 创建首个代码分析项目 -成功部署并启动 Server 与 Web 服务后,您可以按照 [指引](./initRepo.md) 创建您的首个代码分析项目。 +成功部署并启动TCA后,您可以按照 [指引](./deploySever.md) 创建您的首个代码分析项目。 :::tip 默认平台登录账号/密码:CodeDog/admin ::: -## 部署与配置客户端 - -在启动您的首个代码分析项目前,您需要在本地部署腾讯云代码分析的客户端。完成客户端的项目配置后,即可启动您的首个代码分析项目,并在腾讯云代码分析平台上查看您的分析结果。 - -您可以通过以下三种方式部署并使用腾讯云代码分析的客户端: - -- [通过源代码](./deployClient.md#通过源代码) - -- [通过 Docker-Compose](./deployClient.md#通过docker-compose) +### 快速扩展客户端 -- [通过可执行文件](./deployClient.md#通过可执行文件) +TCA客户端支持通过可执行文件进行快速扩展部署,详见[通过可执行文件](./deployClient.md#通过可执行文件) :::tip 客户端可在本地执行代码分析,也可以作为[在线常驻节点](../advanced/任务分布式执行.md)进行在线分析。 ::: -## 了解更多 +### 了解更多 更多关于腾讯云代码分析平台的使用指南和配置说明,参见[帮助文档](../guide/README.md)。 diff --git a/doc/en/quickStarted/references/install_nginx_from_source.md b/doc/en/quickStarted/references/install_nginx_from_source.md index 01be42c1e..ca924bf4a 100644 --- a/doc/en/quickStarted/references/install_nginx_from_source.md +++ b/doc/en/quickStarted/references/install_nginx_from_source.md @@ -13,6 +13,7 @@ ```bash yum -y install gcc zlib-devel pcre-devel bzip2-devel openssl-devel readline-devel ``` +> Ubuntu: ``apt install gcc libssl-dev zlib1g-dev libpcre3-dev libbz2-dev libreadline-dev`` ## 下载源码 @@ -32,7 +33,7 @@ $ cd /usr/local/src/nginx-1.20.2 # 配置 $ ./configure \ --sbin-path=/usr/local/nginx/nginx \ ---conf-path=/usr/local/nginx/nginx.conf \ +--conf-path=/etc/nginx/nginx.conf \ --pid-path=/run/nginx.pid \ --with-stream \ --with-http_ssl_module --with-http_v2_module --with-http_auth_request_module @@ -49,27 +50,40 @@ $ ln -s /usr/local/nginx/nginx /usr/local/bin/nginx ## 添加nginx配置文件 ```bash -mkdir /usr/local/nginx/conf.d/ -vi /usr/local/nginx/nginx.conf +mkdir /etc/nginx/conf.d/ +vi /etc/nginx/nginx.conf ``` -检查``nginx.conf``配置文件,检查是否缺失这一行``include conf.d/*.conf;``,如果缺失则加上,加上位置如下所示: +检查``nginx.conf``配置文件: + +1. 检查``pid /run/nginx.pid``,如果缺失或被注释则加上,加上位置如下所示: +2. 检查是否缺失这一行``include conf.d/*.conf;``,如果缺失则加上,加上位置如下所示: ```bash +# ...省略内容 +#pid logs/nginx.pid; # 默认有的 +pid /run/nginx.pid; + +events { + # ...省略内容 +} + +# ...省略内容 + http { - # ... + # ...省略内容 # - include conf.d/*.conf; - include /etc/nginx/conf.d/*.conf; - + include conf.d/*.conf; server { - # ... + # ...省略内容 } } ``` -后续可以将nginx配置文件放置到``/usr/local/nginx/conf.d/``目录或者``/etc/nginx/conf.d/``目录下 +后续可以将nginx配置文件放置到``/etc/nginx/conf.d/``目录下 + + ## 配置开机自动启动 @@ -91,10 +105,11 @@ PIDFile=/run/nginx.pid # Nginx will fail to start if /run/nginx.pid already exists but has the wrong # SELinux context. This might happen when running `nginx -t` from the cmdline. # https://bugzilla.redhat.com/show_bug.cgi?id=1268621 -ExecStartPre=/usr/bin/rm -f /run/nginx.pid +ExecStartPre=/bin/rm -f /run/nginx.pid ExecStartPre=/usr/local/bin/nginx -t ExecStart=/usr/local/bin/nginx ExecReload=/usr/local/bin/nginx -s reload +ExecStop=/usr/local/bin/nginx -s stop KillSignal=SIGQUIT TimeoutStopSec=5 KillMode=process diff --git a/doc/en/quickStarted/references/install_python37_on_centos.md b/doc/en/quickStarted/references/install_python37_on_centos.md index 0762fdd8d..ac782a828 100644 --- a/doc/en/quickStarted/references/install_python37_on_centos.md +++ b/doc/en/quickStarted/references/install_python37_on_centos.md @@ -61,3 +61,49 @@ pip默认是到``pypi``官方源下载第三方依赖包,下载速度可能会 mkdir ~/.pip/ echo "extra-index-url = https://mirrors.cloud.tencent.com/pypi/simple" >> ~/.pip/pip.conf ``` + +## 一键安装脚本 +以下脚本内容是上面的步骤集合,省去了复制粘贴的重复动作。 +1. 创建文件 `install_py37.sh`,写入以下 shell 脚本 +2. 赋予执行权限,`chmox +x install_py37.sh` +3. 执行脚本,`./install_py37.sh` + +```bash +#!/bin/env bash + +## 下载 Python 源码,如果已下载源码在脚本当前目录下,可注释跳过下载步骤 +wget https://www.python.org/ftp/python/3.7.12/Python-3.7.12.tgz + +## 安装编译依赖组件 +yum -y install wget zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make libffi-devel xz-devel + +## 解压安装 +# 解压到/usr/local/src目录 +tar zvxf Python-3.7.12.tgz -C /usr/local/src +cd /usr/local/src/Python-3.7.12 +# 编译前配置 +./configure prefix=/usr/local/python3 --enable-shared +# 编译构建 +make -j8 +# 安装Python +make install +# 清理编译产出的中间文件 +make clean +# 链接构建产出的Python可执行文件到/usr/local/bin目录 +ln -s /usr/local/python3/bin/python3 /usr/local/bin/python +# 链接构建产出的pip3可执行文件到/usr/local/bin目录 +ln -s /usr/local/python3/bin/pip3 /usr/local/bin/pip +# 链接构建产出的Python动态库 +ln -s /usr/local/python3/lib/libpython3.7m.so.1.0 /usr/lib/libpython3.7m.so.1.0 +# 配置动态库 +ldconfig + +## 检查Python版本是否安装成功 +echo -e "\033[1;42;37m[$(date "+%Y/%m/%d %H:%M:%S")] [Check]: 检查Python版本\033[0m" +python --version +echo -e "\033[1;42;37m[$(date "+%Y/%m/%d %H:%M:%S")] [Check]: 检查Python版本\033[0m" + +## pypi下载源配置 +mkdir ~/.pip/ +echo "extra-index-url = https://mirrors.cloud.tencent.com/pypi/simple" >> ~/.pip/pip.conf +``` diff --git a/doc/en/quickStarted/runProject.md b/doc/en/quickStarted/runProject.md index e9e124b9b..3fa362da5 100644 --- a/doc/en/quickStarted/runProject.md +++ b/doc/en/quickStarted/runProject.md @@ -8,32 +8,10 @@ ![代码分析](../../images/start_scan_06.png) -## 在线分析 - -在线分析即是通过Server端将分析任务注册到执行队列中,并将任务分配到平台配置的常驻节点上,在常驻节点执行分析,分析完毕后将分析结果上报入库。 - -::: tip -平台需要存在常驻节点,请查阅 [常驻节点分析](../guide/客户端/常驻节点分析.md) - -否则任务因没有机器而无法完成分配,超时后任务会注销。 -::: - -## 客户端分析 - -客户端分析即是本地分析,可直接配置本地的客户端配置文件,或在平台上配置好对应信息后,下载配置文件,替换客户端配置问题,并启动客户端分析。分析完毕后会将数据上报入库。 - -- 下载配置文件 - - ![下载配置文件](../../images/start_scan_03.png) - -- 替换客户端配置文件,并启动客户端分析。 - -::: tip -本地需要下载客户端,请查阅 - -- [部署与配置客户端](./deployClient.md) -- [客户端使用说明文档](../guide/客户端/本地分析.md) -::: +注: +- TCA推荐使用`在线分析`,您可根据具体使用场景选择其一。 +- `在线分析`表示配置代码库链接后,TCA客户端拉取代码后进行分析;`客户端分析`在配置本地待扫描代码路径后,无需代码拉取直接分析本地代码。 +- `在线分析`与`客户端分析`具体详情及配置参考[TCA客户端配置详情](../guide/客户端/客户端配置详情.md) ## 查看分析历史 diff --git a/doc/images/manage_01.png b/doc/images/manage_01.png deleted file mode 100644 index 43e880497..000000000 Binary files a/doc/images/manage_01.png and /dev/null differ diff --git a/doc/images/manage_02.png b/doc/images/manage_02.png deleted file mode 100644 index 7827cc622..000000000 Binary files a/doc/images/manage_02.png and /dev/null differ diff --git a/doc/images/manage_03.png b/doc/images/manage_03.png deleted file mode 100644 index e3f0eb25e..000000000 Binary files a/doc/images/manage_03.png and /dev/null differ diff --git a/doc/images/manage_04.png b/doc/images/manage_04.png deleted file mode 100644 index 31a394651..000000000 Binary files a/doc/images/manage_04.png and /dev/null differ diff --git a/doc/images/manage_05.png b/doc/images/manage_05.png deleted file mode 100644 index ddce7d441..000000000 Binary files a/doc/images/manage_05.png and /dev/null differ diff --git a/doc/images/manage_06.png b/doc/images/manage_06.png deleted file mode 100644 index d04552d8c..000000000 Binary files a/doc/images/manage_06.png and /dev/null differ diff --git a/doc/images/manage_job_01.png b/doc/images/manage_job_01.png new file mode 100644 index 000000000..973333db7 Binary files /dev/null and b/doc/images/manage_job_01.png differ diff --git a/doc/images/manage_node_01.png b/doc/images/manage_node_01.png new file mode 100644 index 000000000..5012308be Binary files /dev/null and b/doc/images/manage_node_01.png differ diff --git a/doc/images/manage_node_02.png b/doc/images/manage_node_02.png new file mode 100644 index 000000000..217aa7d0c Binary files /dev/null and b/doc/images/manage_node_02.png differ diff --git a/doc/images/manage_node_03.png b/doc/images/manage_node_03.png new file mode 100644 index 000000000..1b6067e8d Binary files /dev/null and b/doc/images/manage_node_03.png differ diff --git a/doc/images/manage_node_04.png b/doc/images/manage_node_04.png new file mode 100644 index 000000000..d6d2c3a6d Binary files /dev/null and b/doc/images/manage_node_04.png differ diff --git a/doc/images/manage_oauth_01.png b/doc/images/manage_oauth_01.png new file mode 100644 index 000000000..cbc3b1e7b Binary files /dev/null and b/doc/images/manage_oauth_01.png differ diff --git a/doc/images/manage_oauth_02.png b/doc/images/manage_oauth_02.png new file mode 100644 index 000000000..0092c52a1 Binary files /dev/null and b/doc/images/manage_oauth_02.png differ diff --git a/doc/images/manage_org_01.png b/doc/images/manage_org_01.png new file mode 100644 index 000000000..37e62f2a0 Binary files /dev/null and b/doc/images/manage_org_01.png differ diff --git a/doc/images/manage_org_02.png b/doc/images/manage_org_02.png new file mode 100644 index 000000000..52161dea1 Binary files /dev/null and b/doc/images/manage_org_02.png differ diff --git a/doc/images/manage_team_01.png b/doc/images/manage_team_01.png new file mode 100644 index 000000000..ff5908bbb Binary files /dev/null and b/doc/images/manage_team_01.png differ diff --git a/doc/images/manage_tool_01.png b/doc/images/manage_tool_01.png new file mode 100644 index 000000000..2cf005f66 Binary files /dev/null and b/doc/images/manage_tool_01.png differ diff --git a/doc/images/manage_user_01.png b/doc/images/manage_user_01.png new file mode 100644 index 000000000..8596ee282 Binary files /dev/null and b/doc/images/manage_user_01.png differ diff --git a/doc/images/manage_user_02.png b/doc/images/manage_user_02.png new file mode 100644 index 000000000..9a955cc49 Binary files /dev/null and b/doc/images/manage_user_02.png differ diff --git a/doc/images/org_node_manager_1.png b/doc/images/org_node_manager_1.png new file mode 100644 index 000000000..375447db2 Binary files /dev/null and b/doc/images/org_node_manager_1.png differ diff --git a/doc/images/org_node_manager_2.png b/doc/images/org_node_manager_2.png new file mode 100644 index 000000000..db937ee4e Binary files /dev/null and b/doc/images/org_node_manager_2.png differ diff --git a/doc/images/org_node_process.png b/doc/images/org_node_process.png new file mode 100644 index 000000000..758092622 Binary files /dev/null and b/doc/images/org_node_process.png differ diff --git a/doc/images/org_tag_manager.png b/doc/images/org_tag_manager.png new file mode 100644 index 000000000..f3cfdaa27 Binary files /dev/null and b/doc/images/org_tag_manager.png differ diff --git a/doc/images/org_tag_node.png b/doc/images/org_tag_node.png new file mode 100644 index 000000000..890af1469 Binary files /dev/null and b/doc/images/org_tag_node.png differ diff --git a/doc/images/org_tag_scheme.png b/doc/images/org_tag_scheme.png new file mode 100644 index 000000000..b8c352587 Binary files /dev/null and b/doc/images/org_tag_scheme.png differ diff --git "a/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/OAuth\347\256\241\347\220\206.md" "b/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/OAuth\347\256\241\347\220\206.md" new file mode 100644 index 000000000..d2cc8fbad --- /dev/null +++ "b/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/OAuth\347\256\241\347\220\206.md" @@ -0,0 +1,18 @@ +# OAuth管理 + +- 可**创建**、**编辑**、**清除**主流代码托管平台的Oauth应用配置,为使用者提供OAuth授权支持。 + +- 支持平台及如何创建OAuth应用: + + - 腾讯工蜂:[创建 OAuth 应用程序](https://code.tencent.com/help/oauth2/) + - GitHub:[创建 OAuth 应用程序](https://docs.github.com/cn/developers/apps/building-oauth-apps/creating-an-oauth-app) + - Gitee:[创建 OAuth 应用程序](https://gitee.com/api/v5/oauth_doc#/list-item-3) + - GitLab:[创建 OAuth 应用程序](https://docs.gitlab.com/ee/integration/oauth_provider.html) + +![OAuth管理](../../../images/manage_oauth_01.png) + +![OAuth管理](../../../images/manage_oauth_02.png) + +::: tip +配置OAuth应用时,回调地址栏需填入当前TCA平台配置的域名或IP地址(如当前页面非80端口,需要显式指定端口号),作为Git平台上OAuth应用的回调地址。 +::: diff --git "a/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\210\206\346\236\220\350\256\260\345\275\225\347\256\241\347\220\206.md" "b/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\210\206\346\236\220\350\256\260\345\275\225\347\256\241\347\220\206.md" new file mode 100644 index 000000000..82db6162f --- /dev/null +++ "b/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\210\206\346\236\220\350\256\260\345\275\225\347\256\241\347\220\206.md" @@ -0,0 +1,7 @@ +# 分析记录管理 + +- 可查看平台**全部分析记录**。 + +- 可点击查阅**分析记录详情**。 + +![分析记录列表](../../../images/manage_job_01.png) \ No newline at end of file diff --git "a/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\220\216\345\217\260\347\256\241\347\220\206\350\257\264\346\230\216.md" "b/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\220\216\345\217\260\347\256\241\347\220\206\350\257\264\346\230\216.md" deleted file mode 100644 index 113f0020b..000000000 --- "a/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\220\216\345\217\260\347\256\241\347\220\206\350\257\264\346\230\216.md" +++ /dev/null @@ -1,72 +0,0 @@ -# 后台管理说明 - -仅**超级管理员**可进入后台管理页面 - -包含以下管理页面:`用户管理`、`分析记录管理`、`节点管理`、`工具管理` - -## 用户管理 - -- 可**查看**、**编辑**、**创建**平台用户。 - -- 可配置用户的**登录密码**、**用户级别**、**超级管理员**等。 - -![用户管理](../../../images/manage_01.png) - -## 分析记录管理 - -- 可查看平台**全部分析记录**。 - -- 可点击查阅**分析记录详情**。 - -![分析记录管理](../../../images/manage_02.png) - -## 节点管理 - -- 可查看**常驻节点状态**。 - -- 可**查看**、**编辑**、**删除**常驻节点。 - -- 可配置节点**工具进程**。 - -- 可配置**标签** - -![节点管理](../../../images/manage_03.png) - -## 工具管理 - -- 可查看**全部工具**(包含平台提供工具、团队自定义工具)。 - -- 可**查看**、**编辑**工具。 - -- 可变更工具**权限状态**。 - -![工具管理](../../../images/manage_04.png) - -::: tip -工具的权限状态仅能由**平台管理员**进行变更调整,需谨慎调整 - -- **团队内可用**:即工具配置了可用团队白名单的团队可以使用该工具,默认创建工具的团队已在白名单内 - -- **全平台可用**:即不同团队都可见可用该工具 - -- **支持自定义规则,全平台可用**:即该工具不同团队都可见可用,且支持用户添加团队所需的自定义规则,该自定义规则存在团队隔离,仅团队内可以,其他团队不可使用 -::: - -## OAuth管理 - -- 可**创建**、**编辑**、**清除**主流代码托管平台的Oauth应用配置,为使用者提供OAuth授权支持。 - -- 支持平台及如何创建OAuth应用: - - - 腾讯工蜂:[创建 OAuth 应用程序](https://code.tencent.com/help/oauth2/) - - GitHub:[创建 OAuth 应用程序](https://docs.github.com/cn/developers/apps/building-oauth-apps/creating-an-oauth-app) - - Gitee:[创建 OAuth 应用程序](https://gitee.com/api/v5/oauth_doc#/list-item-3) - - GitLab:[创建 OAuth 应用程序](https://docs.gitlab.com/ee/integration/oauth_provider.html) - -![OAuth管理](../../../images/manage_05.png) - -![OAuth管理](../../../images/manage_06.png) - -::: tip -配置OAuth应用时,回调地址栏需填入当前TCA平台配置的域名或IP地址(如当前页面非80端口,需要显式指定端口号),作为Git平台上OAuth应用的回调地址。 -::: diff --git "a/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" "b/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" new file mode 100644 index 000000000..68a879e2b --- /dev/null +++ "b/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" @@ -0,0 +1,9 @@ +# 团队管理 + +- 可查看平台创建的团队列表,并提供了相应筛选 + +- 可**禁用**、**恢复**团队 + +![团队列表](../../../images/manage_org_01.png) + +![团队操作](../../../images/manage_org_02.png) \ No newline at end of file diff --git "a/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206.md" "b/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206.md" new file mode 100644 index 000000000..0b62da6b1 --- /dev/null +++ "b/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206.md" @@ -0,0 +1,19 @@ +# 工具管理 + +- 可查看**全部工具**(包含平台提供工具、团队自定义工具)。 + +- 可**查看**、**编辑**工具。 + +- 可变更工具**权限状态**。 + +![工具管理](../../../images/manage_tool_01.png) + +::: tip +工具的权限状态仅能由**平台管理员**进行变更调整,需谨慎调整 + +- **团队内可用**:即工具配置了可用团队白名单的团队可以使用该工具,默认创建工具的团队已在白名单内 + +- **全平台可用**:即不同团队都可见可用该工具 + +- **支持自定义规则,全平台可用**:即该工具不同团队都可见可用,且支持用户添加团队所需的自定义规则,该自定义规则存在团队隔离,仅团队内可以,其他团队不可使用 +::: \ No newline at end of file diff --git "a/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\347\224\250\346\210\267\347\256\241\347\220\206.md" "b/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\347\224\250\346\210\267\347\256\241\347\220\206.md" new file mode 100644 index 000000000..b51f8f02b --- /dev/null +++ "b/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\347\224\250\346\210\267\347\256\241\347\220\206.md" @@ -0,0 +1,9 @@ +# 用户管理 + +- 可**查看**、**编辑**、**创建**平台用户。 + +- 可配置用户的**登录密码**、**用户级别**、**超级管理员**等。 + +![用户列表](../../../images/manage_user_01.png) + +![用户编辑](../../../images/manage_user_02.png) \ No newline at end of file diff --git "a/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" "b/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" new file mode 100644 index 000000000..3a23e9ddc --- /dev/null +++ "b/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" @@ -0,0 +1,14 @@ +# 节点管理 + +- 可查看**常驻节点状态**,包含**公共节点**和**团队节点**。 + +- 可**查看**、**编辑**、**删除**常驻节点。 + +- 可配置节点**工具进程**。 + +- 可配置**节点标签** + +![节点管理](../../../images/manage_node_01.png) +![节点管理](../../../images/manage_node_02.png) +![节点管理](../../../images/manage_node_03.png) +![节点管理](../../../images/manage_node_04.png) \ No newline at end of file diff --git "a/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\351\241\271\347\233\256\347\256\241\347\220\206.md" "b/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\351\241\271\347\233\256\347\256\241\347\220\206.md" new file mode 100644 index 000000000..3f6e4fbf2 --- /dev/null +++ "b/doc/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\351\241\271\347\233\256\347\256\241\347\220\206.md" @@ -0,0 +1,7 @@ +# 项目管理 + +- 可查看平台创建的项目列表,并提供了提供相应筛选 + +- 可**禁用**、**恢复**项目 + +![项目列表](../../../images/manage_team_01.png) \ No newline at end of file diff --git "a/doc/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" "b/doc/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" index 234c9e649..274a04e62 100644 --- "a/doc/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" +++ "b/doc/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" @@ -1,4 +1,4 @@ -# 团队管理 +# 团队说明 ![成员权限](../../../images/team_member.png) diff --git "a/doc/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\346\210\220\345\221\230\346\235\203\351\231\220.md" "b/doc/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\346\210\220\345\221\230\346\235\203\351\231\220.md" index 5967036a1..d4dfc5d66 100644 --- "a/doc/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\346\210\220\345\221\230\346\235\203\351\231\220.md" +++ "b/doc/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\346\210\220\345\221\230\346\235\203\351\231\220.md" @@ -1,6 +1,6 @@ # 成员权限 -## 团队成员管理 +## 团队成员 ![成员权限](../../../images/team_member.png) @@ -10,7 +10,7 @@ **团队普通成员**:可以创建项目,可以访问自己有权限的项目。创建项目的人会自动成为这个项目的项目管理员。 -## 项目成员管理 +## 项目成员 项目成员分为**项目管理员**和**项目普通成员**。 diff --git "a/doc/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" "b/doc/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" new file mode 100644 index 000000000..e9684cd13 --- /dev/null +++ "b/doc/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" @@ -0,0 +1,105 @@ +# 节点与标签 + +除了使用**公共节点**执行代码分析外,团队还可以利用**团队标签**注册并使用**团队节点**。 + +## 名词释义与特点 + +- 团队节点是**团队注册并管理**的**私有**节点。 + +- 团队节点**仅会运行**当前团队所属的分析任务。 + +- 团队标签是用于关联节点机器与分析项目。 + ::: tip + 当一个分析项目在方案中配置运行环境为团队标签后,该项目创建的任务就会下发到团队标签关联的节点机器上运行 + ::: + +## 适用场景 + +1. 业务项目**不想**在公共机器上**排队**等待 + +2. 业务项目**代码比较敏感**,不能在公共机器上运行 + +3. 业务项目需要**依赖特定**的**机器环境**(比如CPU架构、操作系统等) + +4. ... + +以上场景,均可考虑使用团队节点,业务团队提供机器资源接入作为团队节点,仅分析自己业务的代码库,**保证执行效率**,**保护源码不泄漏**,**支持项目特殊依赖**等 + +## 团队节点注册 + +- 根据环境下载客户端二进制文件或拉取源码,参考[客户端](../客户端/配置说明.md)。 + +- 通过终端启动客户端: + + - 客户端二进制启动 + + ```bash + ./codepuppy start -t TOKEN --org-sid ORG_SID + ``` + + - 客户端源码启动 + + ```bash + python3 codepuppy.py start -t TOKEN --org-sid ORG_SID + ``` + + ::: tip + 1. TOKEN 可以从平台**个人中心-个人令牌**页面获取 + 2. ORG_SID 可以从**页面链接**中获取 + ::: + +## 团队节点管理 + +完成团队节点注册后,可以在当前团队下看到对应的节点信息,同时**需要进行配置** + +::: warning +- 团队节点**首次注册**时,需要手动在平台上配置**所属标签**、**节点可用性**、**工具进程**等。 +- 将节点的**节点可用性**调整为**活跃**后,运行客户端节点的终端会输出**心跳上报成功**的日志 +::: + +- 首次注册团队节点,节点状态为不可用 + + ![注册团队节点](../../../images/org_node_manager_1.png) + +- 调整后的节点 + + ![注册团队节点](../../../images/org_node_manager_2.png) + +- 配置节点关联的工具进程: + + ![配置工具进程](../../../images/org_node_process.png) + +::: tip +1. 团队节点使用的**所属标签**均为当前团队内创建的标签,可参见[团队标签管理](#团队标签管理) +2. 团队标签可以参考`CodeDog`标签为不同的系统类型(Linux、MacOS、Windows)建立标签,比如`专属标签-Linux`、`专属标签-Mac`等 +::: + +### 团队节点执行任务范围 + +::: warning 使用团队节点运行分析任务的前提 +对应分析项目使用的分析方案中,需要配置分析方案中的**运行环境**为该团队节点配置的所属标签。 +::: + +::: warning 团队节点执行的任务范围取决于该节点的负责人 +- 如果节点负责人为团队管理员,该节点可以执行当前团队所有项目的分析任务 +- 如果节点负责人为项目管理员,该节点只能运行指定项目下的分析任务 +- 如果节点负责人为部分代码库的管理员,该节点只能运行对应代码库的分析任务 +::: + + +## 团队标签管理 + +您可以创建一个团队标签,并配置到您的团队节点和您的分析方案中 + +- 创建团队标签。 + + ![创建团队标签](../../../images/org_tag_manager.png) + +- 配置团队节点所属标签。 + + ![节点配置团队标签](../../../images/org_tag_node.png) + +- 配置分析方案运行环境。 + + ![方案配置团队标签](../../../images/org_tag_scheme.png) + diff --git a/web/.eslintrc.js b/web/.eslintrc.js index 783e634eb..50c525f11 100644 --- a/web/.eslintrc.js +++ b/web/.eslintrc.js @@ -5,52 +5,53 @@ module.exports = { es2020: true, node: true, }, + parser: 'esprima', + parserOptions: { + ecmaVersion: 2021, + sourceType: 'module', + ecmaFeatures: { + jsx: true, // 允许解析JSX + }, + // tsconfigRootDir: __dirname, + }, + plugins: ['react'], extends: [ 'eslint:recommended', + 'plugin:react/recommended', ], - parserOptions: { - ecmaVersion: 11, - sourceType: 'module', - tsconfigRootDir: __dirname, + settings: { + react: { + version: 'detect', // 告诉eslint-plugin-react自动检测要使用的React版本 + }, + }, + rules: { + // 禁止对函数参数再赋值 - 关闭 + 'no-param-reassign': 'off', }, overrides: [ { - files: [ - '*.ts', - '*.tsx', - ], - extends: [ - 'plugin:react/recommended', - ], + files: ['*.ts', '*.tsx'], parser: '@typescript-eslint/parser', - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - ecmaVersion: 11, - sourceType: 'module', - }, - plugins: [ - 'react', - '@typescript-eslint', + plugins: ['@typescript-eslint'], + extends: [ + 'plugin:@typescript-eslint/recommended', ], rules: { + // 禁止 ts any - 关闭 + '@typescript-eslint/no-explicit-any': 'off', + // 禁止 ts require - 关闭 '@typescript-eslint/no-require-imports': 'off', - // 禁止对函数参数再赋值 - 关闭 - 'no-param-reassign': 'off', - '@typescript-eslint/no-unused-vars': ['error', { vars: 'all', args: "after-used", ignoreRestSiblings: true }], - 'no-undef': 'off' + // 禁止定义了变量却不使用它 + '@typescript-eslint/no-unused-vars': [ + 'error', + { + args: 'after-used', + ignoreRestSiblings: true, + argsIgnorePattern: '^_.+', + varsIgnorePattern: '^_.+' + } + ] }, }, ], - rules: { - // 禁止对函数参数再赋值 - 关闭 - 'no-param-reassign': 'off', - }, - settings: { - react: { - pragma: 'React', - version: 'detect', - }, - }, }; diff --git a/web/package.json b/web/package.json index e37ccac49..ac004d1f2 100644 --- a/web/package.json +++ b/web/package.json @@ -16,12 +16,12 @@ "build": "lerna run build --stream" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "^4.33.0", - "@typescript-eslint/parser": "^4.33.0", - "eslint": "^7.32.0", - "eslint-plugin-react": "^7.26.1", - "lerna": "^4.0.0", + "@typescript-eslint/eslint-plugin": "^5.13.0", + "@typescript-eslint/parser": "^5.13.0", + "eslint": "^8.10.0", + "eslint-plugin-react": "^7.29.3", + "lerna": "^5.1.7", "lerna-update-wizard": "^1.1.2", - "typescript": "^4.4.2" + "typescript": "^4.5.5" } } \ No newline at end of file diff --git a/web/packages/framework/README.md b/web/packages/framework/README.md index a9f34ef3d..2cd64e7d2 100644 --- a/web/packages/framework/README.md +++ b/web/packages/framework/README.md @@ -54,6 +54,11 @@ | FAVICON | 网页图标 | | MICRO_FRONTEND_API | 微前端资源配置地址 | +### setting 接口配置项 + +| 环境变量 | 说明 | +| ---------------------: | :-------------------------------------------- | + ## 公共依赖处理 微前端基座已经将部分公共依赖使用 `expose-loader` 暴露为全局依赖。子前端项目只需要在 webpack 配置中加入如下配置,将这部分公共依赖排除打包。 diff --git a/web/packages/framework/global.d.ts b/web/packages/framework/global.d.ts index 6562e9377..7a6e50717 100644 --- a/web/packages/framework/global.d.ts +++ b/web/packages/framework/global.d.ts @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - declare module '*.scss'; type Store = import('redux').Store; diff --git a/web/packages/framework/package.json b/web/packages/framework/package.json index 3a3407d83..e772bc022 100644 --- a/web/packages/framework/package.json +++ b/web/packages/framework/package.json @@ -2,15 +2,23 @@ "name": "framework", "version": "1.0.0", "description": "微前端基座", + "private": true, "keywords": [ "single-spa", "micro-frontend" ], "license": "MIT", + "main": "src/index.ts", + "files": [ + "src" + ], "scripts": { - "dev": "NODE_ENV=development webpack server --config ./scripts/webpack.dev.js --open --progress --color", - "build": "NODE_ENV=production webpack --config ./scripts/webpack.prod.js --progress --color", - "analyzer": "webpack-bundle-analyzer --port 8888 ./dist/stats.json" + "dev": "NODE_ENV=development webpack server --config ./webpack.config.ts --open --progress --color", + "build": "NODE_ENV=production webpack --config ./webpack.config.ts --progress --color", + "build:analyzer:comment": "echo '构建并打开bundle分析插件'", + "build:analyzer": "BUNDLE_ANALYZER=true NODE_ENV=production webpack --config ./webpack.config.ts --progress --color", + "lint": "eslint --ext .js,.jsx,.ts,.tsx src/", + "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx src/ --fix" }, "dependencies": { "@types/lodash": "^4.14.175", @@ -19,7 +27,7 @@ "@types/react-dom": "^17.0.9", "@types/react-redux": "^7.1.18", "classnames": "^2.3.1", - "coding-oa-uikit": "^4.3.9", + "coding-oa-uikit": "^4.3.10", "lodash": "^4.17.21", "nprogress": "^0.2.0", "react": "^17.0.2", @@ -30,37 +38,7 @@ "universal-cookie": "^4.0.4" }, "devDependencies": { - "@babel/core": "^7.15.5", - "@babel/plugin-proposal-class-properties": "^7.14.5", - "@babel/plugin-proposal-object-rest-spread": "^7.15.6", - "@babel/plugin-transform-runtime": "^7.15.0", - "@babel/preset-env": "^7.15.6", - "@babel/preset-react": "^7.14.5", - "@babel/preset-typescript": "^7.15.0", - "@hot-loader/react-dom": "^17.0.1", - "@types/friendly-errors-webpack-plugin": "^0.1.4", - "@types/mini-css-extract-plugin": "^2.3.0", - "@types/sass": "^1.16.1", - "babel-loader": "^8.2.2", - "clean-webpack-plugin": "^4.0.0", - "copy-webpack-plugin": "^9.0.1", - "css-loader": "^6.3.0", - "eslint-webpack-plugin": "^3.0.1", - "expose-loader": "^3.0.0", - "friendly-errors-webpack-plugin": "^1.7.0", - "html-webpack-plugin": "^5.3.2", - "mini-css-extract-plugin": "^2.3.0", - "postcss-loader": "^6.1.1", - "postcss-preset-env": "^6.7.0", - "react-hot-loader": "^4.13.0", - "sass": "^1.42.1", - "sass-loader": "^12.1.0", - "style-loader": "^3.3.0", - "webpack": "^5.54.0", - "webpack-bundle-analyzer": "^4.4.2", - "webpack-cli": "^4.8.0", - "webpack-dev-server": "^4.3.0", - "webpack-merge": "^5.8.0", - "webpackbar": "^5.0.0-3" + "@tencent/micro-frontend-webpack": "^1.0.0", + "expose-loader": "^3.0.0" } } \ No newline at end of file diff --git a/web/packages/framework/scripts/envs.js b/web/packages/framework/scripts/envs.js deleted file mode 100644 index 3d0c51b90..000000000 --- a/web/packages/framework/scripts/envs.js +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -const envs = { - PUBLIC_PATH: process.env.PUBLIC_PATH || '/', - MICRO_VERSION_INTERVAL: process.env.MICRO_VERSION_INTERVAL, - MICRO_VERSION_ENABLED: process.env.MICRO_VERSION_ENABLED, - MICRO_FRONTEND_SETTING_API: process.env.MICRO_FRONTEND_SETTING_API, - TITLE: process.env.TITLE, - DESCRIPTION: process.env.DESCRIPTION, - KEYWORDS: process.env.KEYWORDS, - FAVICON: process.env.FAVICON, - MICRO_FRONTEND_API: process.env.MICRO_FRONTEND_API, - GIT_REVISION: process.env.GIT_REVISION || 'dirty', -}; - -// 用于生成index.runtime.html 可被替换的值 -const runtimeKeys = ['TITLE', 'DESCRIPTION', 'KEYWORDS', 'FAVICON', 'MICRO_FRONTEND_API', 'MICRO_FRONTEND_SETTING_API']; - -const runtimeEnvs = {}; - -runtimeKeys.forEach((key) => { - runtimeEnvs[key] = `__${key}__`; -}); - -module.exports = { - envs, - runtimeEnvs, -}; diff --git a/web/packages/framework/scripts/webpack.common.js b/web/packages/framework/scripts/webpack.common.js deleted file mode 100644 index a1994528f..000000000 --- a/web/packages/framework/scripts/webpack.common.js +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -const path = require('path'); -const webpack = require('webpack'); -const { CleanWebpackPlugin } = require('clean-webpack-plugin'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const CopyWebpackPlugin = require('copy-webpack-plugin'); -const EslintWebpackPlugin = require('eslint-webpack-plugin'); -// 日志优化 -const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin'); -const WebpackBar = require('webpackbar'); - -// 根据NODE_ENV分别获取不同的config -const isDev = process.env.NODE_ENV === 'development'; -// 获取项目环境变量 -const { envs, runtimeEnvs } = require('./envs'); -// 打包目录路径 -const BASE_DIR = path.resolve(__dirname, '..'); -const buildPath = path.resolve(BASE_DIR, 'dist'); -// index.html路径 -const indexPath = path.resolve(BASE_DIR, 'public', 'index.html'); -// static 路径 -const staticPath = path.resolve(BASE_DIR, 'public', 'static'); - -const htmlMinify = { - html5: true, // 根据HTML5规范解析输入 - collapseWhitespace: true, // 折叠空白区域 - preserveLineBreaks: false, - minifyCSS: true, // 压缩文内css - minifyJS: true, // 压缩文内js - removeComments: true, // 移除注释 -}; - -module.exports = { - entry: { - framework: path.resolve(BASE_DIR, 'src/index.ts'), - }, - output: { - filename: '[name].bundle.js', - path: buildPath, - publicPath: process.env.PUBLIC_PATH || '/', - }, - resolve: { - // 尝试按顺序解析这些后缀名 - extensions: ['.ts', '.tsx', '.js', '.jsx', '.svg'], - alias: { - '@src': path.resolve(BASE_DIR, 'src'), - 'react-dom': '@hot-loader/react-dom', - }, - modules: [BASE_DIR, 'node_modules'], - }, - module: { - rules: [ - { - test: /\.[jt]sx?$/i, - exclude: /node_modules/, - loader: 'babel-loader', - options: { - rootMode: 'upward', - }, - }, - // Images - { - test: /\.(?:ico|gif|png|jpg|jpeg)$/i, - type: 'asset/resource', - }, - // Fonts and SVGs - { - test: /\.(woff(2)?|eot|ttf|otf|svg|)$/i, - type: 'asset/inline', - }, - { - test: /\.css$/i, - use: [ - isDev ? 'style-loader' : MiniCssExtractPlugin.loader, - 'css-loader', - ], - }, - { - test: /\.s[ac]ss$/i, - exclude: [path.resolve(BASE_DIR, 'public')], - use: [ - // 将 JS 字符串生成为 style 节点 - isDev ? 'style-loader' : MiniCssExtractPlugin.loader, - // 将 CSS 转化成 CommonJS 模块 - { - loader: 'css-loader', - options: { - modules: { - localIdentName: '[local]-[hash:base64:10]', - exportLocalsConvention: 'camelCase', - }, - importLoaders: 2, - }, - }, - // 将 Sass 编译成 CSS - { - loader: 'sass-loader', - options: { - // Prefer `dart-sass` - implementation: require('sass'), - }, - }, - ], - }, - ], - }, - plugins: [ - // 打包前移除/清理 打包目录 - new FriendlyErrorsWebpackPlugin(), - new CleanWebpackPlugin(), - new webpack.DefinePlugin({ - 'process.env': Object.keys(envs).reduce((e, key) => { - e[key] = JSON.stringify(envs[key]); - return e; - }, {}), - }), - new EslintWebpackPlugin({ - fix: true, - extensions: ['js', 'jsx', 'tsx', 'ts'], - }), - new HtmlWebpackPlugin({ - inject: true, - template: indexPath, - envs, - minify: htmlMinify, - }), - // 该配置生成一个 index.runtime.html 模板用于提供 index.html runtime 的能力 - new HtmlWebpackPlugin({ - inject: true, - template: indexPath, - filename: 'index.runtime.html', - minify: htmlMinify, - envs: { - ...envs, - ...runtimeEnvs, - }, - }), - new CopyWebpackPlugin({ - patterns: [{ - from: staticPath, - to: path.resolve(buildPath, 'static'), - }, { - from: path.resolve(BASE_DIR, 'public', '404.html'), - to: buildPath, - }, { - from: path.resolve(BASE_DIR, 'public', 'unsupported-browser.html'), - to: buildPath, - }], - }), - // // 忽略第三方包指定目录,让这些指定目录不要被打包进去,对moment操作参考:https://blog.csdn.net/qq_17175013/article/details/86845624 - new webpack.IgnorePlugin({ - resourceRegExp: /^\.\/locale$/, - contextRegExp: /moment$/, - }), - new WebpackBar(), - ], -}; diff --git a/web/packages/framework/scripts/webpack.dev.js b/web/packages/framework/scripts/webpack.dev.js deleted file mode 100644 index 99207edba..000000000 --- a/web/packages/framework/scripts/webpack.dev.js +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -const { merge } = require('webpack-merge'); -const common = require('./webpack.common.js'); -module.exports = merge(common, { - devtool: 'inline-source-map', - target: 'web', - devServer: { - hot: true, - allowedHosts: 'all', - host: process.env.HOST || '0.0.0.0', - port: process.env.PORT || 4000, - historyApiFallback: true, - compress: true, - devMiddleware: { - writeToDisk: true, - }, - headers: { - 'Access-Control-Allow-Origin': '*', - }, - proxy: { - // '/static': { - // ws: false, - // target: 'http://ip', - // changeOrigin: true, - // }, - // '/server': { - // ws: false, - // target: 'http://ip:8000', - // changeOrigin: true, - // pathRewrite: { - // '^/server': '', - // }, - // }, - }, - }, -}); diff --git a/web/packages/framework/scripts/webpack.prod.js b/web/packages/framework/scripts/webpack.prod.js deleted file mode 100644 index 355980b16..000000000 --- a/web/packages/framework/scripts/webpack.prod.js +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -const { merge } = require('webpack-merge'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -// 文件体积监控 -const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); - - -const common = require('./webpack.common.js'); - -module.exports = merge(common, { - output: { - filename: '[name]-[chunkhash:8].js', - chunkFilename: '[name]-[chunkhash:8].js', - publicPath: process.env.PUBLIC_PATH || '/', - }, - devtool: false, - optimization: { - runtimeChunk: true, - splitChunks: { - chunks: 'all', - name: (module, chunks, cacheGroupKey) => { - const allChunksNames = chunks.map(item => item.name).join('~'); - return `${cacheGroupKey}~${allChunksNames}`; - }, - cacheGroups: { - vendors: { - test: /[\\/]node_modules[\\/]/, - chunks: 'all', - enforce: true, - }, - }, - }, - }, - performance: { - hints: false, - }, - plugins: [ - new MiniCssExtractPlugin({ - filename: '[name]-[chunkhash:8].css', - chunkFilename: '[name]-[chunkhash:8].css', - ignoreOrder: false, - }), - new BundleAnalyzerPlugin({ - analyzerMode: 'disabled', // 不启动展示打包报告的http服务器 - generateStatsFile: true, // 不打开网站,但是在dist生成stats.json文件 - }), - ], -}); diff --git a/web/packages/framework/src/constant.ts b/web/packages/framework/src/constant.ts index a1767861e..cf3a0d2db 100755 --- a/web/packages/framework/src/constant.ts +++ b/web/packages/framework/src/constant.ts @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - export default { LOG_LEVEL: 'LOG_LEVEL', MICRO_FRONTEND_API: 'MICRO_FRONTEND_API', @@ -13,8 +7,8 @@ export default { MICRO_VERSION_ENABLED: 'MICRO_VERSION_ENABLED', }; -export const DEFAULT_MICRO_FRONTEND_API = '/static/configs.json'; +export const DEFAULT_MICRO_FRONTEND_API = '/static/micro-frontend/configs/latest/'; -export const DEFAULT_MICRO_FRONTEND_SETTING_API = '/static/settings.json'; +export const DEFAULT_MICRO_FRONTEND_SETTING_API = '/static/micro-frontend/settings/'; export const DEFAULT_MICRO_VERSION_INTERVAL = 5 * 60 * 1000; // 5min diff --git a/web/packages/framework/src/hook/index.ts b/web/packages/framework/src/hook/index.ts index 930ac0c74..12e9319f0 100644 --- a/web/packages/framework/src/hook/index.ts +++ b/web/packages/framework/src/hook/index.ts @@ -1,18 +1,15 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - +/** + * 挂载数据到window上 + */ import { Store } from 'redux'; import { registration } from '@src/register'; -import { TInjectAsyncReducer } from '@src/store'; +import { InjectAsyncReducer } from '@src/store'; const HOOK_NAME = 'microHook'; const DEV_API_LIST_NAME = 'microDevApiList'; -export default (store: Store, injectAsyncReducer: TInjectAsyncReducer) => { +export default (store: Store, injectAsyncReducer: InjectAsyncReducer) => { /** * 挂载 window 注册函数,禁止复写 */ diff --git a/web/packages/framework/src/index.ts b/web/packages/framework/src/index.ts index 487a69054..2b7719199 100644 --- a/web/packages/framework/src/index.ts +++ b/web/packages/framework/src/index.ts @@ -1,20 +1,12 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -import { registerApplication, AppProps, start } from 'single-spa'; -import { Store } from 'redux'; +import { registerApplication, start } from 'single-spa'; import NProgress from 'nprogress'; import 'nprogress/nprogress.css'; import 'coding-oa-uikit/dist/coding-oa-uikit.css'; -import { omit } from 'lodash'; import MicroApplication from '@src/meta/application'; import applicationLoader from '@src/loader'; import { registration } from '@src/register'; -import { store, injectAsyncReducer, TInjectAsyncReducer } from '@src/store'; +import { store, injectAsyncReducer } from '@src/store'; import createHook from '@src/hook'; import { getOrCreateBodyContainer, info, debug, LOG_DEBUG } from '@src/utils'; @@ -27,16 +19,6 @@ const MAIN_CONTAINER = 'main-container'; // 配置顶部加载进度条 NProgress.configure({ showSpinner: false }); -interface ApplicationProps { - store: Store; - injectAsyncReducer: TInjectAsyncReducer; - rootDom: HTMLElement; -} - -const getApplicationProps = (props: AppProps & ApplicationProps): ApplicationProps => { - return omit(props, ['name', 'singleSpa', 'mountParcel']); -}; - /** * 在window上挂载一些全局参数 */ @@ -60,9 +42,9 @@ createHook(store, injectAsyncReducer); } else { info('加载App', name, '版本', commitId, '变更', changeAt); } - registerApplication( + registerApplication( name, - async (props: AppProps & ApplicationProps) => { + async ({ name, singleSpa, mountParcel, ...customProps }) => { NProgress.start(); // 获取微前端应用资源,并进行微前端应用注册 await app.loadResources(); @@ -74,36 +56,35 @@ createHook(store, injectAsyncReducer); // 获取微前端应用生命周期 const { bootstrap, mount, unmount, update = Promise.resolve } = register.lifeCycles; NProgress.done(); - // 获取ApplicationProps - const applicationProps = getApplicationProps(props); return { bootstrap: [ async () => { debug('Bootstrap app', name); - await bootstrap(applicationProps); + await bootstrap(customProps); }, ], mount: [ async () => { debug('Mount app', name); - await mount(applicationProps); + await mount(customProps); }, ], update: [ async () => { debug('Update app', name); - await update(applicationProps); + await update(customProps); }, ], unmount: [ async () => { debug('Unmount app', name); - await unmount(applicationProps); + await unmount(customProps); activedApps.delete(app); }, ], }; - }, (location: Location) => { + }, + (location: Location) => { const shouldActive = app.path().test(location.pathname); if (shouldActive) { activedApps.add(app); diff --git a/web/packages/framework/src/loader/api.ts b/web/packages/framework/src/loader/api.ts index e11d2fc72..c0a30ca29 100644 --- a/web/packages/framework/src/loader/api.ts +++ b/web/packages/framework/src/loader/api.ts @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import isEmpty from 'lodash/isEmpty'; import { message } from 'coding-oa-uikit'; @@ -20,9 +14,6 @@ const MICRO_FRONTEND_API = getMetaEnv(Constant.MICRO_FRONTEND_API, DEFAULT_MICRO * 从 API 中加载,需要接口返回 MicroApplicationProps 的数据 */ export class MicroApplicationAPILoader implements MicroApplicationLoader { - public renderUI: (production?: MicroApplication[]) => null; - public exit: () => {}; - private url: string; constructor() { this.url = MICRO_FRONTEND_API; diff --git a/web/packages/framework/src/loader/development.ts b/web/packages/framework/src/loader/development.ts index 92cb916c7..52e724fa6 100644 --- a/web/packages/framework/src/loader/development.ts +++ b/web/packages/framework/src/loader/development.ts @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import Cookies from 'universal-cookie'; import { isEmpty, uniqBy } from 'lodash'; import { message } from 'coding-oa-uikit'; @@ -77,7 +71,7 @@ export class MicroApplicationDevelopmentLoader implements MicroApplicationLoader // cookies中移除开发模式微前端资源配置 cookies.remove(Constant.MICRO_FRONTEND_API_LIST, { path: '/', - domain: window.location.hostname + domain: window.location.hostname, }); if (reload) { debug('Exit development success and reload page'); diff --git a/web/packages/framework/src/loader/index.ts b/web/packages/framework/src/loader/index.ts index b23fe0751..97f988c0e 100644 --- a/web/packages/framework/src/loader/index.ts +++ b/web/packages/framework/src/loader/index.ts @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import isEmpty from 'lodash/isEmpty'; import { message } from 'coding-oa-uikit'; // 项目内 @@ -55,10 +49,6 @@ class CombineLoader implements MicroApplicationLoader { message.error('微前端启动失败', 0); throw new Error('微前端启动失败'); } - - public renderUI() { } - - public exit() { } } const loader = new CombineLoader(apiLoader, developmentLoader, settingLoader); diff --git a/web/packages/framework/src/loader/loader.ts b/web/packages/framework/src/loader/loader.ts index dba24f4d9..665637820 100644 --- a/web/packages/framework/src/loader/loader.ts +++ b/web/packages/framework/src/loader/loader.ts @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import MicroApplication from '@src/meta/application'; /** @@ -12,6 +6,6 @@ import MicroApplication from '@src/meta/application'; export interface MicroApplicationLoader { readonly enabled: () => Promise; readonly loadMeta: (production?: MicroApplication[]) => Promise; - readonly renderUI: (production: MicroApplication[], development: MicroApplication[]) => any; - readonly exit: (reload: boolean) => any; + readonly renderUI?: (production: MicroApplication[], development: MicroApplication[]) => void; + readonly exit?: (reload: boolean) => void; } diff --git a/web/packages/framework/src/loader/setting.ts b/web/packages/framework/src/loader/setting.ts index bc6708e8c..f566db222 100644 --- a/web/packages/framework/src/loader/setting.ts +++ b/web/packages/framework/src/loader/setting.ts @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import { toUpper } from 'lodash'; import { getMetaEnv, warn, info } from '@src/utils'; import Constant, { DEFAULT_MICRO_FRONTEND_SETTING_API } from '@src/constant'; diff --git a/web/packages/framework/src/meta/application.ts b/web/packages/framework/src/meta/application.ts index fb3070b88..b5ce50827 100644 --- a/web/packages/framework/src/meta/application.ts +++ b/web/packages/framework/src/meta/application.ts @@ -1,22 +1,24 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import { info } from '@src/utils'; /** * 微前端资源属性类型 */ export interface MicroApplicationProps { + /** 微前端唯一标识 */ name: string; + /** 描述 */ description: string; + /** url匹配,可正则 */ match: string; + /** git commit id */ commitId?: string; + /** 变更描述 */ changeAt?: string; + /** css资源路径列表 */ css: string[]; + /** js资源路径列表 */ js: string[]; + /** 前缀 */ prefix: string[] | string; } diff --git a/web/packages/framework/src/register/index.ts b/web/packages/framework/src/register/index.ts index bb333709e..8bcd84816 100644 --- a/web/packages/framework/src/register/index.ts +++ b/web/packages/framework/src/register/index.ts @@ -1,19 +1,13 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import { toPromise, info } from '@src/utils'; -export interface LifeCycle { +export interface LifeCycle { bootstrap: (config: T) => Promise; mount: (config: T) => Promise; unmount: (config: T) => Promise; update?: (config: T) => Promise; } -export interface RegistrableApp { +export interface RegistrableApp { name: string; lifeCycles: LifeCycle; } @@ -21,7 +15,7 @@ export interface RegistrableApp { /** * 微前端注册器 */ -export class MicroRegistration { +export class MicroRegistration { private microApp: Map> = new Map(); // 注册微前端 diff --git a/web/packages/framework/src/store/allowed-reducers.ts b/web/packages/framework/src/store/allowed-reducers.ts index 25678ceed..5ae09ad60 100644 --- a/web/packages/framework/src/store/allowed-reducers.ts +++ b/web/packages/framework/src/store/allowed-reducers.ts @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - export default [ 'APP', 'INITIAL', diff --git a/web/packages/framework/src/store/index.ts b/web/packages/framework/src/store/index.ts index 21c088439..7f3efdca5 100644 --- a/web/packages/framework/src/store/index.ts +++ b/web/packages/framework/src/store/index.ts @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import { createStore, combineReducers, Reducer } from 'redux'; import allowedReducers from './allowed-reducers'; @@ -12,30 +6,41 @@ interface InjectReducer { } const ASYNC_REDUCERS: InjectReducer = {}; -export type TInjectAsyncReducer = (name: string, reducer: Reducer, override?: boolean) => void; +export type InjectAsyncReducer = (name: string, reducer: Reducer, override?: boolean) => void; -function createGlobalStore() { - // 将store挂载到window - const rootReducers = {}; +/** + * 创建全局store + * @returns store, injectAsyncReducer + */ +const createGlobalStore = () => { + /** 设置 root reducers */ + const rootReducers: InjectReducer = {}; const store = createStore(combineReducers({ ...rootReducers })); - const injectAsyncReducer: TInjectAsyncReducer = (name: string, reducer: Reducer, override = false) => { + /** + * 注入reducer + * @param name 名称 + * @param reducer reducer + * @param override 是否覆盖,默认不覆盖 + * @returns + */ + const injectAsyncReducer: InjectAsyncReducer = (name: string, reducer: Reducer, override = false) => { // reducer白名单控制 if (!allowedReducers.includes(name)) { throw new Error(`[injectAsyncReducer] not allowed inject reducer namespace ${name}`); } // 默认不允许覆盖 - if (ASYNC_REDUCERS[name] && !override) { - return; + if (!(ASYNC_REDUCERS[name] && !override)) { + ASYNC_REDUCERS[name] = reducer; + store.replaceReducer(combineReducers({ ...rootReducers, ...ASYNC_REDUCERS })); } - ASYNC_REDUCERS[name] = reducer; - store.replaceReducer(combineReducers({ ...rootReducers, ...ASYNC_REDUCERS })); }; + return { store, injectAsyncReducer, }; -} +}; const creator = createGlobalStore(); export const { store } = creator; diff --git a/web/packages/framework/src/support/expose.ts b/web/packages/framework/src/support/expose.ts index e2afdaee1..d49a73687 100644 --- a/web/packages/framework/src/support/expose.ts +++ b/web/packages/framework/src/support/expose.ts @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import 'expose-loader?exposes=React!react'; import 'expose-loader?exposes=ReactDOM!react-dom'; import 'expose-loader?exposes=ReactRedux!react-redux'; diff --git a/web/packages/framework/src/support/index.ts b/web/packages/framework/src/support/index.ts index 80510bd15..2b630c75b 100644 --- a/web/packages/framework/src/support/index.ts +++ b/web/packages/framework/src/support/index.ts @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import './unsupported-browser'; import './version'; import './expose'; diff --git a/web/packages/framework/src/support/unsupported-browser.ts b/web/packages/framework/src/support/unsupported-browser.ts index aeff7f6bf..f5acad423 100644 --- a/web/packages/framework/src/support/unsupported-browser.ts +++ b/web/packages/framework/src/support/unsupported-browser.ts @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import { info } from '@src/utils'; const checkBrowser = () => { diff --git a/web/packages/framework/src/support/version-checking/index.tsx b/web/packages/framework/src/support/version-checking/index.tsx index 8d711c656..5c50626fe 100644 --- a/web/packages/framework/src/support/version-checking/index.tsx +++ b/web/packages/framework/src/support/version-checking/index.tsx @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import React from 'react'; import ReactDOM from 'react-dom'; import { getMountedApps } from 'single-spa'; @@ -12,7 +6,7 @@ import xorBy from 'lodash/xorBy'; import toInteger from 'lodash/toInteger'; import useInterval from '@src/utils/use-interval'; -import { error, getMetaEnv, info, getOrCreateBodyContainer } from '@src/utils'; +import { error, getMetaEnv, info, getOrCreateBodyContainer, isTrue } from '@src/utils'; import Constant, { DEFAULT_MICRO_FRONTEND_API, DEFAULT_MICRO_VERSION_INTERVAL } from '@src/constant'; @@ -23,7 +17,7 @@ const MICRO_FRONTEND_API = getMetaEnv(Constant.MICRO_FRONTEND_API, DEFAULT_MICRO const MICRO_VERSION_INTERVAL = toInteger(getMetaEnv(Constant.MICRO_VERSION_INTERVAL)) || DEFAULT_MICRO_VERSION_INTERVAL; // 系统级别是否开启版本检查 -const MICRO_VERSION_ENABLED = getMetaEnv(Constant.MICRO_VERSION_ENABLED) === 'TRUE'; +const MICRO_VERSION_ENABLED = isTrue(getMetaEnv(Constant.MICRO_VERSION_ENABLED, 'true')); /** * 校验挂载的apps新旧版本 @@ -49,31 +43,33 @@ const check = async (mountedApps: string[]) => { }; const VersionChecking = () => { - useInterval(async () => { - // 获取当前挂载的apps - const mountedApps = getMountedApps(); - const diffs = await check(mountedApps); - if (diffs.length > 0) { - message.open({ - type: 'info', - content: ( - - 发现新版本,自动更新中... - - ), - duration: 1, - onClose: () => { - window.location.reload(); - }, - icon: , - }); - } + useInterval(() => { + (async () => { + // 获取当前挂载的apps + const mountedApps = getMountedApps(); + const diffs = await check(mountedApps); + if (diffs.length > 0) { + message.open({ + type: 'info', + content: ( + + 发现新版本,自动更新中... + + ), + duration: 1, + onClose: () => { + window.location.reload(); + }, + icon: , + }); + } + })(); }, MICRO_VERSION_INTERVAL); return <>; diff --git a/web/packages/framework/src/support/version.ts b/web/packages/framework/src/support/version.ts index 12eaac639..ae3dfddf7 100644 --- a/web/packages/framework/src/support/version.ts +++ b/web/packages/framework/src/support/version.ts @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import { info } from '@src/utils'; import './version-checking'; diff --git a/web/packages/framework/src/ui/commit/index.tsx b/web/packages/framework/src/ui/commit/index.tsx index 50a9f5aa3..7b3133c95 100755 --- a/web/packages/framework/src/ui/commit/index.tsx +++ b/web/packages/framework/src/ui/commit/index.tsx @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import React from 'react'; import { render } from 'react-dom'; import { debug, getOrCreateBodyContainer } from '@src/utils'; diff --git a/web/packages/framework/src/ui/commit/ui.tsx b/web/packages/framework/src/ui/commit/ui.tsx index e5302aa59..dc21951a6 100755 --- a/web/packages/framework/src/ui/commit/ui.tsx +++ b/web/packages/framework/src/ui/commit/ui.tsx @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import React, { useState, useEffect } from 'react'; import cn from 'classnames'; import { message } from 'coding-oa-uikit'; diff --git a/web/packages/framework/src/ui/development/dev-modal.tsx b/web/packages/framework/src/ui/development/dev-modal.tsx index 8307b0eca..d740d5fa4 100644 --- a/web/packages/framework/src/ui/development/dev-modal.tsx +++ b/web/packages/framework/src/ui/development/dev-modal.tsx @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import React, { useEffect, useState } from 'react'; import { unionBy } from 'lodash'; import { Modal, Button, Form, Input, Space } from 'coding-oa-uikit'; diff --git a/web/packages/framework/src/ui/development/index.tsx b/web/packages/framework/src/ui/development/index.tsx index 2152e042e..2260e918c 100644 --- a/web/packages/framework/src/ui/development/index.tsx +++ b/web/packages/framework/src/ui/development/index.tsx @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import React from 'react'; import ReactDom from 'react-dom'; diff --git a/web/packages/framework/src/ui/development/ui.tsx b/web/packages/framework/src/ui/development/ui.tsx index d0921db46..2019b84d4 100644 --- a/web/packages/framework/src/ui/development/ui.tsx +++ b/web/packages/framework/src/ui/development/ui.tsx @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import React, { useState } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import Cookies from 'universal-cookie'; @@ -25,7 +19,7 @@ const DevUI = () => { const onOK = (data: string) => { cookies.set(Constant.MICRO_FRONTEND_API_LIST, data, { path: '/', - domain: window.location.hostname + domain: window.location.hostname, }); setVisible(false); location.reload(); diff --git a/web/packages/framework/src/utils/index.ts b/web/packages/framework/src/utils/index.ts index 8db592252..21922c40f 100644 --- a/web/packages/framework/src/utils/index.ts +++ b/web/packages/framework/src/utils/index.ts @@ -1,9 +1,3 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import isEmpty from 'lodash/isEmpty'; import Constant from '@src/constant'; @@ -147,3 +141,16 @@ export const error = (message?: any, ...optionalParams: any[]) => { export const warn = (message?: any, ...optionalParams: any[]) => { console.warn(LOG_PREFIX + message, ...optionalParams); }; + +/** + * 判断字段值是否为true + * @param value 字段值 + * @param strict 是否严格模式,默认false,即如果value为true字符串也可 + * @returns 返回boolean + */ +export const isTrue = (value: any, strict = false) => { + if (typeof value === 'string' && !strict) { + return value.toLowerCase() === 'true'; + } + return value === true; +}; diff --git a/web/packages/framework/src/utils/use-interval.ts b/web/packages/framework/src/utils/use-interval.ts index 18ed95410..499e5132f 100644 --- a/web/packages/framework/src/utils/use-interval.ts +++ b/web/packages/framework/src/utils/use-interval.ts @@ -1,13 +1,9 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - // https://github.com/Hermanya/use-interval import { useEffect, useRef } from 'react'; -const noop = () => {}; +const noop = () => { + // ... +}; export function useInterval(callback: () => void, delay: number | null | false, immediate = false) { const savedCallback = useRef(noop); diff --git a/web/packages/framework/webpack.config.ts b/web/packages/framework/webpack.config.ts new file mode 100644 index 000000000..41d904199 --- /dev/null +++ b/web/packages/framework/webpack.config.ts @@ -0,0 +1,50 @@ +import { webpackConfig, Envs } from '@tencent/micro-frontend-webpack/src/index'; +import { merge } from 'webpack-merge'; + +/** + * 配置自定义环境变量 + * @returns 返回envs, runtimeEnvs + */ +const customEnvConfig = () => { + const envs: Envs = { + MICRO_VERSION_INTERVAL: process.env.MICRO_VERSION_INTERVAL, + MICRO_VERSION_ENABLED: process.env.MICRO_VERSION_ENABLED, + MICRO_FRONTEND_SETTING_API: process.env.MICRO_FRONTEND_SETTING_API, + MICRO_FRONTEND_API: process.env.MICRO_FRONTEND_API, + }; + const runtimeKeys = [ + 'MICRO_FRONTEND_API', + 'MICRO_FRONTEND_SETTING_API', + ]; + const runtimeEnvs: Envs = {}; + + runtimeKeys.forEach((key) => { + runtimeEnvs[key] = `__${key}__`; + }); + return { envs, runtimeEnvs }; +}; + +const { config } = webpackConfig({ + configWebpackOptions: { + enable: 'false', + }, + envConfig: customEnvConfig(), +}); + +export default merge(config, { + devServer: { + port: 4000, + proxy: { + '/static': { + ws: false, + target: 'https://tca.tencent.com', + changeOrigin: true, + }, + '/server': { + ws: false, + target: 'https://tca.tencent.com', + changeOrigin: true, + }, + }, + }, +}); diff --git a/web/packages/login/README.md b/web/packages/login/README.md index b5cdb4007..6e61723e2 100644 --- a/web/packages/login/README.md +++ b/web/packages/login/README.md @@ -2,6 +2,11 @@ 登录微前端。 +- 支持账号密码登录 + + +展望:未来需实现一码登录功能。 + ## 本地启动脚本 ```bash @@ -72,7 +77,6 @@ PUBLIC_PATH=http://127.0.0.1:5055/ yarn run dev | GIT_REVISION | git 版本号 | | PUBLIC_PATH | 资源路径前缀 | | PLATFORM_ENV | 平台类型 | -| ENABLE_MANAGE | 开启后台版 | | ENABLE_EXTERNALS | 开启排除额外依赖(用于具有全局依赖时) | | CONFIG_ENABLED | 开启config-webpack-plugin | | PRODUCT_NAME | 产品名称 | diff --git a/web/packages/login/global.d.ts b/web/packages/login/global.d.ts index 87768bbbf..d5cf927a7 100644 --- a/web/packages/login/global.d.ts +++ b/web/packages/login/global.d.ts @@ -1,57 +1 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - declare module '*.scss'; - -declare module 'react-load-script'; - -type Store = import('redux').Store; -type Reducer = import('redux').Reducer; - -interface LifeCycle { - bootstrap: (config: T) => void; - mount: (config: T) => void; - unmount: (config: T) => void; - update?: (config: T) => void; -} - -type RegisterMicroApp = (id: string, lifecycle: LifeCycle) => void; -type TInjectAsyncReducer = (name: string, reducer: Reducer, override?: boolean) => void; - -interface MicroApplicationProps { - name: string; - description: string; - match: string; - commitId?: string; - changeAt?: string; - css: string[]; - js: string[]; - prefix: string[] | string; -} - -interface MicroApplication { - props: MicroApplicationProps; - readonly loadStyle: () => Promise; - readonly loadScript: () => Promise; - readonly loadResources: () => Promise; - readonly removeStyle: () => Promise; - readonly path: () => RegExp; -} - -interface WindowMicroHook { - registerApp: RegisterMicroApp; - injectAsyncReducer: TInjectAsyncReducer; - store: Store; - meta: MicroApplication[]; -} - -interface Window { - microHook: WindowMicroHook; -} - -declare const PLATFORM_ENV: 'saas' | 'private' | 'oa'; - -declare const ENABLE_MANAGE: 'TRUE'; diff --git a/web/packages/login/i18next-scanner.config.js b/web/packages/login/i18next-scanner.config.js new file mode 100644 index 000000000..f0e136f4e --- /dev/null +++ b/web/packages/login/i18next-scanner.config.js @@ -0,0 +1,2 @@ +const { i18nScannerConfig } = require('@tencent/micro-frontend-shared/i18n/i18next-scanner.config'); +module.exports = i18nScannerConfig; diff --git a/web/packages/login/package.json b/web/packages/login/package.json index ad898014e..ae014ef05 100644 --- a/web/packages/login/package.json +++ b/web/packages/login/package.json @@ -2,70 +2,47 @@ "name": "login", "version": "1.0.0", "description": "login 登录微前端", + "private": true, "keywords": [ "login", "micro-frontend" ], + "homepage": "", "license": "MIT", + "main": "src/index.tsx", + "files": [ + "src" + ], "scripts": { - "dev": "NODE_ENV=development webpack server --config ./scripts/webpack.dev.js --progress --color", - "build": "NODE_ENV=production webpack --config ./scripts/webpack.prod.js --progress --color", - "analyzer": "webpack-bundle-analyzer --port 8888 ./dist/stats.json" + "dev": "PLATFORM_ENV=${PLATFORM_ENV:-open} PUBLIC_PATH=${PUBLIC_PATH:-http://127.0.0.1:5055/} PORT=${PORT:-5055} NODE_ENV=development webpack server --config ./webpack.config.ts --progress --color", + "build": "NODE_ENV=production webpack --config ./webpack.config.ts --progress --color", + "analyzer": "webpack-bundle-analyzer --port 8888 ./dist/stats.json", + "lint": "eslint --ext .js,.jsx,.ts,.tsx src/", + "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx src/ --fix", + "scanner": "i18next-scanner" }, "dependencies": { + "@tencent/micro-frontend-shared": "^1.0.0", "@types/lodash": "^4.14.175", + "@types/qrcode": "^1.4.2", "@types/qs": "^6.9.7", "@types/react": "^17.0.24", "@types/react-dom": "^17.0.9", "@types/react-redux": "^7.1.18", "@types/react-router-dom": "^5.3.1", "classnames": "^2.3.1", - "coding-oa-uikit": "^4.3.9", - "i18next": "^21.2.4", "lodash": "^4.17.21", - "moment": "2.29.4", + "qrcode": "^1.5.0", "qs": "^6.10.1", "react": "^17.0.2", "react-dom": "^17.0.2", - "react-i18next": "^11.12.0", - "react-load-script": "^0.0.6", + "react-i18next": "^11.17.3", "react-redux": "^7.2.5", "react-router-dom": "^5.3.0", + "tdesign-react": "0.36.4", "universal-cookie": "^4.0.4" }, "devDependencies": { - "@babel/core": "^7.15.5", - "@babel/plugin-proposal-class-properties": "^7.14.5", - "@babel/plugin-proposal-object-rest-spread": "^7.15.6", - "@babel/plugin-transform-runtime": "^7.15.0", - "@babel/preset-env": "^7.15.6", - "@babel/preset-react": "^7.14.5", - "@babel/preset-typescript": "^7.15.0", - "@hot-loader/react-dom": "^17.0.1", - "@types/friendly-errors-webpack-plugin": "^0.1.4", - "@types/mini-css-extract-plugin": "^2.3.0", - "@types/sass": "^1.16.1", - "assets-webpack-plugin": "^7.1.1", - "axios": "^0.22.0", - "babel-loader": "^8.2.2", - "clean-webpack-plugin": "^4.0.0", - "copy-webpack-plugin": "^9.0.1", - "css-loader": "^6.3.0", - "eslint-webpack-plugin": "^3.0.1", - "friendly-errors-webpack-plugin": "^1.7.0", - "html-webpack-plugin": "^5.3.2", - "mini-css-extract-plugin": "^2.3.0", - "postcss-loader": "^6.1.1", - "postcss-preset-env": "^6.7.0", - "react-hot-loader": "^4.13.0", - "sass": "^1.42.1", - "sass-loader": "^12.1.0", - "style-loader": "^3.3.0", - "webpack": "^5.54.0", - "webpack-bundle-analyzer": "^4.4.2", - "webpack-cli": "^4.8.0", - "webpack-dev-server": "^4.3.0", - "webpack-merge": "^5.8.0", - "webpackbar": "^5.0.0-3" + "@tencent/micro-frontend-webpack": "^1.0.0" } -} +} \ No newline at end of file diff --git a/web/packages/login/public/locales/en-US/translation.json b/web/packages/login/public/locales/en-US/translation.json new file mode 100644 index 000000000..01dc229f2 --- /dev/null +++ b/web/packages/login/public/locales/en-US/translation.json @@ -0,0 +1,10 @@ +{ + "语言切换中...": "语言切换中...", + "登录失败,账户或密码错误,或账户未注册": "登录失败,账户或密码错误,或账户未注册", + "请输入唯一用户名": "请输入唯一用户名", + "用户名": "用户名", + "请输入密码": "请输入密码", + "密码": "密码", + "登录": "登录", + "腾讯云代码分析账号密码登录": "腾讯云代码分析账号密码登录" +} diff --git a/web/packages/login/public/locales/zh-CN/translation.json b/web/packages/login/public/locales/zh-CN/translation.json new file mode 100644 index 000000000..01dc229f2 --- /dev/null +++ b/web/packages/login/public/locales/zh-CN/translation.json @@ -0,0 +1,10 @@ +{ + "语言切换中...": "语言切换中...", + "登录失败,账户或密码错误,或账户未注册": "登录失败,账户或密码错误,或账户未注册", + "请输入唯一用户名": "请输入唯一用户名", + "用户名": "用户名", + "请输入密码": "请输入密码", + "密码": "密码", + "登录": "登录", + "腾讯云代码分析账号密码登录": "腾讯云代码分析账号密码登录" +} diff --git a/web/packages/login/scripts/config-webpack-plugin.js b/web/packages/login/scripts/config-webpack-plugin.js deleted file mode 100644 index afc27e672..000000000 --- a/web/packages/login/scripts/config-webpack-plugin.js +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -const AssetsWebpackPlugin = require('assets-webpack-plugin'); -const merge = require('lodash/merge'); -const chalk = require('chalk'); - -const PLUGIN_NAME = 'ConfigWebpackPlugin'; -const PLUGIN_PREFIX = `[${PLUGIN_NAME}] `; - -function isTrue(value) { - return value === true || value === 'true'; -} - -class ConfigWebpackPlugin { - constructor(options) { - if (!options) { - options = {}; - } - const productName = this.getProductName(options); - const description = this.getDescription(options); - this.options = merge( - { - enable: process.env.CONFIG_ENABLED || true, // 默认启用 - productName, - description, - commitId: process.env.GIT_REVISION || '', - match: process.env.PRODUCT_ROUTE_MATCH, - prefix: process.env.PUBLIC_PATH || '/', - }, - options, - ); - this.isDev = (!!options.isDev) || process.env.NODE_ENV !== 'production'; - - // AssetsWebpackPlugin所需 - this.path = options.path; - this.filename = `${productName}.json`; - this.assetKeys = options.assetKeys || []; - - // 未启用 - if (!isTrue(this.options.enable)) { - console.info(chalk.yellow(PLUGIN_PREFIX) - + chalk.green('plugin is disabled, please use process.env.CONFIG_ENABLED enable it')); - return; - } - this.optionCheck(this.options); - } - - // 对启用该插件进行必要参数校验 - optionCheck(options) { - if (!options.productName) { - throw new Error(`${PLUGIN_PREFIX} 需要设置 productName。const productName = options?.productName || options?.pkgInfo?.name || process.env.PRODUCT_NAME;`); - } - - if (!options.path) { - throw new Error(`${PLUGIN_PREFIX} 需要设置 path。用于AssetsWebpackPlugin打包资源到path下`); - } - - if (!options.assetKeys) { - throw new Error(`${PLUGIN_PREFIX} 需要设置 assetKeys。用于资源获取。`); - } - - if (!options.match) { - throw new Error(`${PLUGIN_PREFIX} 需要设置 match。用于资源路由match`); - } - } - - // 获取应用名称 - getProductName(options) { - if (options) { - if (options.productName) { - return options.productName; - } - if (options.pkgInfo?.name) { - return options.pkgInfo.name; - } - } - return process.env.PRODUCT_NAME; - } - - // 获取应用描述 - getDescription(options) { - if (options?.pkgInfo?.description) { - return options.pkgInfo.description; - } - return process.env.PRODUCT_DESC || ''; - } - - apply(compiler) { - const plugins = [ - new AssetsWebpackPlugin({ - path: this.path, - filename: this.filename, - processOutput: this.processOutput.bind(this), - }), - ]; - - plugins.forEach((plugin) => { - plugin.apply(compiler); - }); - - // 在 compilation 完成时执行,dev环境将配置地址打印出来 - compiler.hooks.done.tap(PLUGIN_NAME, (stats) => { - // 用于dev,生成api.json url - if (this.isDev) { - const { https = false, host = 'localhost', port = 8080 } = stats.compilation.options.devServer; - const reg = new RegExp(/https?:\/\//); - const publicPath = this.options.prefix; - const prefix = reg.test(publicPath) ? publicPath : `http${https ? 's' : ''}://${host}:${port}${publicPath}`; - const api = `${prefix}${this.filename}`; - setTimeout(() => { - console.info(`${chalk.yellow('[dev环境]')}: ${chalk.green(`API = ${chalk.yellow(api)} , `)}`); - }, 20); - } - }); - } - - processOutput(assets) { - // 插件未启用 - if (!isTrue(this.options.enable)) { - console.info(chalk.yellow(PLUGIN_PREFIX) + chalk.green('插件未启用,跳过...')); - return ''; - } - - // 获取资源内的js和css - const js = this.assetKeys.map(k => assets[k]?.js).filter(v => !!v); - const css = this.assetKeys.map(k => assets[k]?.css).filter(v => !!v); - - // 生成config - const config = { - name: this.options.productName, - description: this.options.description, - commitId: this.options.commitId, - match: this.options.match, - js, - css, - prefix: [this.options.prefix], - }; - - console.log(chalk.yellow(PLUGIN_PREFIX) + chalk.yellow('[config]') + chalk.green(JSON.stringify(config))); - return this.options.processOutput ? this.options.processOutput(assets, config) : JSON.stringify(config); - } -} - -module.exports = ConfigWebpackPlugin; diff --git a/web/packages/login/scripts/webpack.common.js b/web/packages/login/scripts/webpack.common.js deleted file mode 100644 index 317eccc23..000000000 --- a/web/packages/login/scripts/webpack.common.js +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -const path = require('path'); -const webpack = require('webpack'); -const { CleanWebpackPlugin } = require('clean-webpack-plugin'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const EslintWebpackPlugin = require('eslint-webpack-plugin'); -// 日志优化 -const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin'); -const WebpackBar = require('webpackbar'); -const ConfigWebpackPlugin = require('./config-webpack-plugin'); - -const pkgInfo = require('../package.json'); - -// 根据NODE_ENV分别获取不同的config -const isDev = process.env.NODE_ENV === 'development'; -// 获取项目环境变量 -const { envs, runtimeEnvs } = require('./envs'); -// 打包目录路径 -const BASE_DIR = path.resolve(__dirname, '..'); -const buildPath = process.env.ENABLE_MANAGE === 'TRUE' ? path.resolve(BASE_DIR, 'dist-admin') : path.resolve(BASE_DIR, 'dist'); -// index.html路径 -const indexPath = path.resolve(BASE_DIR, 'public', 'index.html'); - -const htmlMinify = { - html5: true, // 根据HTML5规范解析输入 - collapseWhitespace: true, // 折叠空白区域 - preserveLineBreaks: false, - minifyCSS: true, // 压缩文内css - minifyJS: true, // 压缩文内js - removeComments: true, // 移除注释 -}; - -module.exports = { - entry: { - [pkgInfo.name]: path.resolve(BASE_DIR, 'src/index.tsx'), - }, - output: { - path: buildPath, - publicPath: process.env.PUBLIC_PATH || '/', - filename: `[name]${isDev ? '' : '-[chunkhash:8]'}.js`, - chunkFilename: `[name]${isDev ? '' : '-[chunkhash:8]'}.js`, - }, - devtool: isDev ? 'inline-source-map' : false, - target: 'web', - resolve: { - // 尝试按顺序解析这些后缀名 - extensions: ['.ts', '.tsx', '.js', '.jsx', '.svg'], - alias: { - '@src': path.resolve(BASE_DIR, 'src'), - 'react-dom': '@hot-loader/react-dom', - }, - modules: [BASE_DIR, 'node_modules'], - }, - module: { - rules: [ - { - test: /\.[jt]sx?$/i, - exclude: /node_modules/, - loader: 'babel-loader', - options: { - rootMode: 'upward', - }, - }, - // Images - { - test: /\.(?:ico|gif|png|jpg|jpeg)$/i, - type: 'asset/resource', - }, - // Fonts and SVGs - { - test: /\.(woff(2)?|eot|ttf|otf|svg|)$/i, - type: 'asset/inline', - }, - { - test: /\.css$/i, - use: [ - MiniCssExtractPlugin.loader, - 'css-loader', - ], - }, - { - test: /\.s[ac]ss$/i, - exclude: [path.resolve(BASE_DIR, 'public')], - use: [ - MiniCssExtractPlugin.loader, - // 将 CSS 转化成 CommonJS 模块 - { - loader: 'css-loader', - options: { - modules: { - localIdentName: '[local]-[hash:base64:10]', - exportLocalsConvention: 'camelCase', - }, - importLoaders: 2, - }, - }, - // 将 Sass 编译成 CSS - { - loader: 'sass-loader', - options: { - // Prefer `dart-sass` - implementation: require('sass'), - }, - }, - ], - }, - ], - }, - optimization: { - runtimeChunk: true, - splitChunks: { - chunks: 'all', - name: (module, chunks, cacheGroupKey) => { - const allChunksNames = chunks.map(item => item.name).join('~'); - return `${cacheGroupKey}~${allChunksNames}`; - }, - cacheGroups: { - vendors: { - test: /[\\/]node_modules[\\/]/, - chunks: 'all', - enforce: true, - }, - }, - }, - }, - plugins: [ - // 打包前移除/清理 打包目录 - new FriendlyErrorsWebpackPlugin(), - new CleanWebpackPlugin(), - new webpack.DefinePlugin({ - 'process.env': Object.keys(envs).reduce((e, key) => { - e[key] = JSON.stringify(envs[key]); - return e; - }, {}), - PLATFORM_ENV: JSON.stringify(process.env.PLATFORM_ENV), - ENABLE_MANAGE: JSON.stringify(process.env.ENABLE_MANAGE), - }), - new EslintWebpackPlugin({ - fix: true, - extensions: ['js', 'jsx', 'tsx', 'ts'], - }), - new MiniCssExtractPlugin({ - filename: `[name]${isDev ? '' : '-[contenthash:8]'}.css`, - chunkFilename: `[name]${isDev ? '' : '-[contenthash:8]'}.css`, - ignoreOrder: false, - }), - new HtmlWebpackPlugin({ - inject: true, - template: indexPath, - envs, - minify: htmlMinify, - }), - // 该配置生成一个 index.runtime.html 模板用于提供 index.html runtime 的能力 - new HtmlWebpackPlugin({ - inject: true, - template: indexPath, - filename: 'index.runtime.html', - minify: htmlMinify, - envs: { - ...envs, - ...runtimeEnvs, - }, - }), - new ConfigWebpackPlugin({ - pkgInfo, - isDev, - path: buildPath, - match: process.env.PRODUCT_ROUTE_MATCH || '^/login', - assetKeys: ['runtime~login', 'vendors~login', 'login'], - }), - // 忽略第三方包指定目录,让这些指定目录不要被打包进去,对moment操作参考:https://blog.csdn.net/qq_17175013/article/details/86845624 - new webpack.IgnorePlugin({ - resourceRegExp: /^\.\/locale$/, - contextRegExp: /moment$/, - }), - new WebpackBar(), - ], -}; diff --git a/web/packages/login/scripts/webpack.dev.js b/web/packages/login/scripts/webpack.dev.js deleted file mode 100644 index 071b4c43b..000000000 --- a/web/packages/login/scripts/webpack.dev.js +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -const path = require('path'); -const { merge } = require('webpack-merge'); -const common = require('./webpack.common.js'); -const host = process.env.HOST || '127.0.0.1'; -const port = process.env.PORT || 5055; -const webSocketURL = `ws://${host}:${port}/ws`; -module.exports = merge(common, { - devServer: { - static: path.join(__dirname, '../dist'), - hot: true, - liveReload: false, - allowedHosts: 'all', - host, - port, - client: { - webSocketURL, - }, - historyApiFallback: true, - compress: true, - devMiddleware: { - writeToDisk: true, - }, - headers: { - 'Access-Control-Allow-Origin': '*', - }, - }, -}); diff --git a/web/packages/login/scripts/webpack.prod.js b/web/packages/login/scripts/webpack.prod.js deleted file mode 100644 index c84bed2ab..000000000 --- a/web/packages/login/scripts/webpack.prod.js +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -const { merge } = require('webpack-merge'); -// 文件体积监控 -const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); - -const common = require('./webpack.common.js'); - -const externals = process.env.ENABLE_EXTERNALS === 'TRUE' ? { - react: 'React', - 'react-dom': 'ReactDOM', - 'react-redux': 'ReactRedux', - classnames: 'Classnames', - 'coding-oa-uikit': 'CodingOAUikit', - lodash: 'Lodash', -} : {}; - -module.exports = merge(common, { - performance: { - hints: false, - }, - plugins: [ - new BundleAnalyzerPlugin({ - analyzerMode: 'disabled', // 不启动展示打包报告的http服务器 - generateStatsFile: true, // 不打开网站,但是在dist生成stats.json文件 - }), - ], - externals, -}); diff --git a/web/packages/login/src/modules/login/assets/login.svg b/web/packages/login/src/assets/login.svg similarity index 100% rename from web/packages/login/src/modules/login/assets/login.svg rename to web/packages/login/src/assets/login.svg diff --git a/web/packages/login/src/modules/login/assets/login_bg.png b/web/packages/login/src/assets/login_bg.png similarity index 100% rename from web/packages/login/src/modules/login/assets/login_bg.png rename to web/packages/login/src/assets/login_bg.png diff --git a/web/packages/login/src/common/style/font.scss b/web/packages/login/src/common/style/font.scss deleted file mode 100644 index 3ac5e75fd..000000000 --- a/web/packages/login/src/common/style/font.scss +++ /dev/null @@ -1,27 +0,0 @@ -.fs-12 { - font-size: 12px; -} - -.fs-14 { - font-size: 14px; -} - -.fs-16 { - font-size: 16px; -} - -.fs-18 { - font-size: 18px; -} - -.fs-20 { - font-size: 20px; -} - -.fs-24 { - font-size: 24px; -} - -.fs-28 { - font-size: 28px; -} diff --git a/web/packages/login/src/common/style/index.scss b/web/packages/login/src/common/style/index.scss deleted file mode 100644 index ae617c338..000000000 --- a/web/packages/login/src/common/style/index.scss +++ /dev/null @@ -1,41 +0,0 @@ -$font-family: "PingFang SC", "Helvetica Neue", "Hiragino Sans GB", "Segoe UI", "Microsoft YaHei", "微软雅黑", sans-serif; -$font-size-base: 14px; -$line-height-base: 1.5; - -:global { - // 全局非模块化 CSS 在这里添加 - @import "./common"; - @import "./uikit.scss"; - - *, - *::before, - *::after { - box-sizing: border-box; - } - - body { - font-family: $font-family; - font-size: $font-size-base; - line-height: $line-height-base; - background: getColor(white, normal); - color: $text-color; - -webkit-font-smoothing: antialiased; - margin: 0; - } - - #container { - position: relative; - padding-top: 48px; - min-width: 1080px; - height: 100vh; - } - - .a-no-style, - .a-no-style:link, - .a-no-style:visited, - .a-no-style:hover, - .a-no-style:active { - text-decoration: none !important; - color: inherit !important; - } -} diff --git a/web/packages/login/src/constant/index.ts b/web/packages/login/src/constant/index.ts new file mode 100644 index 000000000..62b4836ed --- /dev/null +++ b/web/packages/login/src/constant/index.ts @@ -0,0 +1,3 @@ +export const UKEY = 'userinfo'; +export const ACCESS_TOEKN = 'accessToken'; +export const BIND_TYPE = 'bind'; diff --git a/web/packages/login/src/context/constant.ts b/web/packages/login/src/context/constant.ts deleted file mode 100644 index b29defea7..000000000 --- a/web/packages/login/src/context/constant.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - diff --git a/web/packages/login/src/context/store.tsx b/web/packages/login/src/context/store.tsx deleted file mode 100644 index 2c5535527..000000000 --- a/web/packages/login/src/context/store.tsx +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -import React, { createContext, useReducer, useContext } from 'react'; -import { noop } from 'lodash'; - -interface ActionProps { - type: string; - payload: any; -} - -const initialState = {}; - -const StateContext = createContext<{}>(initialState); -const DispatchContext = createContext(noop); - -const reducer = (state: {}, action: ActionProps) => { - switch (action.type) { - default: { - return state; - } - } -}; - -const StoreProvider = ({ children }: { children: any }) => { - const [state, dispatch] = useReducer(reducer, initialState); - - return ( - - {children} - - ); -}; - -const useStateStore = () => useContext(StateContext); -const useDispatchStore = () => useContext(DispatchContext); - -export { StoreProvider, useStateStore, useDispatchStore }; diff --git a/web/packages/login/src/i18n/i18next.ts b/web/packages/login/src/i18n/i18next.ts deleted file mode 100644 index 14f029bc1..000000000 --- a/web/packages/login/src/i18n/i18next.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -import i18next from 'i18next'; - -const t = i18next.t.bind(i18next); - -export { t }; - -export default i18next; diff --git a/web/packages/login/src/i18n/index.ts b/web/packages/login/src/i18n/index.ts deleted file mode 100644 index a74bce2d0..000000000 --- a/web/packages/login/src/i18n/index.ts +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -/* tslint:disable */ -// 请确保 i18n 初始化文件最先加载 -import i18next from 'i18next'; -import { initReactI18next } from 'react-i18next'; -import Cookie from 'universal-cookie'; - -const cookie = new Cookie(); -const language = cookie.get('language') || 'zh_CN'; - -let translation = {}; - -try { - // eslint-disable-next-line @typescript-eslint/no-require-imports - translation = require(`./locales/${language}/translation.json`); -} catch (e) { - translation = {}; -} - -const resources = { - zh_CN: { - translation, - }, - en_US: { - translation, - }, -}; - -i18next.use(initReactI18next).init({ - lng: 'zh_CN', - keySeparator: false, - nsSeparator: false, - interpolation: { - escapeValue: false, - }, - resources, -}); - -export const isEnglish = () => language === 'en_US'; - -export default i18next; diff --git a/web/packages/login/src/i18n/locales/en_US/translation.json b/web/packages/login/src/i18n/locales/en_US/translation.json deleted file mode 100644 index 9e26dfeeb..000000000 --- a/web/packages/login/src/i18n/locales/en_US/translation.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/web/packages/login/src/i18n/locales/zh_CN/translation.json b/web/packages/login/src/i18n/locales/zh_CN/translation.json deleted file mode 100644 index 9e26dfeeb..000000000 --- a/web/packages/login/src/i18n/locales/zh_CN/translation.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/web/packages/login/src/i18n/locales/zh_TW/translation.json b/web/packages/login/src/i18n/locales/zh_TW/translation.json deleted file mode 100644 index 9e26dfeeb..000000000 --- a/web/packages/login/src/i18n/locales/zh_TW/translation.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/web/packages/login/src/index.tsx b/web/packages/login/src/index.tsx index b1ad3ba4c..3e4de36fe 100644 --- a/web/packages/login/src/index.tsx +++ b/web/packages/login/src/index.tsx @@ -1,82 +1,17 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { Provider } from 'react-redux'; -import { createStore } from 'redux'; -import Cookie from 'universal-cookie'; -import { ConfigProvider } from 'coding-oa-uikit'; -import 'coding-oa-uikit/dist/coding-oa-uikit.css'; - -import '@src/common/style/index.scss'; -import reducer from '@src/reducer'; +import MicroInit from '@tencent/micro-frontend-shared/tdesign-component/micro-init'; +import initI18next from '@tencent/micro-frontend-shared/i18n'; +import { initReactI18next } from 'react-i18next'; -import './i18n'; +// 项目内 import Root from './root'; -import { StoreProvider } from './context/store'; - import pkg from '../package.json'; -const ID = 'container'; -const cookie = new Cookie(); -const language = cookie.get('language') || 'zh_CN'; - -let locale: any = ''; -try { - locale = require(`coding-oa-uikit/lib/locale/${language}.js`); -} catch (e) { - locale = require('coding-oa-uikit/lib/locale/zh_CN.js'); -} - -const create = (rootDom: HTMLElement) => { - const e = document.getElementById(ID); - if (rootDom && !e) { - const container = document.createElement('div'); - container.id = ID; - rootDom.appendChild(container); - } -}; - -const bootstrap = () => { }; - -const mount = (props: any) => { - const { rootDom, store } = props; - create(rootDom); - render( - - - (node ? node.parentNode : document.body)} - > - - - - , - document.getElementById(ID), - ); -}; - -const unmount = () => { - const e = document.getElementById(ID); - if (e) { - unmountComponentAtNode(e); - } -}; +// 初始化i18n +initI18next({ modules: [initReactI18next] }); -if (typeof window.microHook === 'object') { - window.microHook.registerApp(pkg.name, { - bootstrap, - mount, - unmount, - }); -} else { - const store = createStore(reducer); - mount({ store }); -} +MicroInit({ + id: 'container', + name: pkg.name, + container: , +}); diff --git a/web/packages/login/src/modules/index.tsx b/web/packages/login/src/modules/index.tsx new file mode 100644 index 000000000..229f0e572 --- /dev/null +++ b/web/packages/login/src/modules/index.tsx @@ -0,0 +1,36 @@ +import React, { useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Tabs } from 'tdesign-react'; + +// 项目内 +import { clearLoginLocalStorage } from '@src/utils'; +import s from '@src/style.scss'; +import { LoginTypeEnum } from '@plat/constant'; +import NormalSignin from './signin'; +import Language from './language'; + +const { TabPanel } = Tabs; + +const Login = () => { + const { t } = useTranslation(); + + useEffect(() => { + // 每次进来都清除storage + clearLoginLocalStorage(); + }, []); + + return ( +
+
+ + + + + +
+ +
+ ); +}; + +export default Login; diff --git a/web/packages/login/src/modules/language.tsx b/web/packages/login/src/modules/language.tsx new file mode 100644 index 000000000..fdb8bfc89 --- /dev/null +++ b/web/packages/login/src/modules/language.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import Cookies from 'universal-cookie'; +import { MessagePlugin, Dropdown, Button } from 'tdesign-react'; +import { InternetIcon, ChevronUpIcon } from 'tdesign-icons-react'; +// 项目内 +import s from '@src/style.scss'; + +const { DropdownMenu, DropdownItem } = Dropdown; + +const COOKIE_NAME = 'i18next'; +const DEFAULT_LANG = 'zh-CN'; +const LANG = [ + { key: 'zh-CN', name: '中文(简体)' }, + { key: 'zh-TW', name: '中文(繁体)' }, + { key: 'en-US', name: 'English(US)' }, +]; +const cookies = new Cookies(); + +const LanguageUI = () => { + const { t, i18n } = useTranslation(); + const lang = cookies.get(COOKIE_NAME) ?? DEFAULT_LANG; + + const onChange = (key: string) => { + i18n.changeLanguage(key); + MessagePlugin.loading(t('语言切换中...')); + const timer = setTimeout(() => { + clearTimeout(timer); + window.location.reload(); + }, 300); + }; + + const { name: langText = '' } = LANG.find(({ key }) => lang === key) || {}; + + return ( + + + + {LANG.map(item => ( + onChange(item.key)}> + {item.name} + + ))} + + + ); +}; + +export default LanguageUI; diff --git a/web/packages/login/src/modules/login/constants.ts b/web/packages/login/src/modules/login/constants.ts deleted file mode 100644 index ec431be62..000000000 --- a/web/packages/login/src/modules/login/constants.ts +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -export const UKEY = 'userinfo'; -export const ACCESS_TOEKN = 'accessToken'; -export const BIND_TYPE = 'bind'; diff --git a/web/packages/login/src/modules/login/index.tsx b/web/packages/login/src/modules/login/index.tsx deleted file mode 100644 index c9df5e659..000000000 --- a/web/packages/login/src/modules/login/index.tsx +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -/** - * 登录 - */ -import React, { useEffect } from 'react'; -import { Tabs, Form, Input, Button, message } from 'coding-oa-uikit'; - -import { useSelector } from 'react-redux'; -import { get } from 'lodash'; - -// 项目内 -import { t } from '@src/i18n/i18next'; -import Language from './language'; -import s from './style.scss'; -import { postPasswordInfo } from '@src/services/common'; -import { useQuery } from '@src/utils/hooks'; -import { clearLoginLocalStorage, loginSuccessHandle } from './utils'; -const { TabPane } = Tabs; - -const Login = () => { - const query = useQuery(); - const APP = useSelector((state: any) => state.APP); - const name = get(APP, 'enterprise.name', ''); - - useEffect(() => { - // 每次进来都清除storage - clearLoginLocalStorage(); - }, []); - - const onFinish = (values: any) => { - const { username, password } = values; - clearLoginLocalStorage(); - postPasswordInfo(username, password) - .then((result) => { - if (result.isRegister) { - const redirect = query.get('redirect_uri') || ''; - const token = get(result, 'access_token'); - loginSuccessHandle(redirect, token); - } else { - message.error(t('登录失败,账户或密码错误,或账户未注册')); - } - }) - .catch((e) => { - message.error(e.msg || t('登录失败,请重试')); - }); - }; - - return ( -
-
- - -
-
- - - - - - -
- -
-
-
-
-
-
- -
- ); -}; -export default Login; diff --git a/web/packages/login/src/modules/login/language.tsx b/web/packages/login/src/modules/login/language.tsx deleted file mode 100644 index 8983a18c3..000000000 --- a/web/packages/login/src/modules/login/language.tsx +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -import React, { useState } from 'react'; -import Cookies from 'universal-cookie'; -import { message, Menu, Dropdown, Button } from 'coding-oa-uikit'; - -import Globe from 'coding-oa-uikit/lib/icon/Globe'; -import AngleUp from 'coding-oa-uikit/lib/icon/AngleUp'; -import { t } from '@src/i18n/i18next'; -import s from './style.scss'; - -const COOKIE_NAME = 'language'; -const DEFAULT_LANG = 'zh_CN'; -const LANG = [ - { key: 'zh_CN', name: '中文(简体)' }, - { key: 'zh_TW', name: '中文(繁体)' }, - { key: 'en_US', name: 'English(US)' }, -]; -const cookies = new Cookies(); - -const LanguageUI = () => { - const [lang, setLang] = useState(cookies.get(COOKIE_NAME) || DEFAULT_LANG); - - const onChange = (key: string) => { - cookies.set(COOKIE_NAME, key); - setLang(key); - message.loading(t('语言切换中...')); - const timer = setTimeout(() => { - window.location.reload(); - clearTimeout(timer); - }, 300); - }; - - const { name: langText = '' } = LANG.find(({ key }) => lang === key) || {}; - - return ( - - {LANG.map(item => ( - onChange(item.key)}> - {item.name} - - ))} - - } - placement="topCenter" - > - - - ); -}; - -export default LanguageUI; diff --git a/web/packages/login/src/modules/login/utils.ts b/web/packages/login/src/modules/login/utils.ts deleted file mode 100644 index c45137750..000000000 --- a/web/packages/login/src/modules/login/utils.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -import { message } from 'coding-oa-uikit'; -import { t } from '@src/i18n/i18next'; - -// 项目内 -import { xssRedirectUri } from '@src/utils'; -import { UKEY, ACCESS_TOEKN } from './constants'; -/** - * 清空登录的storage - */ -export const clearLoginLocalStorage = () => { - localStorage.removeItem(UKEY); - localStorage.removeItem(ACCESS_TOEKN); -}; - - -/** - * 登录成功操作 - * @param redirect 重定向地址 - * @param token token - * @param msg 结果 - */ -export const loginSuccessHandle = (redirect: string, token: string, msg: string = t('登录成功')) => { - localStorage.setItem(ACCESS_TOEKN, token); - localStorage.removeItem(UKEY); - const timer = setTimeout(() => { - message.success(msg); - window.location.href = redirect ? xssRedirectUri(redirect) : '/'; - clearTimeout(timer); - }, 1000); -}; diff --git a/web/packages/login/src/modules/signin.tsx b/web/packages/login/src/modules/signin.tsx new file mode 100644 index 000000000..3411461ab --- /dev/null +++ b/web/packages/login/src/modules/signin.tsx @@ -0,0 +1,63 @@ +import React, { useRef } from 'react'; +import { useTranslation } from 'react-i18next'; +import { get } from 'lodash'; +import { Form, Input, Button, MessagePlugin, SubmitContext, FormInstanceFunctions } from 'tdesign-react'; +import { DesktopIcon, LockOnIcon } from 'tdesign-icons-react'; +import { getURLSearch } from '@tencent/micro-frontend-shared/util'; +// 项目内 +import { postPasswordInfo } from '@src/services/common'; +import { clearLoginLocalStorage, loginSuccessHandler } from '@src/utils'; +import s from '@src/style.scss'; + +const { FormItem } = Form; + +const NormalSignin = () => { + const formRef = useRef(null); + const { t } = useTranslation(); + + const onSubmit = (e: SubmitContext) => { + if (e.validateResult === true) { + const formValue = formRef.current?.getFieldsValue?.(true) || {}; + clearLoginLocalStorage(); + postPasswordInfo(formValue?.username, formValue?.password) + .then((result: any) => { + if (result.isRegister) { + const token = get(result, 'access_token'); + const { redirect_uri: redirectUri = '' } = getURLSearch(); + loginSuccessHandler(redirectUri, token); + } else { + MessagePlugin.error(t('登录失败,账户或密码错误,或账户未注册')); + } + }); + } + }; + + return ( +
+
+ + } placeholder={t('用户名')} /> + + + } clearable={true} placeholder={t('密码')} onEnter={() => { + formRef.current.submit(); + }} /> + + + + +
+
+ ); +}; + +export default NormalSignin; diff --git a/web/packages/login/src/plat/common/constant.ts b/web/packages/login/src/plat/common/constant.ts new file mode 100644 index 000000000..bc31cdb44 --- /dev/null +++ b/web/packages/login/src/plat/common/constant.ts @@ -0,0 +1,5 @@ +export const OPEN_PASSWORD_LOGIN = 'OPEN_PASSWORD_LOGIN'; + +export enum LoginTypeEnum { + NORMAL_SIGNIN = 'normalSignin', +} diff --git a/web/packages/login/src/plat/common/routes.ts b/web/packages/login/src/plat/common/routes.ts new file mode 100644 index 000000000..a5f819305 --- /dev/null +++ b/web/packages/login/src/plat/common/routes.ts @@ -0,0 +1,12 @@ +import { RouteProps } from 'react-router-dom'; + +// 项目内 +import Login from '@src/modules'; + +const ROUTERS: RouteProps[] = [{ + path: '/login', + exact: true, + component: Login, +}]; + +export default ROUTERS; diff --git a/web/packages/login/src/plat/open/constant.ts b/web/packages/login/src/plat/open/constant.ts new file mode 100644 index 000000000..c55bd4f16 --- /dev/null +++ b/web/packages/login/src/plat/open/constant.ts @@ -0,0 +1 @@ +export * from '@src/plat/common/constant'; diff --git a/web/packages/login/src/plat/open/routes.ts b/web/packages/login/src/plat/open/routes.ts new file mode 100644 index 000000000..010115919 --- /dev/null +++ b/web/packages/login/src/plat/open/routes.ts @@ -0,0 +1,2 @@ +import ROUTERS from '@src/plat/common/routes'; +export default ROUTERS; diff --git a/web/packages/login/src/reducer/index.ts b/web/packages/login/src/reducer/index.ts deleted file mode 100644 index e10cf7da6..000000000 --- a/web/packages/login/src/reducer/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -const initialState = {}; - -export interface ActionProps { - type: string; - payload: any; -} - -const reducer = (state = initialState, action: ActionProps) => { - switch (action.type) { - default: - return state; - } -}; -export default reducer; diff --git a/web/packages/login/src/root.tsx b/web/packages/login/src/root.tsx index 9c9e2a3e7..5a6a048ab 100644 --- a/web/packages/login/src/root.tsx +++ b/web/packages/login/src/root.tsx @@ -1,21 +1,15 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import React, { Suspense } from 'react'; import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; import { hot } from 'react-hot-loader/root'; -import Login from './modules/login'; - +// 项目内 +import routes from '@plat/routes'; const Root = () => ( - + {routes.map(item => )} diff --git a/web/packages/login/src/services/common.ts b/web/packages/login/src/services/common.ts index 22a192671..749010fab 100644 --- a/web/packages/login/src/services/common.ts +++ b/web/packages/login/src/services/common.ts @@ -1,18 +1,12 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - /** * 公共请求 */ -import { post } from './index'; +import { post } from '@tencent/micro-frontend-shared/util/fetch'; export const LOGIN_SERVER_API = '/server/credential/api/v3/login'; /** - * 获取账号密码登录谢谢 + * 获取账号密码登录信息 * @param username 用户名 * @param password 密码 */ diff --git a/web/packages/login/src/services/index.ts b/web/packages/login/src/services/index.ts index fd0abfe76..d0b932366 100644 --- a/web/packages/login/src/services/index.ts +++ b/web/packages/login/src/services/index.ts @@ -1,112 +1 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -import NProgress from 'nprogress'; -import { isEmpty } from 'lodash'; -import qs from 'qs'; -// import { message } from 'coding-oa-uikit'; - -import fetch from '../utils/fetch'; - -function jsonInterceptor(res: any) { - if (res.status === 204) { - return { code: 0 }; - } - - // 请求成功 - return Promise.resolve(res.json()); -} - -export function errorInterceptor(json: any) { - if (!json || JSON.stringify(json) === '{}') { - return Promise.reject(json); - } - // 请求成功 - if (json.code === 0) { - return Promise.resolve(json.data); - } - - // 否则请求失败,失败提示 - // message.error(json.msg); - return Promise.reject(json); -} - -function request(url: string, options?: any) { - NProgress.start(); - return fetch(url, { - ...options, - credentials: 'include', - }) - .then(jsonInterceptor) - .then(errorInterceptor) - .catch((err: any) => Promise.reject(err)) - .finally(() => { - NProgress.done(); - }); -} - -/** - * 用于自定义处理fetch结果的接口 - * @param url - * @param options - */ -export function customRequest(url: string, options?: any) { - return fetch(url, { - ...options, - credentials: 'include', - }).then(jsonInterceptor); -} - -export function get(url: string, data?: any) { - return request(`${url}${isEmpty(data) ? '' : `?${qs.stringify(data)}`}`, { - method: 'GET', - }); -} - -export function post(url: string, data: any, authorization: any = {}) { - return request(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...authorization, - }, - body: JSON.stringify(data), - }); -} - -export function put(url: string, data: any) { - return request(url, { - method: 'PUT', - body: JSON.stringify(data), - }); -} - -export function del(url: string, data?: any) { - return request(url, { - method: 'DELETE', - body: data && JSON.stringify(data), - }); -} - -export function getFilePost(url: string, data: any) { - return fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(data), - }).catch((err: any) => Promise.reject(err)); -} - -export function upload(url: string, data: any) { - return fetch(url, { - method: 'POST', - headers: {}, - body: data, - }) - .then(jsonInterceptor) - .catch((err: any) => Promise.reject(err)); -} +export * from './common'; diff --git a/web/packages/login/src/modules/login/style.scss b/web/packages/login/src/style.scss similarity index 65% rename from web/packages/login/src/modules/login/style.scss rename to web/packages/login/src/style.scss index 1c1abafc4..ba4a06282 100644 --- a/web/packages/login/src/modules/login/style.scss +++ b/web/packages/login/src/style.scss @@ -16,8 +16,14 @@ width: 400px; .body { min-height: 400px; + background-color: transparent; + + :global(.t-tabs__nav-scroll) { + justify-content: center; + } } } + .language-dropdown { position: absolute; bottom: 30px; @@ -25,12 +31,26 @@ text-align: center; transform: translateX(-50%); } + + .iconQQ { + width: 11px; + height: 12px; + background-image: url(./assets/login.svg); + background-position: 41.42% 83.33%; + background-size: 1636.3636363636363% 1050%; + background-repeat: no-repeat; + margin-right: 5px; + display: inline-block; + } } + .login-box { text-align: center; height: 300px; - margin-top: 16px; + margin: 16px 0px; + padding: 20px; } + .associate-items { .item { margin-bottom: 16px; @@ -45,6 +65,7 @@ } } } + .hidden { display: none; } diff --git a/web/packages/login/src/utils/fetch.ts b/web/packages/login/src/utils/fetch.ts deleted file mode 100644 index b2fda7c98..000000000 --- a/web/packages/login/src/utils/fetch.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -import Cookies from 'universal-cookie'; -import { get } from 'lodash'; - -const cookies = new Cookies(); - -export const CSRF_HEADER_NAME = 'X-XSRF-TOKEN'; -export const CSRF_COOKIE_NAME = 'XSRF-TOKEN'; - -export function appendXSRFTokenHeader(headers: object): object { - const xsrfToken = cookies.get(CSRF_COOKIE_NAME); - if (!xsrfToken) { - return headers; - } - return { - ...headers, - [CSRF_HEADER_NAME]: xsrfToken, - }; -} - -export default function fetch(...args: any) { - const [input, init] = args; - - const headers: any = { - 'Content-Type': get(init, 'headers.Content-Type', 'application/json'), - }; - - init.headers = appendXSRFTokenHeader(headers); - - return window.fetch(input, init); -} diff --git a/web/packages/login/src/utils/getRoutePath.ts b/web/packages/login/src/utils/getRoutePath.ts new file mode 100644 index 000000000..2f4273d82 --- /dev/null +++ b/web/packages/login/src/utils/getRoutePath.ts @@ -0,0 +1,4 @@ +/** + * 获取登录路由 + */ +export const getLoginPath = () => '/login'; diff --git a/web/packages/login/src/utils/hooks.ts b/web/packages/login/src/utils/hooks.ts deleted file mode 100644 index cfdd19202..000000000 --- a/web/packages/login/src/utils/hooks.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -import { useLocation } from 'react-router-dom'; - -/** - * 获取当前页面查询参数 hooks - */ -export const useQuery = () => new URLSearchParams(useLocation().search); diff --git a/web/packages/login/src/utils/index.ts b/web/packages/login/src/utils/index.ts index 599b73cd4..041a2079b 100644 --- a/web/packages/login/src/utils/index.ts +++ b/web/packages/login/src/utils/index.ts @@ -1,25 +1,28 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== +import { MessagePlugin } from 'tdesign-react'; +import { xssRedirectUri } from '@tencent/micro-frontend-shared/util'; +import { UKEY, ACCESS_TOEKN } from '@src/constant'; + +/** + * 清空登录的storage + */ +export const clearLoginLocalStorage = () => { + localStorage.removeItem(UKEY); + localStorage.removeItem(ACCESS_TOEKN); +}; /** - * 防止url调整漏洞,对回调地址进行校验 - * @param href 回调地址 - * @param validHostNames 域名白名单 - * @returns 回调链接 + * 登录成功操作 + * @param redirect 重定向地址 + * @param token token + * @param msg 结果 */ -export const xssRedirectUri = ( - redirectUri: string, - validHostNames: Array = [window.location.hostname], -) => { - const a = document.createElement('a'); - a.href = decodeURIComponent(redirectUri) || ''; - // 接下来对hostname进行域名白名单的判断 - if (validHostNames.includes(a.hostname)) { - return a.href; - } - return ''; +export const loginSuccessHandler = (redirect: string, token: string, msg = '登录成功') => { + localStorage.setItem(ACCESS_TOEKN, token); + localStorage.removeItem(UKEY); + const timer = setTimeout(() => { + MessagePlugin.success(msg); + window.location.href = redirect ? xssRedirectUri(redirect) : '/'; + clearTimeout(timer); + }, 1000); }; diff --git a/web/packages/login/tsconfig.json b/web/packages/login/tsconfig.json index dacaf4a17..23e4e59ff 100644 --- a/web/packages/login/tsconfig.json +++ b/web/packages/login/tsconfig.json @@ -1,11 +1,11 @@ { "extends": "../../tsconfig", "compilerOptions": { - "baseUrl": ".", // baseUrl用于设置解析非相对模块名称的基本目录,相对模块不会受到baseUrl的影响 - "paths": { // paths用于设置模块名到基于baseUrl的路径映射 + "baseUrl": ".", + "paths": { "@src/*": ["src/*"], + "@plat/*": ["src/plat/open/*"] } }, "include": ["src", "global.d.ts"], - "exclude": ["lib"] } \ No newline at end of file diff --git a/web/packages/login/webpack.config.ts b/web/packages/login/webpack.config.ts new file mode 100644 index 000000000..3dd96f762 --- /dev/null +++ b/web/packages/login/webpack.config.ts @@ -0,0 +1,14 @@ +import { webpackConfig } from '@tencent/micro-frontend-webpack/src/index'; +import { merge } from 'webpack-merge'; + +const { config } = webpackConfig({ + configWebpackOptions: { + match: '^/login', + }, +}); + +export default merge(config, { + devServer: { + + }, +}); diff --git a/web/packages/shared/README.md b/web/packages/shared/README.md index a0257c123..8e8cdae93 100644 --- a/web/packages/shared/README.md +++ b/web/packages/shared/README.md @@ -1,3 +1,11 @@ -# `@micro-frontend/shared` +# `@tencent/micro-frontend-shared` -> 公共模块 +> TODO: description + +## Usage + +``` +const shared = require('@tencent/micro-frontend-shared'); + +// TODO: DEMONSTRATE API +``` diff --git a/web/packages/shared/component/404/index.tsx b/web/packages/shared/component/404/index.tsx new file mode 100644 index 000000000..568747158 --- /dev/null +++ b/web/packages/shared/component/404/index.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { useHistory } from 'react-router-dom'; +import { Result, Button } from 'coding-oa-uikit'; +import { ResultProps } from 'coding-oa-uikit/lib/result'; + + +const Page404 = (props: Omit) => { + const history = useHistory(); + const { + style = { marginTop: 60 }, + title = '404', + subTitle = '抱歉,您访问的页面不存在,请确认路由是否准确', + extra = <> + + , + ...rest + } = props; + return ; +}; + +export default Page404; diff --git a/web/packages/shared/component/copy/index.tsx b/web/packages/shared/component/copy/index.tsx new file mode 100644 index 000000000..89877d2ce --- /dev/null +++ b/web/packages/shared/component/copy/index.tsx @@ -0,0 +1,46 @@ +/** + * 复制到剪切板 + */ + +import React from 'react'; +import cn from 'classnames'; +import CopyToClipboard from 'react-copy-to-clipboard'; +import Tooltip from 'coding-oa-uikit/lib/tooltip'; +import message from 'coding-oa-uikit/lib/message'; +import CopyIcon from 'coding-oa-uikit/lib/icon/Copy'; + +import s from './style.scss'; + +interface CopyProps { + /** tooltip 提示文字 */ + text: string; + /** copy 文字,不传默认为 text */ + copyText?: string; + /** 复制成功提示,不传默认为 “复制成功” */ + msg?: string; + /** copy 图标的自定义 class */ + className?: string; + /** copy 图标的自定义 style */ + style?: React.CSSProperties; + /** 弹框的渲染父节点 */ + getPopupContainer?: (triggerNode: HTMLElement) => HTMLElement +} + +const Copy = ({ text, copyText, msg, className, style, getPopupContainer }: CopyProps) => ( + + { + message.success(msg || '复制成功'); + }} + > + {/* tooltip 组件会默认替换第一个子节点类名 */} + + + + + +); + +export default Copy; + diff --git a/web/packages/shared/component/copy/style.scss b/web/packages/shared/component/copy/style.scss new file mode 100644 index 000000000..aac91c02d --- /dev/null +++ b/web/packages/shared/component/copy/style.scss @@ -0,0 +1,12 @@ +@import "../../style/color.scss"; + +.copy-icon { + color: $grey-6; + margin-left: 6px; + transition: color 0.3s; + cursor: pointer; + + &:hover { + color: $grey-8; + } +} diff --git a/web/packages/tca-manage/src/components/ellipsis/index.tsx b/web/packages/shared/component/ellipsis/index.tsx similarity index 100% rename from web/packages/tca-manage/src/components/ellipsis/index.tsx rename to web/packages/shared/component/ellipsis/index.tsx diff --git a/web/packages/shared/component/filter/FilterItem.tsx b/web/packages/shared/component/filter/FilterItem.tsx new file mode 100644 index 000000000..4a4d10d2e --- /dev/null +++ b/web/packages/shared/component/filter/FilterItem.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import Form, { FormItemProps } from 'coding-oa-uikit/lib/form'; + +const Item = (props: FormItemProps) => { + const { children, ...formItemProps } = props; + return ( + + {children} + + ); +}; + +export default Item; diff --git a/web/packages/shared/component/filter/index.tsx b/web/packages/shared/component/filter/index.tsx new file mode 100644 index 000000000..787bfefc4 --- /dev/null +++ b/web/packages/shared/component/filter/index.tsx @@ -0,0 +1,24 @@ +/** + * 过滤筛选组件 + */ + +import React from 'react'; +import Form, { FormProps } from 'coding-oa-uikit/lib/form'; + +import Item from './FilterItem'; + +const Filter = (props: FormProps) => { + const { children, layout = 'inline', ...formProps } = props; + return ( +
+ {children} +
+ ); +}; + +Filter.Item = Item; + +export default Filter; diff --git a/web/packages/shared/component/index.tsx b/web/packages/shared/component/index.tsx new file mode 100644 index 000000000..133aa7420 --- /dev/null +++ b/web/packages/shared/component/index.tsx @@ -0,0 +1 @@ +export * from './modal'; diff --git a/web/packages/shared/component/loading/index.tsx b/web/packages/shared/component/loading/index.tsx new file mode 100644 index 000000000..dff6d9eaa --- /dev/null +++ b/web/packages/shared/component/loading/index.tsx @@ -0,0 +1,18 @@ +/** + * 加载中组件 + */ + +import React from 'react'; +import LoadingIcon from 'coding-oa-uikit/lib/icon/Loading'; +import s from './style.scss'; + +interface LoadingProps { + msg?: string; +} + +const Loading = ({ msg = '加载中' }: LoadingProps) =>
+ + {msg} +
; + +export default Loading; diff --git a/web/packages/tca-manage/src/components/loading/style.scss b/web/packages/shared/component/loading/style.scss similarity index 100% rename from web/packages/tca-manage/src/components/loading/style.scss rename to web/packages/shared/component/loading/style.scss diff --git a/web/packages/shared/component/micro-init/index.tsx b/web/packages/shared/component/micro-init/index.tsx new file mode 100644 index 000000000..a603cc1a4 --- /dev/null +++ b/web/packages/shared/component/micro-init/index.tsx @@ -0,0 +1,144 @@ +import React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { legacy_createStore as createStore, combineReducers } from 'redux'; +import { Provider } from 'react-redux'; +import { ConfigProvider } from 'coding-oa-uikit'; +import Cookie from 'universal-cookie'; +import 'coding-oa-uikit/dist/coding-oa-uikit.css'; +import { StoreProvider } from '../../hook-store'; +import { Store, StateProps, ActionProps, Middleware } from '../../hook-store/types'; +import '../../style/index.scss'; + +/** + * 国际化资源加载 + * @returns + */ +const loadLocale = async () => { + const cookie = new Cookie(); + const language = (cookie.get('i18next') as string ?? 'zh-CN').replace('-', '_'); + try { + return await import(`coding-oa-uikit/lib/locale/${language}.js`); + } catch (error) { + return await import('coding-oa-uikit/lib/locale/zh_CN.js'); + } +}; + +interface MicroInitProps { + /** 微前端外层HTMLElement id */ + id: string; + /** 微前端唯一标识名称 */ + name: string; + /** 入口 */ + container: JSX.Element | React.ReactNode; + /** 微前端上层reducers */ + reducers?: InjectReducer; +} + +interface MicroHookStore { + /** react-hook store 状态管理 */ + hookStore?: { + enable: boolean; + stores: Store[]; + middlewares?: Middleware[]; + } +} + +/** + * 微前端初始化 + * @param microInitProps + */ +const MicroInit = ({ + id, name, container, reducers, hookStore, +}: MicroInitProps & MicroHookStore) => { + const bootstrap = () => { + // bootstrap + }; + + const create = (rootDom: HTMLElement) => { + if (rootDom) { + let wrapDom = document.getElementById(name); + if (!wrapDom) { + wrapDom = document.createElement('div'); + wrapDom.id = name; + wrapDom.className = `${name}-container`; + } + let containerDom = document.getElementById(id); + if (!containerDom) { + containerDom = document.createElement('div'); + containerDom.id = id; + containerDom.appendChild(wrapDom); + rootDom.appendChild(containerDom); + } else { + containerDom.appendChild(wrapDom); + } + } + }; + + const mount = async (props: ApplicationCustomProps) => { + const { rootDom, injectAsyncReducer, store } = props; + create(rootDom); + const locale = await loadLocale(); + let renderContent = (node ? node.parentNode as HTMLElement : document.body)} + > + {container} + ; + + if (hookStore?.enable) { + const { stores, middlewares } = hookStore; + renderContent = + {renderContent} + ; + } + + if (store) { + // 将reducers注入 + if (reducers && injectAsyncReducer) { + Object.keys(reducers).forEach(key => injectAsyncReducer(key, reducers[key])); + } + renderContent = + {renderContent} + ; + } + + render(renderContent, document.getElementById(name)); + }; + + const unmount = () => { + const e = document.getElementById(name); + if (e) { + unmountComponentAtNode(e); + e.remove(); + } + }; + + if (typeof window.microHook === 'object') { + window.microHook.registerApp(name, { + bootstrap, + mount, + unmount, + }); + } else { + const ASYNC_REDUCERS: InjectReducer = {}; + const { store, injectAsyncReducer } = (() => { + const rootReducers: InjectReducer = {}; + const store = createStore(combineReducers({ ...rootReducers })); + const injectAsyncReducer = (name: string, reducer: Reducer, override = false) => { + if (!(ASYNC_REDUCERS[name] && !override)) { + ASYNC_REDUCERS[name] = reducer; + store.replaceReducer(combineReducers({ ...rootReducers, ...ASYNC_REDUCERS })); + } + }; + return { store, injectAsyncReducer }; + })(); + mount({ + rootDom: document.body, + store, + injectAsyncReducer, + }); + } +}; + +export default MicroInit; diff --git a/web/packages/tca-manage/src/micro-layout.tsx b/web/packages/shared/component/micro-layout/index.tsx similarity index 72% rename from web/packages/tca-manage/src/micro-layout.tsx rename to web/packages/shared/component/micro-layout/index.tsx index 5210b8b3a..747db9677 100644 --- a/web/packages/tca-manage/src/micro-layout.tsx +++ b/web/packages/shared/component/micro-layout/index.tsx @@ -1,12 +1,12 @@ -import React, { ReactNode } from 'react'; +import React from 'react'; import { get } from 'lodash'; -import Loading from '@src/components/loading'; +import Loading from '../loading'; import { useSelector } from 'react-redux'; -interface IProps { - children: ReactNode; +interface MicroLayoutProps { + children: React.ReactNode; value?: string; - loading?: ReactNode; + loading?: React.ReactNode; disable?: boolean; } @@ -15,7 +15,7 @@ const MicroLayout = ({ value = 'completed', loading = , disable = false, -}: IProps) => { +}: MicroLayoutProps) => { const INITIAL = useSelector((state: any) => state.INITIAL); const completed = get(INITIAL, value, false); if (!completed && !disable) { diff --git a/web/packages/tca-manage/src/components/modal/danger-modal.tsx b/web/packages/shared/component/modal/danger-modal.tsx similarity index 64% rename from web/packages/tca-manage/src/components/modal/danger-modal.tsx rename to web/packages/shared/component/modal/danger-modal.tsx index 2d2df52b4..2e326c957 100644 --- a/web/packages/tca-manage/src/components/modal/danger-modal.tsx +++ b/web/packages/shared/component/modal/danger-modal.tsx @@ -1,24 +1,18 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - import React, { ReactNode } from 'react'; import { Modal, Button } from 'coding-oa-uikit'; -// 项目内 -import { t } from '@src/i18n/i18next'; interface IProps { title: string; visible: boolean; + onCancelText?: string; onCancel: (e: React.MouseEvent) => void; + onOkText?: string; onOk: (e: React.MouseEvent) => void; content?: ReactNode; } const DangerModal = (props: IProps) => { - const { visible, title, onCancel, onOk, content } = props; + const { visible, title, onCancel, onCancelText = '取消', onOk, onOkText = '确认', content } = props; return ( { onCancel={onCancel} footer={[ , , ]} > diff --git a/web/packages/shared/component/modal/index.tsx b/web/packages/shared/component/modal/index.tsx new file mode 100644 index 000000000..aeb9a6e34 --- /dev/null +++ b/web/packages/shared/component/modal/index.tsx @@ -0,0 +1 @@ +export { default as DangerModal } from './danger-modal'; diff --git a/web/packages/shared/component/search/index.tsx b/web/packages/shared/component/search/index.tsx new file mode 100644 index 000000000..7837954ff --- /dev/null +++ b/web/packages/shared/component/search/index.tsx @@ -0,0 +1,192 @@ +/** + * 过滤筛选组件 + */ +import React, { useState, useEffect } from 'react'; +import { useHistory } from 'react-router-dom'; +import { isEmpty, pick } from 'lodash'; +import classnames from 'classnames'; +import { Input, Form, Select, DatePicker, Button, Checkbox } from 'coding-oa-uikit'; +import AngleDown from 'coding-oa-uikit/lib/icon/AngleDown'; +import AngleUp from 'coding-oa-uikit/lib/icon/AngleUp'; + +import { getURLPathByFilterParams } from '../../util/route'; +import { FilterField, Filter as FilterParams } from '../../util/types'; +import Filter from '../filter'; +import s from './style.scss'; + +/** 判断是否存在不为空的筛选参数 */ +const isExistSearchParam = (params: FilterParams) => Object.keys(params) + .some((key: string) => typeof params[key] === 'number' || !isEmpty(params[key])); + +/** search 组件 表单类型 */ +export type SearchFormFieldType = 'input' | 'select' | 'datepicker' | 'checkbox' | 'multiselect'; + +/** search 组件表单字段类型 */ +export interface SearchFormField extends FilterField { + /** 表单类型 */ + formType: SearchFormFieldType; + /** 表单value */ + formValue?: string | number; + /** form item 名称 */ + label?: string; + /** 表单 placeholder */ + placeholder?: string; + /** select options,仅type为select时生效 */ + options?: any[] + /** 表单样式 */ + style?: React.CSSProperties +} + +interface SearchProps { + /** 一级筛选字段 */ + fields: SearchFormField[], + /** 高级筛选字段 */ + moreFields?: SearchFormField[], + /** 筛选参数,传入参数 */ + searchParams: FilterParams; + /** 筛选请求加载中 */ + loading: boolean; + /** 筛选回调 */ + callback?: (params: any) => void; + /** 是否开启路由跳转,默认开启 */ + route?: boolean; + /** 筛选组件class */ + className?: string; + /** 筛选组件style */ + style?: React.CSSProperties; + /** 首行额外内容 */ + extraContent?: React.ReactNode +} + +const Search = ({ + fields, moreFields, searchParams, loading, callback, + className, style, extraContent, route = true, +}: SearchProps) => { + const [form] = Form.useForm(); + const [more, setMore] = useState(false); + const history = useHistory(); + + useEffect(() => { + // loading 结束后reset form + if (!loading) { + form.resetFields(); + // 判断是否展开高级筛选 + if (moreFields) { + const fieldNames = moreFields.map(field => field.name); + if (isExistSearchParam(pick(searchParams, fieldNames))) { + setMore(true); + } + } + } + }, [loading]); + + // 筛选路由跳转处理,仅当route为true时生效 + const onRouteHandle = (params: FilterParams) => { + if (route) { + const url = getURLPathByFilterParams(moreFields ? fields.concat(moreFields) : fields, params); + history.push(url); + } + }; + + const onChange = (key: string, value: any) => { + const params = { + ...searchParams, + [key]: value, + }; + onRouteHandle(params); + callback?.(params); + }; + + const onClear = () => { + const params: FilterParams = {}; + // 将筛选入参清空 + Object.keys(searchParams).forEach((key) => { + params[key] = ''; + }); + onRouteHandle(params); + callback?.(params); + }; + + const getItem = (field: SearchFormField) => { + switch (field.formType) { + case 'input': + return onChange(field.name, value)} + />; + case 'select': + return onChange(field.name, value)} />; + case 'datepicker': + return onChange(field.name, date ? date.format('YYYY-MM-DD HH:mm:ss') : '')} + />; + case 'checkbox': + return onChange(field.name, e.target.checked ? field.formValue : '')}>{field.placeholder}; + default: + throw new Error('field type error'); + } + }; + + return
+
+ + {fields.map(field => {field.label} : ''} + name={field.name} + valuePropName={field.formType === 'checkbox' ? 'checked' : 'value'}> + {getItem(field)} + )} + {moreFields + && + } + { + isExistSearchParam(searchParams) && ( + + ) + } + + {extraContent} +
+ { + moreFields && more && ( + + {moreFields.map(field => {field.label} : ''} name={field.name} > + {getItem(field)} + )} + + ) + } +
; +}; + +export default Search; + diff --git a/web/packages/shared/component/search/style.scss b/web/packages/shared/component/search/style.scss new file mode 100644 index 000000000..cbb3eeb64 --- /dev/null +++ b/web/packages/shared/component/search/style.scss @@ -0,0 +1,27 @@ +.search { + padding: 1px 24px; + // background-color: #fcfcfa; + background-color: hsla(0, 0%, 98.8%, 0.98); + box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.1), 0 0.5px 0 0 rgba(0, 0, 0, 0.08); + + .search-first { + display: flex; + align-items: center; + justify-content: space-between; + } + // display: flex; + // align-items: center; + // justify-content: space-between; + // height: $filter-height; + // padding: 0 30px; + // background-color: hsla(0, 0%, 98.8%, 0.98); + // box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.1), 0 0.5px 0 0 rgba(0, 0, 0, 0.08); + + .search-content { + margin: 5px 0; + + :global(.ant-picker) { + padding: 3px 10px 3px; + } + } +} diff --git a/web/packages/shared/component/table/index.tsx b/web/packages/shared/component/table/index.tsx new file mode 100644 index 000000000..c17f25500 --- /dev/null +++ b/web/packages/shared/component/table/index.tsx @@ -0,0 +1,36 @@ +/** + * table组件,默认设置了分页器,以及分页change + */ +import React from 'react'; +import { useHistory } from 'react-router-dom'; +import Table, { TableProps, TablePaginationConfig } from 'coding-oa-uikit/lib/table'; +import { getPaginationParams, getURLPathByFilterParams } from '@tencent/micro-frontend-shared/util'; +import { FilterField } from '@tencent/micro-frontend-shared/util/types'; +import s from './style.scss'; +interface RouteTableProps extends TableProps { + pagination: TablePaginationConfig; + filterFields?: FilterField[] +} + +const RouteTable = (props: RouteTableProps) => { + const { pagination, filterFields = [], ...otherProps } = props; + const history = useHistory(); + const onChange = (page: number, pageSize?: number) => { + if (!pageSize) { + throw new Error('pageSize is undefined'); + } + const url = getURLPathByFilterParams(filterFields, getPaginationParams(page, pageSize)); + history.push(url); + }; + return ( + `${range[0]} - ${range[1]} 条数据,共 ${total} 条`, + onChange, + ...pagination, + }} rowKey={(item: any) => item.id} {...otherProps} /> + ); +}; + +RouteTable.Column = Table.Column; + +export default RouteTable; diff --git a/web/packages/shared/component/table/style.scss b/web/packages/shared/component/table/style.scss new file mode 100644 index 000000000..f85873dcf --- /dev/null +++ b/web/packages/shared/component/table/style.scss @@ -0,0 +1,5 @@ +.route-table { + :global(.ant-table-thead > tr > th) { + white-space: nowrap; + } +} diff --git a/web/packages/shared/component/user-avatar/index.tsx b/web/packages/shared/component/user-avatar/index.tsx new file mode 100644 index 000000000..7198dd7f1 --- /dev/null +++ b/web/packages/shared/component/user-avatar/index.tsx @@ -0,0 +1,64 @@ +import React, { useMemo } from 'react'; +import { Avatar } from 'coding-oa-uikit'; +import { AvatarProps } from 'coding-oa-uikit/lib/avatar'; + +import UserIcon from 'coding-oa-uikit/lib/icon/User'; + +interface UserAvatarProps extends AvatarProps { + /** 头像昵称 */ + nickname: string; + /** 头像地址 */ + url?: string; + /** 仅展示头像 */ + onlyAvatar?: boolean; + /** 展示头像icon */ + showIcon?: boolean; +} + +const COLOR_LIST = [ + { + color: '#d46b08', + backgroundColor: '#ffe7ba', + }, + { + color: '#d48806', + backgroundColor: '#fff1b8', + }, + { + color: '#7cb305', + backgroundColor: '#f4ffb8', + }, + { + color: '#096dd9', + backgroundColor: '#bae7ff', + }, + { + color: '#c41d7f', + backgroundColor: '#ffd6e7', + }, + { + color: '#531dab', + backgroundColor: '#efdbff', + }, +]; + +const UserAvatar = ({ url, nickname, onlyAvatar = false, showIcon = false, ...avatarProps }: UserAvatarProps) => { + const avatarRender = useMemo(() => { + if (url) { + return ; + } + if (nickname) { + const colorStyle = COLOR_LIST[nickname.toString().charCodeAt(0) % COLOR_LIST.length]; + return } + style={{ ...avatarProps.style, ...colorStyle }} > + {nickname.toString()[0].toUpperCase()} + ; + } + return ''; + }, [nickname, url]); + + return <> + {avatarRender} {!onlyAvatar && nickname && {nickname}} + ; +}; +export default UserAvatar; diff --git a/web/packages/shared/global.d.ts b/web/packages/shared/global.d.ts new file mode 100644 index 000000000..99294520b --- /dev/null +++ b/web/packages/shared/global.d.ts @@ -0,0 +1,54 @@ +declare module '*.scss'; +type Store = import('redux').Store; +type Reducer = import('redux').Reducer; + +interface LifeCycle { + bootstrap: (config: T) => void; + mount: (config: T) => Promise | void; + unmount: (config: T) => void; + update?: (config: T) => void; +} + +type RegisterMicroApp = (id: string, lifecycle: LifeCycle) => void; +interface InjectReducer { + [key: string]: Reducer; +} + +type InjectAsyncReducer = (name: string, reducer: Reducer, override?: boolean) => void; + +interface ApplicationCustomProps { + rootDom: HTMLElement; + store: Store; + injectAsyncReducer?: InjectAsyncReducer; +} + +interface MicroApplicationProps { + name: string; + description: string; + match: string; + commitId?: string; + changeAt?: string; + css: string[]; + js: string[]; + prefix: string[] | string; +} + +interface MicroApplication { + props: MicroApplicationProps; + readonly loadStyle: () => Promise; + readonly loadScript: () => Promise; + readonly loadResources: () => Promise; + readonly removeStyle: () => Promise; + readonly path: () => RegExp; +} + +interface WindowMicroHook { + registerApp: RegisterMicroApp; + injectAsyncReducer: InjectAsyncReducer; + store: Store; + meta: MicroApplication[]; +} + +interface Window { + microHook: WindowMicroHook; +} diff --git a/web/packages/shared/hook-store/index.tsx b/web/packages/shared/hook-store/index.tsx new file mode 100644 index 000000000..0db5755fe --- /dev/null +++ b/web/packages/shared/hook-store/index.tsx @@ -0,0 +1,48 @@ +/** + * react hooks store + * 来自 https://github.com/hangyangws/hooks-store + */ +import React, { Context, useMemo, createContext, Dispatch, useContext, useReducer } from 'react'; + +import { StateProps, ProviderProps, DispatchContextType } from './types'; +import { getInitialState, getComdbinedReducer } from './utils'; +import { applyMiddleware, genarateDefaultMiddlewares } from './middleware'; + +let StateContext: Context; +let DispatchContext: DispatchContextType; + +export const StoreProvider = (props: ProviderProps) => { + const initialState: StateProps = useMemo( + () => getInitialState(props.stores), + [props.stores], + ); + + StateContext = useMemo(() => createContext(initialState), [initialState]); + + DispatchContext = useMemo(() => createContext>(() => 0), []); + + const combinedReducer = useMemo(() => getComdbinedReducer(props.stores), [props.stores]); + + const [state, dispatch] = useReducer(combinedReducer, initialState); + + // 让 dispatch 支持 middlewares + const defaultMiddlewares = genarateDefaultMiddlewares(); + const middlewares = defaultMiddlewares.concat(props.middlewares ? props.middlewares : []); + const enhancedDispatch = middlewares?.length + ? applyMiddleware(state, dispatch, middlewares) + : dispatch; + + return ( + + {props.children} + + ); +}; + +export const useDispatchStore = () => useContext(DispatchContext as DispatchContextType); + +export const useStateStore = (nameSpace?: string) => { + const store = useContext(StateContext); + const state: State = nameSpace ? store[nameSpace] : store; + return state; +}; diff --git a/web/packages/shared/hook-store/middleware.ts b/web/packages/shared/hook-store/middleware.ts new file mode 100644 index 000000000..d3365c4f6 --- /dev/null +++ b/web/packages/shared/hook-store/middleware.ts @@ -0,0 +1,42 @@ +import React from 'react'; + +import { StateProps, Middleware } from './types'; + +export const applyMiddleware = ( + state: StateProps, + dispatch: React.Dispatch, + middlewares: Middleware[], +) => { + // in the same middleware three should be the shame next function + // so there must cache the index of current + const executeMiddleware = (index: number) => (action: Action) => { + if (index >= middlewares.length) { + dispatch(action); + return; + } + + middlewares[index]({ next: executeMiddleware(index + 1), action, state }); + }; + + return (action: Action) => { + middlewares[0]({ next: executeMiddleware(1), action, state }); + }; +}; + +/** 生成默认的配置的中间件 */ +export const genarateDefaultMiddlewares = () => { + const actionLog: Middleware = ({ next, action, state }) => { + const log = { + action, + state, + }; + + console.table(log); + + next(action); + }; + if (process.env.NODE_ENV === 'development') { + return [actionLog]; + } + return []; +}; diff --git a/web/packages/shared/hook-store/types.ts b/web/packages/shared/hook-store/types.ts new file mode 100644 index 000000000..f8ee6d404 --- /dev/null +++ b/web/packages/shared/hook-store/types.ts @@ -0,0 +1,39 @@ +import React from 'react'; + +export interface StateProps { + [name: string]: any; +} + +export interface ActionProps { + type: string; + [name: string]: any +} + +export type DispatchContextType = React.Context>; + +export type Middleware = ({ + next, + action, + state, +}: { + next: React.Dispatch; + action: Action; + state?: StateProps; +}) => void; + +export type Reducer = ( + state: State, + action: Action +) => State | undefined; + +export interface Store { + name: string; + initialState: State; + reducer: Reducer; +} + +export interface ProviderProps { + stores: Store[]; + middlewares?: Middleware[]; + children: JSX.Element[] | JSX.Element | React.ReactNode; +} diff --git a/web/packages/shared/hook-store/utils.ts b/web/packages/shared/hook-store/utils.ts new file mode 100644 index 000000000..b1d75ea79 --- /dev/null +++ b/web/packages/shared/hook-store/utils.ts @@ -0,0 +1,38 @@ +import { StateProps, Store } from './types'; + +export const getInitialState = (stores: Store[]): StateProps => stores.reduce( + (pre, cur) => ({ + ...pre, + [cur.name]: cur.initialState, + }), + {}, +); + +const isStateChanged = (current: State, next: State) => { + // nextCurrentState 为 null 或者 undefined 表示没有进行数据处理 + if (next === null) { + return false; + } + + return current !== next; +}; + +export const getComdbinedReducer = ( + stores: Store[]) => (state: StateProps, action: Action): StateProps => { + let index = 0; + while (index < stores.length) { + const currentStore = stores[index]; + const currentState = state[currentStore.name]; + const nextCurrentState = currentStore.reducer(currentState, action); + index += 1; + + if (isStateChanged(currentState, nextCurrentState)) { + return { + ...state, + [currentStore.name]: nextCurrentState, + }; + } + } + + return state; + }; diff --git a/web/packages/shared/hooks/index.ts b/web/packages/shared/hooks/index.ts new file mode 100644 index 000000000..52bb2b528 --- /dev/null +++ b/web/packages/shared/hooks/index.ts @@ -0,0 +1,4 @@ +export { default as useDeepEffect } from './useDeepEffect'; +export { default as useURLParams } from './useURLParams'; +export { default as useURLSearch } from './useURLSearch'; +export { default as useFetch } from './useFetch'; diff --git a/web/packages/shared/hooks/useDeepEffect.ts b/web/packages/shared/hooks/useDeepEffect.ts new file mode 100644 index 000000000..56653eae1 --- /dev/null +++ b/web/packages/shared/hooks/useDeepEffect.ts @@ -0,0 +1,21 @@ +/** + * shared hooks - useDeepEffect + */ +import React, { useEffect, useRef } from 'react'; +import { isEqual } from 'lodash'; + +const useDeepEffect = (fn: () => void, deps: React.DependencyList) => { + const isFirst = useRef(true); + const preDeps = useRef(deps); + useEffect(() => { + const isFirstEffect = isFirst.current; + const isSame = isEqual(preDeps.current, deps); + isFirst.current = false; + preDeps.current = deps; + if (isFirstEffect || !isSame) { + fn(); + } + }, deps); +}; + +export default useDeepEffect; diff --git a/web/packages/shared/hooks/useFetch.ts b/web/packages/shared/hooks/useFetch.ts new file mode 100644 index 000000000..247a431d4 --- /dev/null +++ b/web/packages/shared/hooks/useFetch.ts @@ -0,0 +1,132 @@ +/** + * shared hooks - useFetch + */ +import { useState, useReducer } from 'react'; +import useDeepEffect from './useDeepEffect'; + +type Action = { + type: 'FETCH_INIT' | 'FETCH_SUCCESS' | 'FETCH_FAILURE' | 'FETCH_CANCEL', + payload?: any +}; + +type State = { + isLoading: boolean, + isError: boolean, + error: string + data: any, +}; + +const fetchReducer = (state: State, action: Action) => { + switch (action.type) { + case 'FETCH_INIT': + return { + ...state, + isLoading: true, + isError: false, + }; + case 'FETCH_SUCCESS': + return { + ...state, + isLoading: false, + isError: false, + data: action.payload, + }; + case 'FETCH_FAILURE': + return { + ...state, + isLoading: false, + isError: true, + error: action.payload, + }; + case 'FETCH_CANCEL': + return { + ...state, + isLoading: false, + isError: false, + }; + default: + throw new Error(); + } +}; + +/** FetchApi 类型 */ +type FetchApi = (...args: ApiArgs) => Promise; + +/** UseFetchOptions useFetch额外的options */ +export interface UseFetchOptions { + /** 初始化数据,默认为 null */ + initData?: any + /** 前置处理 */ + onPreHandler?: () => void + /** 成功的回调 */ + onSuccess?: (data: any) => void + /** 失败的回调 */ + onFail?: (error: any) => void + /** 最终的回调 */ + onFinnaly?: () => void +} + +/** + * useFetch + * @param fetchApi api请求 + * @param apiArgs 请求参数 + * @param options useFetch 额外参数 + * @returns [state, reload] + */ +const useFetch = ( + fetchApi: FetchApi, + apiArgs: ApiArgs, + options: UseFetchOptions = {}, +): [State, () => void] => { + const { initData = null, onPreHandler, onSuccess, onFail, onFinnaly } = options; + const [state, dispatch] = useReducer(fetchReducer, { + isLoading: false, + isError: false, + error: '', + data: initData, + }); + + // 用于刷新请求 + const [refresh, setRefresh] = useState(false); + const reload = () => setRefresh(!refresh); + + useDeepEffect(() => { + // 组件销毁时 abort 数据 + let didCancel = false; + const fetchData = async () => { + // 当请求中时,避免多次请求 + if (state.isLoading) return; + // 前置处理 + onPreHandler?.(); + // fetch init + dispatch({ type: 'FETCH_INIT' }); + try { + const res = await fetchApi(...apiArgs); + // 当组件销毁时,将isLoading,isError置为false + if (didCancel) { + dispatch({ type: 'FETCH_CANCEL' }); + return; + } + dispatch({ type: 'FETCH_SUCCESS', payload: res }); + onSuccess?.(res); + } catch (error) { + if (didCancel) { + dispatch({ type: 'FETCH_CANCEL' }); + return; + } + dispatch({ type: 'FETCH_FAILURE', payload: error }); + onFail?.(error); + } finally { + onFinnaly?.(); + } + }; + fetchData(); + return () => { + didCancel = true; + }; + }, [apiArgs, refresh]); + + return [state, reload]; +}; + +export default useFetch; diff --git a/web/packages/shared/hooks/useURLParams.ts b/web/packages/shared/hooks/useURLParams.ts new file mode 100644 index 000000000..9c914630d --- /dev/null +++ b/web/packages/shared/hooks/useURLParams.ts @@ -0,0 +1,41 @@ +/** + * shared hooks - useURLParams 获取url参数hooks + */ +import { useMemo } from 'react'; + +import { getFilterFieldByURLSearch } from '../util/route'; +import { FilterField } from '../util/types'; + +/** + * 根据筛选字段列表获取路由筛选相关信息数据 + * @param filterFields 筛选字段列表 + * @returns + */ +const getURLParams = (filterFields: FilterField[]) => { + const filter = getFilterFieldByURLSearch(filterFields); + const { limit, offset, ordering, ...searchParams } = filter; + const pageSize = limit as number; + const pageStart = offset as number; + const currentPage = Math.floor(pageStart / pageSize) + 1; + return { + /** 全部筛选项,筛选参数+分页参数+排序参数 */ + filter, + /** 当前页码 */ + currentPage, + /** 当前每页数量 */ + pageSize, + /** 当前偏移数量 */ + pageStart, + /** 筛选参数 */ + searchParams, + /** 排序参数 */ + ordering, + }; +}; + +/** 根据路由变化,获取参数 */ +const useURLParams = (filterFields: FilterField[] = []) => useMemo(() => getURLParams(filterFields), [ + window.location.search, +]); + +export default useURLParams; diff --git a/web/packages/shared/hooks/useURLSearch.ts b/web/packages/shared/hooks/useURLSearch.ts new file mode 100644 index 000000000..d6d70081d --- /dev/null +++ b/web/packages/shared/hooks/useURLSearch.ts @@ -0,0 +1,13 @@ +/** + * shared hooks - useURLSearch 获取url参数hooks + */ +import { useMemo } from 'react'; + +import { getURLSearch } from '../util/route'; + +/** 根据路由变化,获取参数 */ +const useURLSearch = () => useMemo(() => getURLSearch(), [ + window.location.search, +]); + +export default useURLSearch; diff --git a/web/packages/shared/i18n/i18next-scanner.config.js b/web/packages/shared/i18n/i18next-scanner.config.js new file mode 100644 index 000000000..789d66585 --- /dev/null +++ b/web/packages/shared/i18n/i18next-scanner.config.js @@ -0,0 +1,82 @@ +const fs = require('fs'); +const chalk = require('chalk'); +const path = require('path'); +const typescript = require('typescript'); +const { merge } = require('lodash'); + +/** + * i18next-scanner配置 + * @param {*} options 默认{}, 可传递i18next-scanner参数调整默认配置 + * @returns i18next-scanner配置 + */ +const i18nScannerConfigFunc = (options = {}) => merge({ + input: [ + 'src/**/*.{js,jsx,ts,tsx}', + '!src/**/*.spec.{js,jsx,ts,tsx}', + '!src/**/*.d.ts', + '!**/node_modules/**', + ], + output: './', + options: { + debug: true, + func: { + list: ['i18next.t', 'i18n.t', 't'], + extensions: [], + }, + trans: { + component: 'Trans', + i18nKey: 'i18nKey', + extensions: [], + fallbackKey(ns, value) { + return value; + }, + acorn: { + ecmaVersion: 2020, + sourceType: 'module', // defaults to 'module' + // Check out https://github.com/acornjs/acorn/tree/master/acorn#interface for additional options + }, + }, + lngs: ['zh-CN', 'en-US'], + resource: { + loadPath: 'public/locales/{{lng}}/{{ns}}.json', + savePath: 'public/locales/{{lng}}/{{ns}}.json', + jsonIndent: 2, + lineEnding: '\n', + }, + defaultValue: '', + nsSeparator: false, // namespace separator + keySeparator: false, // key separator + removeUnusedKeys: true, + }, + transform: function customTransform(file, enc, done) { + 'use strict'; + const { parser } = this; + const content = fs.readFileSync(file.path, enc); + const { outputText } = typescript.transpileModule(content, { + compilerOptions: { target: typescript.ScriptTarget.ES2021 }, + fileName: path.basename(file.path), + }); + + // Parse Translation Function + let count = 0; + parser.parseFuncFromString(outputText, (key) => { + parser.set(key, key); + count += 1; + }); + if (count) { + console.log(`${chalk.blue('i18next-scanner:')} ${file.path}\n`); + } + + // Parse Trans component + parser.parseTransFromString(outputText); + + done(); + }, +}, options); + +const i18nScannerConfig = i18nScannerConfigFunc(); + +module.exports = { + i18nScannerConfigFunc, + i18nScannerConfig, +}; diff --git a/web/packages/shared/i18n/index.ts b/web/packages/shared/i18n/index.ts new file mode 100644 index 000000000..973618ce0 --- /dev/null +++ b/web/packages/shared/i18n/index.ts @@ -0,0 +1,47 @@ +import i18n, { InitOptions } from 'i18next'; +import Backend from 'i18next-http-backend'; +import LanguageDetector from 'i18next-browser-languagedetector'; +import Log from '../util/log'; + +export interface InitI18next { + /** init i18n参数 */ + options?: InitOptions + /** use i18n模块 */ + modules?: any[] +} + +/** + * 初始化i18n + * @param param0 {options, modules} 参数配置 + * @returns 初始化i18n + * + * 注:默认modules已使用i18next-http-backend, i18next-browser-languagedetector + * react使用i18n时,需要将react-i18next的initReactI18next传递至modules中, + */ +const initI18next = ({ options, modules = [] }: InitI18next) => { + const i18nModules = [Backend, LanguageDetector, ...modules]; + i18nModules.forEach((module) => { + i18n.use(module); + }); + const i18nOptions: InitOptions = { + react: { + useSuspense: false, + }, + fallbackLng: 'zh-CN', // 未找到最终匹配zh_CN + interpolation: { + escapeValue: false, + }, + detection: { + // caches: ['localStorage', 'cookie'], + caches: ['cookie'], + }, + ...options, + }; + + return i18n.init(i18nOptions, (err) => { + Log.info('i18n插件初始化完毕', err); + }); +}; + +export default initI18next; +export const t = i18n.t.bind(i18n); diff --git a/web/packages/shared/package.json b/web/packages/shared/package.json index d4355fd95..aa96635ad 100644 --- a/web/packages/shared/package.json +++ b/web/packages/shared/package.json @@ -1,13 +1,51 @@ { - "name": "@micro-frontend/shared", + "name": "@tencent/micro-frontend-shared", "version": "1.0.0", - "description": "shared", + "description": "micro-frontend 公共模块", "keywords": [ + "micro-frontend", "shared" ], + "homepage": "https://github.com/Tencent/CodeAnalysis", "license": "MIT", - "scripts": {}, - "devDependencies": { - "assets-webpack-plugin": "^7.1.1" + "main": "dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "build": "tsc", + "lint": "eslint --ignore-pattern dist --ignore-pattern node_modules --ext .js,.jsx,.ts,.tsx .", + "lint:fix": "eslint --ignore-pattern dist --ignore-pattern node_modules --ext .js,.jsx,.ts,.tsx . --fix", + "prepare": "yarn build", + "prepublishOnly": "yarn lint" + }, + "dependencies": { + "@types/classnames": "^2.3.1", + "@types/lodash": "^4.14.175", + "@types/qs": "^6.9.7", + "@types/validator": "^13.7.3", + "@types/react": "^17.0.24", + "@types/react-dom": "^17.0.9", + "@types/react-redux": "^7.1.18", + "@types/react-router-dom": "^5.3.1", + "@types/react-copy-to-clipboard": "^5.0.2", + "classnames": "^2.3.1", + "coding-oa-uikit": "^4.3.10", + "lodash": "^4.17.21", + "i18next": "^21.6.14", + "i18next-browser-languagedetector": "^6.1.3", + "i18next-http-backend": "^1.4.0", + "i18next-scanner": "^3.1.0", + "universal-cookie": "^4.0.4", + "moment": "^2.29.4", + "qs": "^6.10.1", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-redux": "^7.2.5", + "react-router-dom": "^5.3.0", + "react-copy-to-clipboard": "^5.0.4", + "react-i18next": "^11.17.3", + "tdesign-react": "0.36.4", + "typescript": "^4.5.5" } } \ No newline at end of file diff --git a/web/packages/login/src/common/style/color.scss b/web/packages/shared/style/color.scss similarity index 100% rename from web/packages/login/src/common/style/color.scss rename to web/packages/shared/style/color.scss diff --git a/web/packages/login/src/common/style/common.scss b/web/packages/shared/style/common.scss similarity index 98% rename from web/packages/login/src/common/style/common.scss rename to web/packages/shared/style/common.scss index cb6336229..a476f9753 100644 --- a/web/packages/login/src/common/style/common.scss +++ b/web/packages/shared/style/common.scss @@ -1,8 +1,5 @@ @import "./color.scss"; -@import "./font.scss"; -@import "./reset.scss"; -// nickctang 添加的公共样式 .text-left { text-align: left; } @@ -450,8 +447,6 @@ $z-max: 9998 !default; margin-left: 12px; } -$text-color: $grey-8; - .text-black { color: $black; } diff --git a/web/packages/tca-manage/src/common/style/font.scss b/web/packages/shared/style/font.scss similarity index 100% rename from web/packages/tca-manage/src/common/style/font.scss rename to web/packages/shared/style/font.scss diff --git a/web/packages/shared/style/index.scss b/web/packages/shared/style/index.scss new file mode 100644 index 000000000..83cc2820c --- /dev/null +++ b/web/packages/shared/style/index.scss @@ -0,0 +1,8 @@ +:global { + @import "./color.scss"; + @import "./reset.scss"; + @import "./font.scss"; + @import "./common.scss"; + @import "./uikit.scss"; + @import "./tdesign.scss"; +} diff --git a/web/packages/login/src/common/style/reset.scss b/web/packages/shared/style/reset.scss similarity index 100% rename from web/packages/login/src/common/style/reset.scss rename to web/packages/shared/style/reset.scss diff --git a/web/packages/shared/style/tdesign.scss b/web/packages/shared/style/tdesign.scss new file mode 100644 index 000000000..9fad4afeb --- /dev/null +++ b/web/packages/shared/style/tdesign.scss @@ -0,0 +1,61 @@ +@import "./color.scss"; + +:global(.t-progress__info) { + display: flex; + align-items: center; +} + +:global(.t-table__body) { + &>tr { + td>a.link-name { + color: $grey-8; + + &.with-icon { + display: flex; + align-items: center; + } + + .path { + font-size: 12px; + color: $grey-7; + max-width: 400px; + word-break: break-all; + } + } + + &:hover { + td>a.link-name { + color: $blue-5 !important; + } + } + } +} + +:global(.t-tag) { + + &.warning { + color: $warging-color; + background-color: $warging-bg; + } + + &.success { + color: $success-color; + background-color: $success-bg; + } + + &.fatal { + color: $fatal-color; + background-color: $fatal-bg; + } + + &.error { + color: $error-color; + background-color: $error-bg; + } + + &.processing, + &.info { + color: $tip-color; + background-color: $tip-bg; + } +} \ No newline at end of file diff --git a/web/packages/login/src/common/style/uikit.scss b/web/packages/shared/style/uikit.scss similarity index 77% rename from web/packages/login/src/common/style/uikit.scss rename to web/packages/shared/style/uikit.scss index 5f6e28515..d5d4a7229 100644 --- a/web/packages/login/src/common/style/uikit.scss +++ b/web/packages/shared/style/uikit.scss @@ -4,7 +4,7 @@ margin-bottom: 0 !important; &::before { - border-bottom-color: $grey-3 !important; + border-bottom-color: $grey-3 !important; } } @@ -26,6 +26,7 @@ } :global(.ant-tabs-tab-btn) { + &:active, &:focus { color: $grey-8; @@ -37,20 +38,22 @@ background: $grey-5; } -:global(.ant-table-wrapper) { - // 防止筛选项过长被挡住 - min-height: 300px; -} - :global(.ant-table-tbody) { - & > tr { - td > a.link-name { + &>tr { + td>a.link-name { color: $grey-8; + + .path { + font-size: 12px; + color: $grey-7; + max-width: 400px; + word-break: break-all; + } } &:hover { - td > a.link-name { - color: $blue-5 !important; + td>a.link-name { + color: $blue-5 !important; } } } @@ -58,7 +61,6 @@ :global(.ant-tag) { padding: 0px 8px; - border: none; &.warning { color: $warging-color; @@ -80,8 +82,9 @@ background-color: $error-bg; } - &.processing { + &.processing, + &.info { color: $tip-color; background-color: $tip-bg; } -} +} \ No newline at end of file diff --git a/web/packages/shared/tca/component/index.ts b/web/packages/shared/tca/component/index.ts new file mode 100644 index 000000000..715ba6587 --- /dev/null +++ b/web/packages/shared/tca/component/index.ts @@ -0,0 +1 @@ +export { default as NodeStatus } from './node-status'; diff --git a/web/packages/shared/tca/component/node-status/index.tsx b/web/packages/shared/tca/component/node-status/index.tsx new file mode 100644 index 000000000..38e0caed9 --- /dev/null +++ b/web/packages/shared/tca/component/node-status/index.tsx @@ -0,0 +1,51 @@ +/** + * 节点状态组件 + */ +import React from 'react'; +import { Tag } from 'coding-oa-uikit'; +import Loading from 'coding-oa-uikit/lib/icon/Loading'; +import Stop from 'coding-oa-uikit/lib/icon/Stop'; +import ExclamationCircle from 'coding-oa-uikit/lib/icon/ExclamationCircle'; +import DotCircle from 'coding-oa-uikit/lib/icon/DotCircle'; + +export const STATUS_ENUM = { + /** 不可用 */ + DISACTIVE: 0, + /** 活跃 */ + ACTIVE: 1, + /** 离线 */ + OFFLINE: 2, +}; + +export const STATE_ENUM = { + /** 空闲 */ + FREE: 0, + /** 忙碌 */ + BUSY: 1, +}; + + +interface NodeStatusProps { + /** 节点信息 */ + node: any +} + +/** 节点状态组件 */ +const NodeStatus = ({ node }: NodeStatusProps) => { + if (node) { + const { enabled, state } = node; + if (enabled === STATUS_ENUM.ACTIVE && state === STATE_ENUM.BUSY) { + return } color='processing'>运行中; + } + if (enabled === STATUS_ENUM.ACTIVE) { + return } color='success'>在线; + } + if (enabled === STATUS_ENUM.DISACTIVE) { + return }>不可用; + } + return } color='warning'>离线; + } + return <>; +}; + +export default NodeStatus; diff --git a/web/packages/shared/tdesign-component/ellipsis/index.tsx b/web/packages/shared/tdesign-component/ellipsis/index.tsx new file mode 100644 index 000000000..43042ace1 --- /dev/null +++ b/web/packages/shared/tdesign-component/ellipsis/index.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import classnames from 'classnames'; + +interface IProps { + maxWidth?: number; + className?: string; + children?: React.ReactNode; +} + +const EllipsisTemplate = ({ maxWidth, className, children }: IProps) => ( +
+ {children} +
+); + +export default EllipsisTemplate; diff --git a/web/packages/tca-manage/src/context/constant.ts b/web/packages/shared/tdesign-component/index.tsx similarity index 100% rename from web/packages/tca-manage/src/context/constant.ts rename to web/packages/shared/tdesign-component/index.tsx diff --git a/web/packages/shared/tdesign-component/loading/index.tsx b/web/packages/shared/tdesign-component/loading/index.tsx new file mode 100644 index 000000000..8d4c82584 --- /dev/null +++ b/web/packages/shared/tdesign-component/loading/index.tsx @@ -0,0 +1,14 @@ +/** + * 加载中组件 + */ + +import React from 'react'; +import { LoadingIcon } from 'tdesign-icons-react'; +import s from './style.scss'; + +const Loading = () =>
+ + 加载中 +
; + +export default Loading; diff --git a/web/packages/shared/tdesign-component/loading/style.scss b/web/packages/shared/tdesign-component/loading/style.scss new file mode 100644 index 000000000..181540c6d --- /dev/null +++ b/web/packages/shared/tdesign-component/loading/style.scss @@ -0,0 +1,11 @@ +.loading { + display: flex; + align-items: center; + justify-content: center; + padding: 20px 0; + + .icon { + margin-right: 8px; + font-size: 20px; + } +} diff --git a/web/packages/shared/tdesign-component/micro-init/index.tsx b/web/packages/shared/tdesign-component/micro-init/index.tsx new file mode 100644 index 000000000..9d56388fc --- /dev/null +++ b/web/packages/shared/tdesign-component/micro-init/index.tsx @@ -0,0 +1,132 @@ +import React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { legacy_createStore as createStore, combineReducers } from 'redux'; +import { Provider } from 'react-redux'; +import { ConfigProvider } from 'tdesign-react'; +import enConfig from 'tdesign-react/es/locale/en_US'; +import zhConfig from 'tdesign-react/es/locale/zh_CN'; +import 'tdesign-react/es/style/index.css'; +import Cookie from 'universal-cookie'; +import { StoreProvider } from '../../hook-store'; +import { Store, StateProps, ActionProps, Middleware } from '../../hook-store/types'; +import '../../style/index.scss'; + +interface MicroInitProps { + /** 微前端外层HTMLElement id */ + id: string; + /** 微前端唯一标识名称 */ + name: string; + /** 入口 */ + container: JSX.Element | React.ReactNode; + /** 微前端上层reducers */ + reducers?: InjectReducer; +} + +interface MicroHookStore { + /** react-hook store 状态管理 */ + hookStore?: { + enable: boolean; + stores: Store[]; + middlewares?: Middleware[]; + } +} + +/** + * 微前端初始化 + * @param microInitProps + */ +const MicroInit = ({ + id, name, container, reducers, hookStore, +}: MicroInitProps & MicroHookStore) => { + const bootstrap = () => { + // bootstrap + }; + + const create = (rootDom: HTMLElement) => { + if (rootDom) { + let wrapDom = document.getElementById(name); + if (!wrapDom) { + wrapDom = document.createElement('div'); + wrapDom.id = name; + wrapDom.className = `${name}-container`; + } + let containerDom = document.getElementById(id); + if (!containerDom) { + containerDom = document.createElement('div'); + containerDom.id = id; + containerDom.appendChild(wrapDom); + rootDom.appendChild(containerDom); + } else { + containerDom.appendChild(wrapDom); + } + } + }; + + const mount = async (props: ApplicationCustomProps) => { + const { rootDom, injectAsyncReducer, store } = props; + create(rootDom); + const cookie = new Cookie(); + const language = (cookie.get('i18next') as string); + const locale = language === 'en-US' ? enConfig : zhConfig; + let renderContent = + {container} + ; + + if (hookStore?.enable) { + const { stores, middlewares } = hookStore; + renderContent = + {renderContent} + ; + } + + if (store) { + // 将reducers注入 + if (reducers && injectAsyncReducer) { + Object.keys(reducers).forEach(key => injectAsyncReducer(key, reducers[key])); + } + renderContent = + {renderContent} + ; + } + + render(renderContent, document.getElementById(name)); + }; + + const unmount = () => { + const e = document.getElementById(name); + if (e) { + unmountComponentAtNode(e); + e.remove(); + } + }; + + if (typeof window.microHook === 'object') { + window.microHook.registerApp(name, { + bootstrap, + mount, + unmount, + }); + } else { + const ASYNC_REDUCERS: InjectReducer = {}; + const { store, injectAsyncReducer } = (() => { + const rootReducers: InjectReducer = {}; + const store = createStore(combineReducers({ ...rootReducers })); + const injectAsyncReducer = (name: string, reducer: Reducer, override = false) => { + if (!(ASYNC_REDUCERS[name] && !override)) { + ASYNC_REDUCERS[name] = reducer; + store.replaceReducer(combineReducers({ ...rootReducers, ...ASYNC_REDUCERS })); + } + }; + return { store, injectAsyncReducer }; + })(); + mount({ + rootDom: document.body, + store, + injectAsyncReducer, + }); + } +}; + +export default MicroInit; diff --git a/web/packages/shared/tdesign-component/micro-layout/index.tsx b/web/packages/shared/tdesign-component/micro-layout/index.tsx new file mode 100644 index 000000000..747db9677 --- /dev/null +++ b/web/packages/shared/tdesign-component/micro-layout/index.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { get } from 'lodash'; +import Loading from '../loading'; +import { useSelector } from 'react-redux'; + +interface MicroLayoutProps { + children: React.ReactNode; + value?: string; + loading?: React.ReactNode; + disable?: boolean; +} + +const MicroLayout = ({ + children, + value = 'completed', + loading = , + disable = false, +}: MicroLayoutProps) => { + const INITIAL = useSelector((state: any) => state.INITIAL); + const completed = get(INITIAL, value, false); + if (!completed && !disable) { + return <>{loading}; + } + return <>{children}; +}; + +export default MicroLayout; diff --git a/web/packages/shared/tdesign-component/modal/delete-modal/index.tsx b/web/packages/shared/tdesign-component/modal/delete-modal/index.tsx new file mode 100644 index 000000000..ba6ef66b7 --- /dev/null +++ b/web/packages/shared/tdesign-component/modal/delete-modal/index.tsx @@ -0,0 +1,111 @@ +// Copyright (c) 2021-2022 THL A29 Limited +// +// This source code file is made available under MIT License +// See LICENSE for details +// ============================================================================== + +/** + * 确认删除操作弹框 + */ + +import React, { useEffect, useState, useRef } from 'react'; +import { Dialog, Form, Input, message, Button } from 'tdesign-react'; +import { useTranslation } from 'react-i18next'; + +import s from './style.scss'; + +const { FormItem } = Form; + +interface DeleteModalProps { + actionType: string; + objectType: string; + confirmName: string; + visible: boolean; + addtionInfo?: string; + onCancel: () => void; + onOk: () => void; +} + +const DeleteModal = ({ actionType, objectType, confirmName, addtionInfo = '', visible, onCancel, onOk }: DeleteModalProps) => { + const formRef = useRef(null); + const { t } = useTranslation(); + const [confirmed, setConfirmed] = useState(false); + + useEffect(() => { + visible && setConfirmed(false); + }, [visible]); + + /** + * 表单提交操作 + */ + const onSubmitHandle = () => { + formRef.current?.validate().then((result: any) => { + if (result === true) { + const formData = formRef.current?.getFieldsValue(true); + if (formData?.confirm === confirmName) { + onOk(); + } else { + message.error(t('验证失败,请重新输入')); + } + } + }); + }; + + const checkConfirm = (changedValues: any) => { + if (changedValues?.confirm === confirmName) { + setConfirmed(true); + } else { + setConfirmed(false); + } + }; + + return ( + + {t('取消')} + , + , + ]} + destroyOnClose + > +

+ {t('您正在')}{actionType}{objectType} {confirmName}{' '}
+

+ {addtionInfo &&

{addtionInfo}

} +

{t(`为确认${actionType}操作,请输入您要${actionType}的`)}{objectType}{t('名称')}

+
+ + + + +
+ ); +}; + +export default DeleteModal; diff --git a/web/packages/tca-manage/src/components/delete-modal/style.scss b/web/packages/shared/tdesign-component/modal/delete-modal/style.scss similarity index 99% rename from web/packages/tca-manage/src/components/delete-modal/style.scss rename to web/packages/shared/tdesign-component/modal/delete-modal/style.scss index 59d9b1e32..b522d4bd2 100644 --- a/web/packages/tca-manage/src/components/delete-modal/style.scss +++ b/web/packages/shared/tdesign-component/modal/delete-modal/style.scss @@ -18,4 +18,4 @@ .confirm-input { margin-bottom: 0px; } -} \ No newline at end of file +} diff --git a/web/packages/shared/tdesign-component/modal/index.tsx b/web/packages/shared/tdesign-component/modal/index.tsx new file mode 100644 index 000000000..40718a3ee --- /dev/null +++ b/web/packages/shared/tdesign-component/modal/index.tsx @@ -0,0 +1 @@ +export { default as DeleteModal } from './delete-modal'; diff --git a/web/packages/shared/tdesign-component/node-status/index.tsx b/web/packages/shared/tdesign-component/node-status/index.tsx new file mode 100644 index 000000000..ccd07190d --- /dev/null +++ b/web/packages/shared/tdesign-component/node-status/index.tsx @@ -0,0 +1,48 @@ +/** + * 节点状态组件 + */ +import React from 'react'; +import { Tag } from 'tdesign-react'; +import { LoadingIcon, MinusCircleIcon, ErrorCircleIcon, ChartBubbleIcon } from 'tdesign-icons-react'; + +export enum StatusEnum { + /** 不可用 */ + DISACTIVE, + /** 活跃 */ + ACTIVE, + /** 离线 */ + OFFLINE, +} + +export enum StateEnum { + /** 空闲 */ + FREE, + /** 忙碌 */ + BUSY +} + + +interface NodeStatusProps { + /** 节点信息 */ + node: any +} + +/** 节点状态组件 */ +const NodeStatus = ({ node }: NodeStatusProps) => { + if (node) { + const { enabled, state } = node; + if (enabled === StatusEnum.ACTIVE && state === StateEnum.BUSY) { + return } theme="primary" variant="light">运行中; + } + if (enabled === StatusEnum.ACTIVE) { + return } theme="success" variant="light">在线; + } + if (enabled === StatusEnum.DISACTIVE) { + return }>不可用; + } + return } theme="warning" variant="light">离线; + } + return <>; +}; + +export default NodeStatus; diff --git a/web/packages/shared/tdesign-component/search/index.tsx b/web/packages/shared/tdesign-component/search/index.tsx new file mode 100644 index 000000000..8b2f21977 --- /dev/null +++ b/web/packages/shared/tdesign-component/search/index.tsx @@ -0,0 +1,250 @@ +/** + * 过滤筛选组件 + */ +import React, { useState, useEffect, useRef } from 'react'; +import { useHistory } from 'react-router-dom'; +import { get, isEmpty, pick } from 'lodash'; +import classnames from 'classnames'; +import { Button, Checkbox, Input, Select, DatePicker, Loading, Row, Col, DateRangePicker, Form } from 'tdesign-react'; +import { ChevronDownIcon, ChevronUpIcon, ClearIcon } from 'tdesign-icons-react'; +import { getURLPathByFilterParams } from '../../util/route'; +import { FilterField, Filter as FilterParams } from '../../util/types'; +import { formatDate } from '../../util'; +import s from './style.scss'; + +const { FormItem } = Form; + +/** 判断是否存在不为空的筛选参数 */ +const isExistSearchParam = (params: FilterParams) => Object.keys(params) + .some((key: string) => typeof params[key] === 'number' || !isEmpty(params[key])); + +/** search 组件 表单类型 */ +export type SearchFormFieldType = 'input' | 'select' | 'datepicker' | 'checkbox' | 'multiselect' | 'rangepicker'; + +/** search 组件表单字段类型 */ +export interface SearchFormField extends FilterField { + /** 表单类型 */ + formType: SearchFormFieldType; + /** 表单value */ + defaultValue?: string | number; + /** form item 名称 */ + label?: string; + /** 表单 placeholder */ + placeholder?: string; + /** select options,仅type为select时生效 */ + options?: any[]; + /** 表单样式 */ + style?: React.CSSProperties; +} + +interface SearchProps { + /** 一级筛选字段 */ + fields: SearchFormField[], + /** 高级筛选字段 */ + moreFields?: SearchFormField[], + /** 筛选参数,传入参数 */ + searchParams: FilterParams; + /** 筛选请求加载中 */ + loading: boolean; + /** 筛选回调 */ + callback?: (params: any) => void; + /** 是否开启路由跳转,默认开启 */ + route?: boolean; + /** 筛选组件class */ + className?: string; + /** 筛选组件style */ + style?: React.CSSProperties; + /** 首行额外内容 */ + extraContent?: React.ReactNode; + /** 筛选项 */ + filters?: FilterField[]; + /** 默认筛选键值对 */ + defaultValues?: any; +} + +const formLayout = { + flex: '0 1 auto', +}; + +const Search = ({ + fields, moreFields, searchParams, loading, callback, + className, style, extraContent, route = true, filters, + defaultValues, +}: SearchProps) => { + const [more, setMore] = useState(false); + const history = useHistory(); + const formRef = useRef(null); + + useEffect(() => { + if (!loading) { + // 判断是否展开高级筛选 + if (moreFields) { + const fieldNames = moreFields.map(field => field.name); + if (isExistSearchParam(pick(searchParams, fieldNames))) { + setMore(true); + } + } + } + }, [loading]); + + // 筛选路由跳转处理,仅当route为true时生效 + const onRouteHandle = (params: FilterParams) => { + if (route) { + const filterFields = moreFields ? fields.concat(moreFields) : fields; + const url = getURLPathByFilterParams(filters ? filters : filterFields, params); + history.push(url); + } + }; + + const onChange = (key: string, value: any) => { + const params = { + ...searchParams, + [key]: value, + }; + onRouteHandle(params); + callback?.(params); + }; + + const onRangeChange = (key: string, range: any) => { + const params = { + ...searchParams, + [`${key}_gte`]: range[0], + [`${key}_lte`]: range[1], + }; + onRouteHandle(params); + callback?.(params); + }; + + const onClear = () => { + const params: FilterParams = {}; + // 将筛选入参清空 + Object.keys(searchParams).forEach((key) => { + params[key] = ''; + }); + onRouteHandle({ ...params, ...defaultValues }); + callback?.({ ...params, ...defaultValues }); + formRef.current?.setFieldsValue?.({ ...params, ...defaultValues }); + }; + + const getItem = (field: SearchFormField) => { + switch (field.formType) { + case 'input': + return onChange(field.name, value)} + onClear={() => onChange(field.name, '')} />; + case 'select': + return onChange(field.name, value)} />; + case 'datepicker': + return onChange(field.name, date ? formatDate(date) : '')} + />; + case 'rangepicker': + return onRangeChange(field.name, dateRange)} + />; + case 'checkbox': + return onChange(field.name, checked)}>{field.placeholder}; + default: + throw new Error('field type error'); + } + }; + + const getInitData = (field: SearchFormField) => { + switch (field.formType) { + case 'multiselect': + return get(searchParams, field.name) || field.defaultValue || []; + case 'rangepicker': + return [get(searchParams, `${field.name}_gte`), get(searchParams, `${field.name}_lte`)] || field.defaultValue || []; + default: + return get(searchParams, field.name) || field.defaultValue || null; + } + }; + + return ( + +
+
+ + {fields + .map(field => (
+ + {getItem(field)} + + ))} + {!isEmpty(moreFields) && !loading + && + + + } + {isExistSearchParam(searchParams) + && + + + } + + {moreFields && more && + {moreFields + .map(field => + + {getItem(field)} + + )} + } + + {extraContent} + + ); +}; + +export default Search; + diff --git a/web/packages/shared/tdesign-component/search/style.scss b/web/packages/shared/tdesign-component/search/style.scss new file mode 100644 index 000000000..81cbccf32 --- /dev/null +++ b/web/packages/shared/tdesign-component/search/style.scss @@ -0,0 +1,11 @@ +.search { + padding: 3px 24px; + margin-bottom: 1px; + background-color: hsla(0, 0%, 98.8%, 0.98); + box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.1), 0 0.5px 0 0 rgba(0, 0, 0, 0.08); + min-height: 42px; + + .search-content { + margin: 5px 0; + } +} diff --git a/web/packages/shared/tdesign-component/table/index.tsx b/web/packages/shared/tdesign-component/table/index.tsx new file mode 100644 index 000000000..3d357075c --- /dev/null +++ b/web/packages/shared/tdesign-component/table/index.tsx @@ -0,0 +1,39 @@ +/** + * table组件,默认设置了分页器,以及分页change + */ +import React from 'react'; +import { useHistory } from 'react-router-dom'; +import classnames from 'classnames'; +import { PrimaryTable, PrimaryTableProps, PaginationProps, PageInfo } from 'tdesign-react'; +import { getPaginationParams, getURLPathByFilterParams } from '@tencent/micro-frontend-shared/util'; +import { FilterField } from '@tencent/micro-frontend-shared/util/types'; +import s from './style.scss'; +interface RouteTableProps extends Omit { + rowKey?: string + pagination: PaginationProps; + filterFields?: FilterField[] +} + +const RouteTable = (props: RouteTableProps) => { + const { pagination, filterFields = [], className, rowKey = 'id', hover = true, ...otherProps } = props; + const history = useHistory(); + const onChange = (pageInfo: PageInfo) => { + if (!pageInfo.pageSize) { + throw new Error('pageSize is undefined'); + } + const url = getURLPathByFilterParams(filterFields, getPaginationParams(pageInfo.current, pageInfo.pageSize)); + history.push(url); + }; + return ( + + ); +}; + +export default RouteTable; diff --git a/web/packages/shared/tdesign-component/table/style.scss b/web/packages/shared/tdesign-component/table/style.scss new file mode 100644 index 000000000..ffde5af11 --- /dev/null +++ b/web/packages/shared/tdesign-component/table/style.scss @@ -0,0 +1,6 @@ +.route-table { + :global(.t-table__header > tr > th) { + white-space: nowrap; + background-color: white; + } +} diff --git a/web/packages/shared/tsconfig.json b/web/packages/shared/tsconfig.json index 1e8e115a4..77453b7d2 100644 --- a/web/packages/shared/tsconfig.json +++ b/web/packages/shared/tsconfig.json @@ -1,3 +1,22 @@ { - "extends": "../../tsconfig", + "compilerOptions": { + "target": "es2021", + "module": "commonjs", + "declaration": true, + "outDir": "./dist", + "strict": true, + "esModuleInterop": true, + "typeRoots": [], + "jsx": "react", + }, + "include": [ + "component", + "tdesign-component", + "hook-store", + "hooks", + "i18n", + "tca", + "util", + "global.d.ts" + ] } \ No newline at end of file diff --git a/web/packages/shared/util/browser.ts b/web/packages/shared/util/browser.ts new file mode 100644 index 000000000..1fe870657 --- /dev/null +++ b/web/packages/shared/util/browser.ts @@ -0,0 +1,42 @@ + +/** + * 判断是否企业微信内置浏览器 + * @returns boolean + */ +export const isWxWorkBrowser = () => { + const ua = navigator.userAgent.toLowerCase(); + const isWx = ua.match(/MicroMessenger/i)?.[0] === 'micromessenger'; + const isWxWork = ua.match(/WxWork/i)?.[0] === 'wxwork'; + return isWx && isWxWork; +}; + +/** + * 判断是否微信内置浏览器 + * @returns boolean + */ +export const isWxBrowser = () => { + const ua = navigator.userAgent.toLowerCase(); + const isWx = ua.match(/MicroMessenger/i)?.[0] === 'micromessenger'; + const isWxWork = ua.match(/WxWork/i)?.[0] === 'wxwork'; + return isWx && !isWxWork; +}; + +/** + * 判断是否QQ内置浏览器 + * @returns boolean + */ +export const isQQBrowser = () => { + const ua = navigator.userAgent.toLowerCase(); + const isQQ = ua.match(/QQ/i)?.[0] === 'qq'; + return isQQ; +}; + +/** + * 判断是否支付宝内置浏览器 + * @returns boolean + */ +export const isAlipayBrowser = () => { + const ua = navigator.userAgent.toLowerCase(); + const isAlipay = ua.match(/AlipayClient/i)?.[0] === 'alipayclient'; + return isAlipay; +}; diff --git a/web/packages/shared/util/check.ts b/web/packages/shared/util/check.ts new file mode 100644 index 000000000..ab7e07006 --- /dev/null +++ b/web/packages/shared/util/check.ts @@ -0,0 +1,16 @@ +/** + * utils - check 工具库,用于字段等判断校验 + */ + +/** + * 判断字段值是否为true + * @param value 字段值 + * @param strict 是否严格模式,默认false,即如果value为true字符串也可 + * @returns 返回boolean + */ +export const isTrue = (value: any, strict = false) => { + if (typeof value === 'string' && !strict) { + return value.toLowerCase() === 'true'; + } + return value === true; +}; diff --git a/web/packages/shared/util/fetch.ts b/web/packages/shared/util/fetch.ts new file mode 100644 index 000000000..aa041cb9f --- /dev/null +++ b/web/packages/shared/util/fetch.ts @@ -0,0 +1,312 @@ +/** + * utils - fetch 工具库 + */ +import qs from 'qs'; +import message from 'coding-oa-uikit/lib/message'; + +export interface FetchCustomParams { + /** fetch 自定义headers */ + headers?: HeadersInit; + /** fetch超时时间,默认30s */ + timeout?: number; + /** fetch是否提示错误信息,默认展示 */ + showError?: boolean; + /** fetch 成功处理 */ + resultHandler?: (data: any) => any; + /** fetch 失败处理 */ + failResultHandler?: (data: any) => any; + /** fetch 请求状态处理 */ + statusHandler?: (response: Response) => void; +} + +interface RequestCustom extends FetchCustomParams { + /** fetch请求是否已收到响应 */ + isFetched: boolean; + /** fetch请求是否已被中断 */ + isAbort: boolean; +} + +/** + * 封装后的fetch + * @param input RequestInfo + * @param init RequestInit + * @param customParams 自定义请求参数 + * @returns fetch Promise + */ +export const fetch = (input: RequestInfo, init?: RequestInit, customParams?: FetchCustomParams) => { + // 默认参数 + const defaultCustom: RequestCustom = { + showError: true, + headers: {}, + timeout: 30, + isAbort: false, + isFetched: false, + }; + const custom: RequestCustom = Object.assign({}, defaultCustom, customParams); + if (init) { + init.headers = Object.assign({}, init.headers, { + 'X-Requested-With': 'XMLHttpRequest', + 'Content-Type': 'application/json;charset=UTF-8', + }); + // 处理自定义请求头 + if ('headers' in custom) { + const { headers } = custom; + init.headers = Object.assign({}, init.headers, headers); + } + } + const fetchPromise = new Promise((resolve, reject) => { + window.fetch(input, init).then((response) => { + // 如果请求被中断,则return + if (custom.isAbort) { + return; + } + // 请求结束,将isFetched置为true + custom.isFetched = true; + // 请求状态前置处理 + custom.statusHandler?.(response); + if (response.status === 204) { + return resolve({ code: 0 }); + } + if (response.status === 404) { + return reject(failResultHandler({ msg: '接口不存在' }, custom)); + } + response.json().then((jsonData) => { + if (response.ok) { + return resolve(resultHandler(jsonData, custom)); + } + return reject(failResultHandler(jsonData, custom)); + }) + .catch(e => reject(failResultHandler(e, custom))); + }) + .catch((e) => { + if (custom.isAbort) { + return; + } + custom.isFetched = true; + return reject(failResultHandler(e, custom)); + }); + }); + // Promise.race 谁先结束就返回哪个数据 + return Promise.race([fetchPromise, fetchTimeout(custom)]); +}; + +/** + * fetch超时处理,默认30秒 + * @param custom 自定义request参数 + * @returns Promise + */ +const fetchTimeout = (custom: RequestCustom) => new Promise((resolve, reject) => { + const timer = setTimeout(() => { + clearTimeout(timer); + // 如果还未收到响应结果,则执行超时逻辑,中断结果 + if (!custom.isFetched) { + // 还未收到响应,则开始超时逻辑,并标记fetch需要放弃 + custom.isAbort = true; + custom.showError && message.error('网络开小差了,稍后再试'); + reject({ msg: 'timeout' }); + } + }, (custom.timeout || 30) * 1000); +}); + +/** + *处理成功结果 + * @param jsonData 请求数据结果 + * @returns 格式化响应数据结果 + */ +const resultHandler = (jsonData: any, custom: RequestCustom) => { + // 自定义成功结果处理 + if (custom.resultHandler) { + return custom.resultHandler(jsonData); + } + return jsonData.data; +}; + +/** + * 处理失败结果 + * @param jsonData fetch响应结果 + * @param custom 自定义请求参数 + * @returns 失败响应数据结果 + */ +const failResultHandler = (jsonData: any, custom: RequestCustom) => { + // 自定义失败结果处理 + if (custom.failResultHandler) { + return custom.failResultHandler(jsonData); + } + const { msg } = jsonData; + if (msg && custom.showError) { + message.error(getFailMessage(msg)); + } + return jsonData; +}; + +/** + * 获取失败信息 + * @param msg 信息 + * @returns 返回错误信息 + */ +const getFailMessage: any = (msg: any) => { + if (msg) { + if (typeof msg === 'string') { + return msg; + } + if (Array.isArray(msg)) { + return msg.pop(); + } + if (typeof msg === 'object' && Object.keys(msg).length > 0) { + // 遍历 object + return getFailMessage(msg[Object.keys(msg).pop() as any]); + } + } + return '接口请求失败'; +}; + +/** fetch 请求管理模块 */ +export class FetchManager { + private custom?: FetchCustomParams; + constructor(customParams?: FetchCustomParams) { + this.custom = customParams; + } + + get = (url: string, data?: any, custom?: FetchCustomParams) => fetch(`${url}${!data ? '' : `?${qs.stringify(data, { arrayFormat: 'comma' })}`}`, { + method: 'GET', + }, { ...this.custom, ...custom }); + + post = (url: string, data: any, custom?: FetchCustomParams) => fetch(url, { + method: 'POST', + body: JSON.stringify(data), + }, { ...this.custom, ...custom }); + + put = (url: string, data: any, custom?: FetchCustomParams) => fetch(url, { + method: 'PUT', + body: JSON.stringify(data), + }, { ...this.custom, ...custom }); + + patch = (url: string, data: any, custom?: FetchCustomParams) => fetch(url, { + method: 'PATCH', + body: JSON.stringify(data), + }, { ...this.custom, ...custom }); + + del = (url: string, data?: any, custom?: FetchCustomParams) => fetch(url, { + method: 'DELETE', + body: data && JSON.stringify(data), + }, { ...this.custom, ...custom }); + + getFile = (url: string, data?: any, custom?: FetchCustomParams) => fetch(url, { + method: 'GET', + body: data ? JSON.stringify(data) : null, + }, { ...this.custom, ...custom }); +} + +/** 初始化默认的fetch模块 */ +const fetchManager = new FetchManager(); + +export const { get, post, put, patch, del } = fetchManager; + +/** + * 初始化API URL 提供 restful api + * 单例模式 + */ +export class FetchAPIManager { + private static instance: FetchAPIManager; + + /** + * 获取 FetchAPIManager 实例 + * @param url API URL + * @param fm fetchManager + * @returns FetchAPIManager实例 + */ + static getInstance(url: string, fm = fetchManager) { + if (!FetchAPIManager.instance || FetchAPIManager.instance.url !== url) { + FetchAPIManager.instance = new FetchAPIManager(url, fm); + } + return FetchAPIManager.instance; + } + + private url: string; + private fm: FetchManager; + + constructor(url: string, fm = fetchManager) { + this.url = url.charAt(url.length - 1) === '/' ? url : `${url}/`; + this.fm = fm; + } + + /** + * 获取实例列表 + * @param data 筛选参数 + * @param custom FetchCustomParams + * @returns Promise + */ + get = (data?: any, custom?: FetchCustomParams) => this.fm.get(this.url, data, custom); + + /** + * 创建实例 + * @param data 数据 + * @param custom FetchCustomParams + * @returns Promise + */ + create = (data: any, custom?: FetchCustomParams) => this.fm.post(this.url, data, custom); + + /** + * 获取实例详情 + * @param id 实例ID + * @param data 参数 + * @param custom FetchCustomParams + * @returns Promise + */ + getDetail = (id: number | string, data?: any, custom?: FetchCustomParams) => this.fm.get(`${this.url}${id}/`, data, custom); + + /** + * 更新实例 + * @param id 实例ID + * @param data 更新数据 + * @param custom FetchCustomParams + * @returns Promise + */ + update = (id: number | string, data: any, custom?: FetchCustomParams) => this.fm.put(`${this.url}${id}/`, data, custom); + + /** + * 删除实例 + * @param id 实例ID + * @param data 数据 + * @param custom FetchCustomParams + * @returns Promise + */ + delete = (id: number | string, data?: any, custom?: FetchCustomParams) => this.fm.del(`${this.url}${id}/`, data, custom); + + /** + * post 操作 + * @param data 参数 + * @param custom FetchCustomParams + * @returns Promise + */ + post = (data: any, custom?: FetchCustomParams) => this.fm.post(this.url, data, custom); + + /** + * put 操作 + * @param data 数据 + * @param extraUrl 额外URL + * @param custom FetchCustomParams + * @returns Promise + */ + put = (data: any, extraUrl = '', custom?: FetchCustomParams) => this.fm.put(`${this.url}${extraUrl}/`, data, custom); + + /** + * del 操作 + * @param data 数据 + * @param extraUrl 额外URL + * @param custom FetchCustomParams + * @returns Promise + */ + del = (data?: any, extraUrl = '', custom?: FetchCustomParams) => this.fm.del(`${this.url}${extraUrl}/`, data, custom); +} + +export default { + fetch, + FetchManager, + FetchAPIManager, + get, + post, + put, + patch, + del, +}; diff --git a/web/packages/shared/util/index.ts b/web/packages/shared/util/index.ts new file mode 100644 index 000000000..2e0f6922b --- /dev/null +++ b/web/packages/shared/util/index.ts @@ -0,0 +1,22 @@ +export * from './meta'; +export * from './time'; +export * from './route'; +export * from './size'; +export * from './check'; +export { default as FetchMgr } from './fetch'; +export { default as LogMgr } from './log'; + + +type CHOICES = { + [key: number | string]: string +}; + +/** + * 将choices转成options + * @param choices + * @returns options + */ +export const generateOptions = (choices: CHOICES, isNumber = false) => Object.keys(choices).map(key => ({ + label: choices[key], + value: isNumber ? parseInt(key, 10) : key, +})); diff --git a/web/packages/shared/util/log.ts b/web/packages/shared/util/log.ts new file mode 100644 index 000000000..a2ee42290 --- /dev/null +++ b/web/packages/shared/util/log.ts @@ -0,0 +1,39 @@ +/** + * utils - log 工具库 + */ + +/** + * 打印带有前缀的日志 + * @param prefix 前缀 + * @returns 打印带有前缀的日志 + */ +export const prefixLog = (prefix = '') => ({ + info: (message: any, ...optionalParams: any[]) => { + Log.info(prefix + message, ...optionalParams); + }, + error: (message: any, ...optionalParams: any[]) => { + Log.error(prefix + message, ...optionalParams); + }, + warn: (message: any, ...optionalParams: any[]) => { + Log.warn(prefix + message, ...optionalParams); + }, +}); + +/** + * 日志 + */ +const Log = { + info: (message: any, ...optionalParams: any[]) => { + console.info(message, ...optionalParams); + }, + error: (message: any, ...optionalParams: any[]) => { + console.error(message, ...optionalParams); + }, + warn: (message: any, ...optionalParams: any[]) => { + console.warn(message, ...optionalParams); + }, + prefixLog, +}; + +export default Log; + diff --git a/web/packages/shared/util/meta.ts b/web/packages/shared/util/meta.ts new file mode 100644 index 000000000..ea2334fc4 --- /dev/null +++ b/web/packages/shared/util/meta.ts @@ -0,0 +1,14 @@ +/** + * utils - meta 工具库 + */ + +/** + * 根据name获取meta标签content + * @param key meta name值 + * @param defaultValue + * @returns meta标签的content + */ +export const getMetaContent = (key: string, defaultValue = '') => { + const meta = document.querySelector(`meta[name=${key}]`) as HTMLMetaElement; + return meta ? meta.content : defaultValue; +}; diff --git a/web/packages/shared/util/route.ts b/web/packages/shared/util/route.ts new file mode 100644 index 000000000..225ff4c81 --- /dev/null +++ b/web/packages/shared/util/route.ts @@ -0,0 +1,154 @@ +/** + * utils - route 工具库,与路由相关的 + */ +import qs from 'qs'; +import { pick, forEach, pickBy, toNumber, toString } from 'lodash'; +import { URLSearch, Filter, FilterField, FilterFieldType } from './types'; + +/** + * 获取page、page_size分页参数 + * @param page 页码 + * @param pageSize 每页显示数量 + * @returns page, page_size + */ +const getPageNumberPaginationParams = (page: number, pageSize: number) => ({ + page, + page_size: pageSize, +}); + +/** + * 获取limit、offset分页参数 + * @param offset 偏移数 + * @param limit 每页显示数量 + * @returns limit, offset + */ +const getLimitOffsetPaginationParams = (offset: number, limit: number) => ({ + limit, offset, +}); + + +type Use = 'limitOffset' | 'pageNumber'; + +/** + * 根据页码和每页显示数量返回对应的后端分页筛选所需字段 + * @param page 页码 + * @param pageSize 每页显示数量 + * @param use 使用返回格式类型,默认使用limitOffset + * @returns 返回后端分页筛选所需字段,如limit, offset或page, page_size + */ +export const getPaginationParams = (page: number, pageSize: number, use: Use = 'limitOffset') => { + if (use === 'limitOffset') { + return getLimitOffsetPaginationParams((page - 1) * pageSize, pageSize); + } + return getPageNumberPaginationParams(page, pageSize); +}; + + +const DEFAULT_FILTER_FIELDS: FilterField[] = [{ + name: 'limit', + type: 'number', +}, { + name: 'offset', + type: 'number', +}, { + name: 'ordering', + type: 'string', +}]; + +/** 筛选默认字段名称 */ +const DEFAULT_FILTER_FIELD_NAMES = DEFAULT_FILTER_FIELDS.map(field => field.name); + +/** + * 根据筛选参数变更并获取路由参数 + * @param filterFields 额外允许的筛选字段 + * @param params 筛选传入参数 + * @returns 路由筛选参数(已剔除空值) + */ +export const getURLSearchByFilterParams = (filterFields: FilterField[] = [], filter: Filter = {}) => { + // 获取并解析路由参数 + const search = getURLSearch(); + // 获取筛选字段名称 + const fieldNames = filterFields.map(field => field.name); + // 从filter对象中pick字段,并将对应值赋予search + forEach(pick(filter, [...DEFAULT_FILTER_FIELD_NAMES, ...fieldNames]), (value, key) => { + search[key] = toString(value); + }); + // 清楚search中为''的值 + return pickBy(search, value => value !== ''); +}; + +/** + * 获取变更路由 + * @param params 参数 + * @param filterFields 允许filter的字段 + * @returns path + */ +export const getURLPathByFilterParams = (filterFields: FilterField[] = [], filter: Filter = {}) => { + const search = getURLSearchByFilterParams(filterFields, filter); + return `${window.location.pathname}?${qs.stringify(search)}`; +}; + +/** + * 通过路由参数获取filter字段 + * @param filterFields 允许filter的字段 + */ +export const getFilterFieldByURLSearch = (filterFields: FilterField[] = [], pager = { limit: 10, offset: 0 }) => { + // 解析路由参数,设置pager默认值 + const search = Object.assign({}, pager, getURLSearch()); + // 获取fitterFields的name:type + const filterFieldKV: { [name: string]: FilterFieldType } = {}; + [...DEFAULT_FILTER_FIELDS, ...filterFields].forEach((field) => { + filterFieldKV[field.name] = field.type; + }); + // 仅选取必要的筛选字段,并进行类型转换 + const filter: Filter = {}; + Object.keys(search).forEach((key) => { + const value = search[key]; + switch (filterFieldKV[key]) { + case 'string': + filter[key] = value; + break; + case 'boolean': + filter[key] = value; + break; + case 'number': + filter[key] = toNumber(value); + break; + case 'array_number': + filter[key] = value.split(',').map(item => toNumber(item)); + break; + case 'array_string': + filter[key] = value.split(','); + break; + case 'time': + filter[key] = value; + break; + default: + break; + } + }); + return filter; +}; + + +/** + * 获取路由参数 + * @returns 路由参数 + */ +export const getURLSearch = () => qs.parse(window.location.search.replace('?', '')) as URLSearch; + +/** + * 防止url调整漏洞,对回调地址进行校验 + * @param href 回调地址 + * @param validHostNames 域名白名单列表,默认[window.location.hostname] + * @returns 回调链接 + */ +export const xssRedirectUri = (redirectUri: string, validHostNames = [window.location.hostname]) => { + const a = document.createElement('a'); + a.href = decodeURIComponent(redirectUri) || ''; + // 接下来对hostname进行域名白名单的判断 + if (validHostNames.includes(a.hostname)) { + return a.href; + } + return ''; +}; diff --git a/web/packages/shared/util/size.ts b/web/packages/shared/util/size.ts new file mode 100644 index 000000000..7c81e725a --- /dev/null +++ b/web/packages/shared/util/size.ts @@ -0,0 +1,20 @@ +/** + * util - size工具库 + */ + +const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + +/** + * 字节格式转化 + * @param bytes 字节 + * @returns 格式化后字节大小 + */ +export const bytesToSize = (bytes: number) => { + if (typeof bytes !== 'number') { + return '-'; + } + if (bytes === 0) return '0 B'; + const k = 1024; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return `${(bytes / (k ** i)).toPrecision(3)} ${sizes[i]}`; +}; diff --git a/web/packages/shared/util/time.ts b/web/packages/shared/util/time.ts new file mode 100644 index 000000000..20a4d4812 --- /dev/null +++ b/web/packages/shared/util/time.ts @@ -0,0 +1,35 @@ +/** + * utils - time 工具库 + */ + +import Moment, { MomentInput } from 'moment'; + +/** + * 格式化time为日期字符串 + * @param time 时间 + * @param format 格式化字符串 + * @returns 格式化日期字符串 + */ +export const formatDate = (time: MomentInput, format = 'YYYY-MM-DD') => (time ? Moment(time).format(format) : null); + +/** + * 格式化time为时间字符串 + * @param time 时间 + * @param format 格式化字符串 + * @returns 格式化时间字符串 + */ +export const formatDateTime = (time: MomentInput, format = 'YYYY-MM-DD HH:mm:ss') => formatDate(time, format); + +/** + * 根据秒转化为H 时 m 分 s 秒格式 + * @param sec 秒 + * @returns H 时 m 分 s 秒 + */ +export const secToHMS = (sec: number|string) => { + const time = Moment.duration(sec, 'seconds'); + const h = time.hours(); + const m = time.minutes(); + const s = time.seconds(); + const format = `${h ? 'H 时 ' : ''}${m ? 'm 分 ' : ''}s 秒`; + return Moment({ h, m, s }).format(format); +}; diff --git a/web/packages/shared/util/types.ts b/web/packages/shared/util/types.ts new file mode 100644 index 000000000..88b5551a5 --- /dev/null +++ b/web/packages/shared/util/types.ts @@ -0,0 +1,26 @@ +/** 筛选字段数据类型 */ +export type FilterFieldType = 'string' | 'number' | 'boolean' | 'time' | 'array_string' | 'array_number'; + +/** 筛选字段格式 */ +export interface FilterField { + /** 字段名称,唯一标识 */ + name: string; + /** 字段格式类型 */ + type: FilterFieldType +} + +/** 解析路由参数后字段值类型 */ +export type URLSearchValue = string; + +/** 路由字段结构 */ +export interface URLSearch { + [name: string]: URLSearchValue +} + +/** 解析筛选字段值类型 */ +export type FilterValue = URLSearchValue | URLSearchValue[] | number | number[]; + +/** 筛选字段结构 */ +export interface Filter { + [name: string]: FilterValue +} diff --git a/web/packages/shared/util/window.ts b/web/packages/shared/util/window.ts new file mode 100644 index 000000000..26d9237d6 --- /dev/null +++ b/web/packages/shared/util/window.ts @@ -0,0 +1,60 @@ +/** + * utils - window窗口操作工具库 + */ + +/** + * 计算窗口居中弹出位置 + * @params width 窗口宽度 + * @params height 窗口高度 + */ +export const getOpenWindowStyle = (width = 800, height = 800) => { + const top = window.innerHeight > height ? (window.innerHeight - height) / 2 : 0; + const left = window.innerWidth > width ? (window.innerWidth - width) / 2 : 0; + return `top=${top},left=${left},width=${width},height=${height}`; +}; + +/** + * 弹出窗口 + * @params url 弹窗地址 + * @params name 弹窗名称 + * @params width 窗口宽度 + * @params height 窗口高度 + */ +export const openWindow = ( + /** 要在新打开的窗口中加载的 URL */ + url: string, + /** 新窗口的名称 */ + name: string, + /** 新窗口的宽度 */ + width?: number, + /** 新窗口的高度 */ + height?: number, +) => window.open(url, name, getOpenWindowStyle(width, height)); + +/** 跨页面通信消息状态码 */ +export enum PostMessageCode { + SUCCESS = 200, + FAIL = 500, +} + +/** 跨页面通信消息类型 */ +export enum PostMessageType { + GIT_OAUTH = 'GitOAuth', + TAPD_OAUTH = 'TapdOAuth' +} + +/** 跨页面通信消息数据结构 */ +export interface PostMessageData { + code: PostMessageCode; + type: PostMessageType; + data?: any; +} + +/** + * 跨页面通信 + * @params target 通信对象窗口 + * @params msg 通信消息 + */ +export const postMessageToTarget = (target: Window, msg: PostMessageData) => { + target.postMessage(msg, target.location.origin); +}; diff --git a/web/packages/tca-analysis/package.json b/web/packages/tca-analysis/package.json index f42abf11c..afaa4ae82 100755 --- a/web/packages/tca-analysis/package.json +++ b/web/packages/tca-analysis/package.json @@ -11,7 +11,9 @@ "scripts": { "dev": "NODE_ENV=development webpack server --config ./scripts/webpack.dev.js --progress --color", "build": "NODE_ENV=production webpack --config ./scripts/webpack.prod.js --progress --color", - "analyzer": "webpack-bundle-analyzer --port 8888 ./dist/stats.json" + "analyzer": "webpack-bundle-analyzer --port 8888 ./dist/stats.json", + "lint": "eslint --ext .js,.jsx,.ts,.tsx src/", + "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx src/ --fix" }, "dependencies": { "@ant-design/plots": "^1.0.9", @@ -98,4 +100,4 @@ "webpack-merge": "^5.8.0", "webpackbar": "^5.0.0-3" } -} +} \ No newline at end of file diff --git a/web/packages/tca-analysis/src/components/node-tag/constants.ts b/web/packages/tca-analysis/src/components/node-tag/constants.ts new file mode 100644 index 000000000..4cb93bcc5 --- /dev/null +++ b/web/packages/tca-analysis/src/components/node-tag/constants.ts @@ -0,0 +1,17 @@ +export const TAG_TYPE_ENUM = { + PUBLIC: 1, + PRIVATE: 2, + DISABLED: 99 +}; + +export const TAG_TYPE_CHOICES = { + [TAG_TYPE_ENUM.PUBLIC]: '公共', + [TAG_TYPE_ENUM.PRIVATE]: '团队', + [TAG_TYPE_ENUM.DISABLED]: '停用', +}; + +export const TAG_TYPE_COLOR = { + [TAG_TYPE_ENUM.PUBLIC]: '#2db7f5', + [TAG_TYPE_ENUM.PRIVATE]: '#108ee9', + [TAG_TYPE_ENUM.DISABLED]: 'default', +}; diff --git a/web/packages/tca-analysis/src/components/node-tag/index.tsx b/web/packages/tca-analysis/src/components/node-tag/index.tsx new file mode 100644 index 000000000..53dc92846 --- /dev/null +++ b/web/packages/tca-analysis/src/components/node-tag/index.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { Form } from 'coding-oa-uikit'; +import { ColProps } from 'coding-oa-uikit/lib/grid'; +import { Rule } from 'coding-oa-uikit/lib/form'; + +import TagSelect, { TagSelectProps } from './select'; + +interface NodeTagProps extends TagSelectProps { + /** Form.Item name */ + name: string; + /** Form.Item label */ + label: string | React.ReactNode + /** Form.Item 布局样式 */ + labelCol?: ColProps; + /** Form.Item 布局样式 */ + wrapperCol?: ColProps; + /** Form.Item 校验规则 */ + rules?: Rule[]; +} + +/** 运行环境表单项 */ +const NodeTag = ({ + label, name, labelCol, wrapperCol, rules, ...other +}: NodeTagProps) => + + ; + +NodeTag.Select = TagSelect; + +export default NodeTag; diff --git a/web/packages/tca-analysis/src/components/node-tag/select.tsx b/web/packages/tca-analysis/src/components/node-tag/select.tsx new file mode 100644 index 000000000..c80f92cba --- /dev/null +++ b/web/packages/tca-analysis/src/components/node-tag/select.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { isEmpty } from 'lodash'; +import { Select, Tag } from 'coding-oa-uikit'; +import { SelectProps, SelectValue } from 'coding-oa-uikit/lib/select'; + +import { TAG_TYPE_ENUM, TAG_TYPE_CHOICES } from './constants'; + +import style from './style.scss'; + +const { Option } = Select; + +export interface TagSelectProps extends SelectProps { + tags: any[]; +} + +const formatTypeTag = (tagType: number) => { + switch (tagType) { + case TAG_TYPE_ENUM.PUBLIC: + return {TAG_TYPE_CHOICES[tagType]}; + case TAG_TYPE_ENUM.PRIVATE: + return {TAG_TYPE_CHOICES[tagType]}; + case TAG_TYPE_ENUM.DISABLED: + return {TAG_TYPE_CHOICES[tagType]}; + default: + return {TAG_TYPE_CHOICES[TAG_TYPE_ENUM.PUBLIC]}; + } +} + +/** + * 运行环境选框 + */ +const TagSelect = (props: TagSelectProps) => { + const { tags, ...other } = props; + + return ( + + ); +}; + +export default TagSelect; + \ No newline at end of file diff --git a/web/packages/tca-analysis/src/components/node-tag/style.scss b/web/packages/tca-analysis/src/components/node-tag/style.scss new file mode 100644 index 000000000..adea880f3 --- /dev/null +++ b/web/packages/tca-analysis/src/components/node-tag/style.scss @@ -0,0 +1,21 @@ +@import "@src/common/style/color.scss"; + +.tag-option { + display: flex; + justify-content: space-between; + align-items: center; + + .private-tag { + color: $cyan-6; + border: 1px solid $cyan-6; + background-color: #fff; + border-radius: 2px; + } + + .public-tag { + color: $blue-6; + border: 1px solid $blue-6; + background-color: #fff; + border-radius: 2px; + } +} diff --git a/web/packages/tca-analysis/src/context/store.tsx b/web/packages/tca-analysis/src/context/store.tsx index 2564a4d70..3fc498e96 100644 --- a/web/packages/tca-analysis/src/context/store.tsx +++ b/web/packages/tca-analysis/src/context/store.tsx @@ -89,13 +89,12 @@ const reducer = (state: StateProps, action: ActionProps) => { }; const StoreProvider = ({ children }: { children: any }) => { - // @ts-ignore const [state, dispatch] = useReducer(reducer, initialState); return ( - - {children} - + + {children} + ); }; diff --git a/web/packages/tca-analysis/src/index.tsx b/web/packages/tca-analysis/src/index.tsx index d931a0ea5..0e053d9e1 100644 --- a/web/packages/tca-analysis/src/index.tsx +++ b/web/packages/tca-analysis/src/index.tsx @@ -42,7 +42,7 @@ const create = (rootDom: HTMLElement) => { } }; -const bootstrap = () => { }; +const bootstrap = () => { console.log('') }; const mount = (props: any) => { const { rootDom, store } = props; @@ -54,7 +54,6 @@ const mount = (props: any) => { (node ? node.parentNode : document.body)} > diff --git a/web/packages/tca-analysis/src/modules/project-team/overview/index.tsx b/web/packages/tca-analysis/src/modules/project-team/overview/index.tsx index 9f00b7832..9245418b0 100644 --- a/web/packages/tca-analysis/src/modules/project-team/overview/index.tsx +++ b/web/packages/tca-analysis/src/modules/project-team/overview/index.tsx @@ -14,8 +14,9 @@ import { useSelector } from 'react-redux'; import { getProjectListRouter, getProjectOverviewRouter } from '@src/utils/getRoutePath'; import { t } from '@src/i18n/i18next'; import { formatDateTime, getUserName } from '@src/utils'; -import { getProjectTeam, putProjectTeam, disableProject} from '@src/services/common'; +import { getProjectTeam, putProjectTeam, disableProject } from '@src/services/common'; import DeleteModal from '@src/components/delete-modal'; +import Copy from '@src/components/copy' const layout = { labelCol: { span: 6 }, @@ -70,7 +71,7 @@ const Overview = () => { }, [orgSid, teamName]); const handleDeleteTeam = () => { - disableProject(orgSid, teamName, {status: 2}).then(() => { + disableProject(orgSid, teamName, { status: 2 }).then(() => { message.success('项目已禁用'); history.push(getProjectListRouter(orgSid)); }).finally(() => setDeleteVisible(false)); @@ -103,7 +104,7 @@ const Overview = () => { label={t('项目唯一标识')} name="name" > - {team.name} +
{team.name}
{ .padding(3) (d3.hierarchy(data) .sum((d: any) => Number(d.size)) - // @ts-ignore - .sort((a, b) => Number(b.size) - Number(a.size))); + .sort((a: any, b: any) => Number(b.size) - Number(a.size))); getCircle = (data: any) => { @@ -56,9 +55,9 @@ class CirclePacking extends Component { .attr('viewBox', `-${width / 2} -${height / 2} ${width} ${height}`) .style('display', 'block') .style('margin', '-14px') - // .style("background", color(0)) + // .style("background", color(0)) .style('cursor', 'pointer') - // .attr("transform", "translate(" + 10 + "," + 10 + ")") + // .attr("transform", "translate(" + 10 + "," + 10 + ")") .on('click', () => zoom(root)); const node = svg.append('g') @@ -68,11 +67,9 @@ class CirclePacking extends Component { .attr('fill', (d: any) => (d.children ? color(d.depth) : '#3D98FF')) .style('fill-opacity', (d: any) => get(d, 'data.weight', 1)) .attr('pointer-events', (d: any) => (!d.children ? 'none' : null)) - // @ts-ignore .on('mouseover', function () { d3.select(this).attr('stroke', '#6FA2A9'); }) - // @ts-ignore .on('mouseout', function () { d3.select(this).attr('stroke', null); }) @@ -102,7 +99,6 @@ class CirclePacking extends Component { } const zoom = (d: any) => { - // @ts-ignore const focus0 = focus; focus = d; @@ -115,18 +111,15 @@ class CirclePacking extends Component { }); label - // @ts-ignore .filter(function (d: any) { return d.parent === focus || this.style.display === 'inline'; }) .transition(transition) .style('fill-opacity', (d: any) => (d.parent === focus ? 1 : 0)) - // @ts-ignore - .on('start', function (d) { + .on('start', function (d: any) { if (d.parent === focus) this.style.display = 'inline'; }) - // @ts-ignore - .on('end', function (d) { + .on('end', function (d: any) { if (d.parent !== focus) this.style.display = 'none'; }); }; @@ -188,12 +181,12 @@ class CirclePacking extends Component { render() { return ( -
- - 请点击上传或者拖拽原始数据文件到这里 - -
-
+
+ + 请点击上传或者拖拽原始数据文件到这里 + +
+
); } } diff --git a/web/packages/tca-analysis/src/modules/projects/issues/issue-detail-modal.tsx b/web/packages/tca-analysis/src/modules/projects/issues/issue-detail-modal.tsx index 5bab76efc..e91c3dcc3 100644 --- a/web/packages/tca-analysis/src/modules/projects/issues/issue-detail-modal.tsx +++ b/web/packages/tca-analysis/src/modules/projects/issues/issue-detail-modal.tsx @@ -101,7 +101,7 @@ const IssueModal = (props: IssueModalProps) => { }; const rowRenderer = ({ index, style: rowStyle }: any) => { - const { lineNum: line, content } = codeFile?.codeContents[index]; + const { lineNum: line, content } = codeFile?.codeContents[index] || {}; const rowRef: any = useRef({}); const language = detail.language ?? codeFile.suffix?.split('.')[1] ?? 'plaintext'; @@ -230,19 +230,19 @@ const IssueModal = (props: IssueModalProps) => { {loading ? ( ) : ( - - {({ height, width }: any) => ( - - {rowRenderer} - - )} - + + {({ height, width }: any) => ( + + {rowRenderer} + + )} + )} diff --git a/web/packages/tca-analysis/src/modules/projects/issues/issue-popover.tsx b/web/packages/tca-analysis/src/modules/projects/issues/issue-popover.tsx index 6dced52f3..2c017fb19 100644 --- a/web/packages/tca-analysis/src/modules/projects/issues/issue-popover.tsx +++ b/web/packages/tca-analysis/src/modules/projects/issues/issue-popover.tsx @@ -54,7 +54,6 @@ export const Operation = ({ }; const updateSeverity = () => { - // @ts-ignore updateIssueSeverity(...params, detail.id, severity).then(() => { message.success('严重级别修改成功'); successCallback({ severity }); @@ -200,36 +199,36 @@ export const Operation = ({ isHandled ? (

将问题重新标记为【未处理】状态

) : ( - <> -

请选择处理问题方式,提交后问题状态将置为已处理

- { - if (e.target.value === 1) { - setScope(1); - } - setType(e.target.value); - }} - > - 已修复 - 无需修复 - 误报 - - { - (ignoreType === 2 || ignoreType === 3) && ( -

- { - setScope(checked ? 2 : 1); - }} - /> 全局忽略 - -

- ) - } + <> +

请选择处理问题方式,提交后问题状态将置为已处理

+ { + if (e.target.value === 1) { + setScope(1); + } + setType(e.target.value); + }} + > + 已修复 + 无需修复 + 误报 + + { + (ignoreType === 2 || ignoreType === 3) && ( +

+ { + setScope(checked ? 2 : 1); + }} + /> 全局忽略 + +

+ ) + } - + ) } > @@ -247,30 +246,30 @@ export const Operation = ({ * 简单封装 Popover 组件 */ const IssuePopover = ({ title, visible, children, content, onOk, onCancel, onCancelText, showFooter = true }: any) => ( - - {content} - { - showFooter && ( -
- { - onOk && - } - { - onCancel && - } -
- ) - } - - } - > - {children} -
+ + {content} + { + showFooter && ( +
+ { + onOk && + } + { + onCancel && + } +
+ ) + } + + } + > + {children} +
); diff --git a/web/packages/tca-analysis/src/modules/projects/metric/ccfiles/detail.tsx b/web/packages/tca-analysis/src/modules/projects/metric/ccfiles/detail.tsx index 7aad6f75f..e5f8ed3db 100644 --- a/web/packages/tca-analysis/src/modules/projects/metric/ccfiles/detail.tsx +++ b/web/packages/tca-analysis/src/modules/projects/metric/ccfiles/detail.tsx @@ -109,7 +109,7 @@ const Detail = () => { // 渲染每行显示 const rowRenderer = ({ index, style: rowStyle }: any) => { - const { lineNum: line, content } = codeFile?.codeContents[index]; + const { lineNum: line, content } = codeFile?.codeContents[index] || {}; const rowRef: any = useRef({}); const language = fileInfo.language ?? codeFile.suffix?.split('.')[1] ?? 'plaintext'; diff --git a/web/packages/tca-analysis/src/modules/projects/metric/ccissues/issue-modal.tsx b/web/packages/tca-analysis/src/modules/projects/metric/ccissues/issue-modal.tsx index 3325408e7..f58574d67 100644 --- a/web/packages/tca-analysis/src/modules/projects/metric/ccissues/issue-modal.tsx +++ b/web/packages/tca-analysis/src/modules/projects/metric/ccissues/issue-modal.tsx @@ -85,7 +85,7 @@ const IssueModal = (props: IssueModalProps) => { }; const rowRenderer = ({ index, style: rowStyle }: any) => { - const { lineNum: line, content } = codeFile?.codeContents[index]; + const { lineNum: line, content } = codeFile?.codeContents[index] || {}; const rowRef: any = useRef({}); const language = detail.language ?? codeFile.suffix?.split('.')[1] ?? 'plaintext'; @@ -181,19 +181,19 @@ const IssueModal = (props: IssueModalProps) => { {loading ? ( ) : ( - - {({ height, width }: any) => ( - - {rowRenderer} - - )} - + + {({ height, width }: any) => ( + + {rowRenderer} + + )} + )} diff --git a/web/packages/tca-analysis/src/modules/projects/project/first-modal.tsx b/web/packages/tca-analysis/src/modules/projects/project/first-modal.tsx index 06fca6b82..9c79c869c 100644 --- a/web/packages/tca-analysis/src/modules/projects/project/first-modal.tsx +++ b/web/packages/tca-analysis/src/modules/projects/project/first-modal.tsx @@ -17,6 +17,7 @@ import { Modal, Form, Input, Select, Radio, Row, Col, Checkbox, message, Tag } f import { getProjectRouter } from '@src/utils/getRoutePath'; import { getLanguages, getTags } from '@src/services/schemes'; import { initRepos } from '@src/services/projects'; +import NodeTag from '@src/components/node-tag'; import { SCAN_LIST } from '../../schemes/constants'; import style from '../style.scss'; @@ -49,7 +50,7 @@ const FirstModal = (props: FirstModalProps) => { useEffect(() => { (async () => { - setTags(get(await getTags(), 'results', [])); + setTags(get(await getTags(orgSid), 'results', [])); setLanguages(get(await getLanguages(), 'results', [])); })(); }, []); @@ -165,27 +166,12 @@ const FirstModal = (props: FirstModalProps) => { ))}
- - - - {tags.map((item: any) => item.public && ( -
- - {item.name} - - - ))} - - - + tags={tags} + /> { ))} - - - - {tags.map(item => item.public && ( - - - {item.name} - - - ))} - - - + tags={tags} + /> diff --git a/web/packages/tca-analysis/src/modules/schemes/code-metrics/index.tsx b/web/packages/tca-analysis/src/modules/schemes/code-metrics/index.tsx index 4bd404953..c235429ec 100644 --- a/web/packages/tca-analysis/src/modules/schemes/code-metrics/index.tsx +++ b/web/packages/tca-analysis/src/modules/schemes/code-metrics/index.tsx @@ -21,57 +21,56 @@ import style from './style.scss'; import formStyle from '../style.scss'; const defaultValues = { - min_ccn: 20, - dup_block_length_min: 120, - dup_min_dup_times: 2, - dup_issue_limit: 1000, + min_ccn: 20, + dup_block_length_min: 120, + dup_min_dup_times: 2, + dup_issue_limit: 1000, }; const layout = { - labelCol: { span: 5 }, - wrapperCol: { span: 19 }, - colon: false, + labelCol: { span: 5 }, + wrapperCol: { span: 19 }, }; interface CodeMetricsProps { - repoId: number; - schemeId: number; - orgSid: string; - teamName: string; + repoId: number; + schemeId: number; + orgSid: string; + teamName: string; } const CodeMetrics = (props: CodeMetricsProps) => { - const { orgSid, teamName, repoId, schemeId } = props; - const [form] = Form.useForm(); - const [data, setData] = useState({}); - - useEffect(() => { - getCodeMetrics(orgSid, teamName, repoId, schemeId).then((response: any) => { - setData(response); - form.resetFields(); - }); - }, [schemeId]); - - const update = (formData: any, info: string) => { - updateCodeMetrics(orgSid, teamName, repoId, schemeId, { - ...data, - ...formData, - }).then((response: any) => { - message.success(`${info}成功`); - setData(response); - form.resetFields(); - }); - }; - - const onFinish = (formData: any) => { - update({ - ...formData, - ...defaultValues, - ...pickBy(formData, key => isNumber(key)), - }, '更新'); - }; - - return ( + const { orgSid, teamName, repoId, schemeId } = props; + const [form] = Form.useForm(); + const [data, setData] = useState({}); + + useEffect(() => { + getCodeMetrics(orgSid, teamName, repoId, schemeId).then((response: any) => { + setData(response); + form.resetFields(); + }); + }, [schemeId]); + + const update = (formData: any, info: string) => { + updateCodeMetrics(orgSid, teamName, repoId, schemeId, { + ...data, + ...formData, + }).then((response: any) => { + message.success(`${info}成功`); + setData(response); + form.resetFields(); + }); + }; + + const onFinish = (formData: any) => { + update({ + ...formData, + ...defaultValues, + ...pickBy(formData, key => isNumber(key)), + }, '更新'); + }; + + return (
{

圈复杂度 document.getElementById('container')} title='可以发现执行路径较多的方法,降低代码的圈复杂度,可测性更高。支持C、C++、Java、C#、JavaScript、Python、Objective-C、Ruby、PHP、Swift、Scala、Go、Lua共13种语言' > @@ -94,7 +92,7 @@ const CodeMetrics = (props: CodeMetricsProps) => { size='small' checked={data.cc_scan_enabled} onChange={(checked: boolean) => update({ - cc_scan_enabled: checked, + cc_scan_enabled: checked, }, `圈复杂度${checked ? '开启' : '关闭'}`)} /> @@ -108,7 +106,6 @@ const CodeMetrics = (props: CodeMetricsProps) => { 检测阈值 document.getElementById('container')} title='仅上报圈复杂度超过该阈值的方法,默认20' > @@ -117,7 +114,7 @@ const CodeMetrics = (props: CodeMetricsProps) => { } > - + ) } @@ -125,7 +122,6 @@ const CodeMetrics = (props: CodeMetricsProps) => {

重复代码 document.getElementById('container')} title='可以发现重复的代码,避免重复代码可以让代码更简洁,更易维护。支持C、C++、Java、JavaScript、Objective-C、PHP、Python、C#、Ruby、Kotlin、Go、Lua、Swift、Scala共14种语言' > @@ -138,7 +134,7 @@ const CodeMetrics = (props: CodeMetricsProps) => { size='small' checked={data.dup_scan_enabled} onChange={(checked: boolean) => update({ - dup_scan_enabled: checked, + dup_scan_enabled: checked, }, `重复代码${checked ? '开启' : '关闭'}`)} /> @@ -151,7 +147,6 @@ const CodeMetrics = (props: CodeMetricsProps) => { 长度区间 document.getElementById('container')} title='一个单词(变量或操作符)记为1' > @@ -180,7 +175,6 @@ const CodeMetrics = (props: CodeMetricsProps) => { 重复次数 document.getElementById('container')} title='当一段代码重复次数达到指定区间才认为是有风险的' > @@ -210,7 +204,6 @@ const CodeMetrics = (props: CodeMetricsProps) => { 上报限制 document.getElementById('container')} title='限制上报的重复代码块数,可以减少开发的压力,提高修复积极性' > @@ -219,7 +212,7 @@ const CodeMetrics = (props: CodeMetricsProps) => { } > - + ) @@ -228,7 +221,6 @@ const CodeMetrics = (props: CodeMetricsProps) => {

代码统计 document.getElementById('container')} title='从目录和业务纬度统计代码行数,也可以获取提交记录便于代码Review。' > @@ -240,7 +232,7 @@ const CodeMetrics = (props: CodeMetricsProps) => { size='small' checked={data.cloc_scan_enabled} onChange={(checked: boolean) => update({ - cloc_scan_enabled: checked, + cloc_scan_enabled: checked, }, `代码统计${checked ? '开启' : '关闭'}`)} /> @@ -250,7 +242,7 @@ const CodeMetrics = (props: CodeMetricsProps) => { - ); + ); }; export default CodeMetrics; diff --git a/web/packages/tca-analysis/src/modules/schemes/create-scheme.tsx b/web/packages/tca-analysis/src/modules/schemes/create-scheme.tsx index 958385e34..6bf37e94b 100644 --- a/web/packages/tca-analysis/src/modules/schemes/create-scheme.tsx +++ b/web/packages/tca-analysis/src/modules/schemes/create-scheme.tsx @@ -17,6 +17,7 @@ import cn from 'classnames'; import { SCAN_LIST } from './constants'; import { createScheme, copyScheme } from '@src/services/schemes'; +import NodeTag from '@src/components/node-tag'; import style from './style.scss'; @@ -185,23 +186,12 @@ const CreatSchemeModal = (props: IProps) => { ))} - - - - {tags.map(item => item.public && ( -

- - {item.name} - - - ))} - - - + tags={tags} + /> { }, [curRepo.id]); const getCommonData = async () => { - setTags(get(await getTags(), 'results', [])); + setTags(get(await getTags(orgSid), 'results', [])); setLanguages(get(await getLanguages(), 'results', [])); setTemplates(get(await getTmplList(orgSid, { limit: 100 }), 'results', [])); }; diff --git a/web/packages/tca-analysis/src/modules/schemes/path-filter/branch.tsx b/web/packages/tca-analysis/src/modules/schemes/path-filter/branch.tsx index f0504f50d..21926cb99 100644 --- a/web/packages/tca-analysis/src/modules/schemes/path-filter/branch.tsx +++ b/web/packages/tca-analysis/src/modules/schemes/path-filter/branch.tsx @@ -49,7 +49,6 @@ const Branch = (props: IProps) => { 过滤对比分支引入的问题 document.body} title='常用于合流场景,对比MR目标分支的增量分析' > diff --git a/web/packages/tca-analysis/src/modules/schemes/path-filter/modal.tsx b/web/packages/tca-analysis/src/modules/schemes/path-filter/modal.tsx index f1ca39507..1fdeb1781 100644 --- a/web/packages/tca-analysis/src/modules/schemes/path-filter/modal.tsx +++ b/web/packages/tca-analysis/src/modules/schemes/path-filter/modal.tsx @@ -71,16 +71,15 @@ const UpdateModal = (props: IProps) => { 正则表达式 - document.getElementById('container')} title={
  • 请填写相对路径(基于代码库根目录),要求匹配到文件
  • 使用Unix通配符格式,示例如下: -
      +
      • 代码根目录
      • |-src
      • |- test
      • @@ -103,16 +102,15 @@ const UpdateModal = (props: IProps) => { 通配符 - document.getElementById('container')} title={
        • 请填写相对路径(基于代码库根目录),要求匹配到文件
        • 使用Unix通配符格式,示例如下: -
            +
            • 代码根目录
            • |-src
            • |- test
            • @@ -161,9 +159,8 @@ const UpdateModal = (props: IProps) => { include(包含) - document.getElementById('container')} title={`表示只分析,如只分析 src/ 目录:src/${getFieldValue('path_type') === 1 ? '' : '.'}*`} > @@ -172,9 +169,8 @@ const UpdateModal = (props: IProps) => { exclude(过滤) - document.getElementById('container')} title={`表示只屏蔽,如要屏蔽 src/lib/ 目录:src/lib/${getFieldValue('path_type') === 1 ? '' : '.'}*`} > diff --git a/web/packages/tca-analysis/src/modules/template/baseinfo/index.tsx b/web/packages/tca-analysis/src/modules/template/baseinfo/index.tsx index 2a8f3a199..5fa1bb7fa 100644 --- a/web/packages/tca-analysis/src/modules/template/baseinfo/index.tsx +++ b/web/packages/tca-analysis/src/modules/template/baseinfo/index.tsx @@ -14,15 +14,13 @@ import cn from 'classnames'; import { Form, Input, - Radio, - Row, - Col, Select, Button, message, } from 'coding-oa-uikit'; import { updateTmpl, syncScheme, getTmplLint, updateTmplLint } from '@src/services/template'; +import NodeTag from '@src/components/node-tag'; import SyncModal from '../sync-modal'; import style from './style.scss'; @@ -32,7 +30,6 @@ const { Option } = Select; const layout = { labelCol: { span: 6 }, wrapperCol: { span: 18 }, - colon: false, }; interface BaseConfigProps { @@ -133,30 +130,14 @@ const BaseConfig = (props: BaseConfigProps) => { ))} - - - - - {tags.map(item => item.public && ( -
- - {item.name} - - - ))} - - - - + tags={tags} + disabled={isSysTmpl} + /> diff --git a/web/packages/tca-analysis/src/modules/template/code-metrics/index.tsx b/web/packages/tca-analysis/src/modules/template/code-metrics/index.tsx index 40d99d04d..5d16e0cc7 100644 --- a/web/packages/tca-analysis/src/modules/template/code-metrics/index.tsx +++ b/web/packages/tca-analysis/src/modules/template/code-metrics/index.tsx @@ -21,83 +21,82 @@ import style from './style.scss'; import formStyle from '../style.scss'; const defaultValues = { - min_ccn: 20, - dup_block_length_min: 120, - dup_min_dup_times: 2, - dup_issue_limit: 1000, + min_ccn: 20, + dup_block_length_min: 120, + dup_min_dup_times: 2, + dup_issue_limit: 1000, }; const layout = { - labelCol: { span: 5 }, - wrapperCol: { span: 19 }, - colon: false, + labelCol: { span: 5 }, + wrapperCol: { span: 19 }, }; interface CodeMetricsProps { - orgSid: string; - tmplId: number; - isSysTmpl: boolean; + orgSid: string; + tmplId: number; + isSysTmpl: boolean; } const CodeMetrics = (props: CodeMetricsProps) => { - const { orgSid, tmplId, isSysTmpl } = props; - const [form] = Form.useForm(); - const [data, setData] = useState({}); - const [visible, setVisible] = useState(false); - const [formData, setFormData] = useState({}); + const { orgSid, tmplId, isSysTmpl } = props; + const [form] = Form.useForm(); + const [data, setData] = useState({}); + const [visible, setVisible] = useState(false); + const [formData, setFormData] = useState({}); - useEffect(() => { - getTmplMetrics(orgSid, tmplId).then((response: any) => { - setData(response); - form.resetFields(); - }); - }, [tmplId]); + useEffect(() => { + getTmplMetrics(orgSid, tmplId).then((response: any) => { + setData(response); + form.resetFields(); + }); + }, [tmplId]); - const update = (formData: any, info: string, callback?: any) => { - updateTmplMetrics(orgSid, tmplId, { - ...data, - ...formData, - }).then((response: any) => { - setData(response); - form.resetFields(); + const update = (formData: any, info: string, callback?: any) => { + updateTmplMetrics(orgSid, tmplId, { + ...data, + ...formData, + }).then((response: any) => { + setData(response); + form.resetFields(); - if (callback) { - callback(); - } else { - message.success(`${info}成功`); - } - }); - }; + if (callback) { + callback(); + } else { + message.success(`${info}成功`); + } + }); + }; - const onFinish = (data: any) => { - setVisible(true); - setFormData(data); - }; + const onFinish = (data: any) => { + setVisible(true); + setFormData(data); + }; - const onSync = (keys: any) => { - update({ - ...formData, - ...defaultValues, - ...pickBy(formData, key => isNumber(key)), - }, '更新', () => { - if (keys.length === 0) { - setVisible(false); - message.success('更新成功'); - } - }); + const onSync = (keys: any) => { + update({ + ...formData, + ...defaultValues, + ...pickBy(formData, key => isNumber(key)), + }, '更新', () => { + if (keys.length === 0) { + setVisible(false); + message.success('更新成功'); + } + }); - if (keys.length > 0) { - syncScheme(orgSid, tmplId, { - sync_metric_conf: true, - schemes: keys, - }).then(() => { - message.success('同步成功'); - setVisible(false); - }); - } - }; + if (keys.length > 0) { + syncScheme(orgSid, tmplId, { + sync_metric_conf: true, + schemes: keys, + }).then(() => { + message.success('同步成功'); + setVisible(false); + }); + } + }; - return ( + return ( <>
{ >

圈复杂度 - document.getElementById('container')} title='可以发现执行路径较多的方法,降低代码的圈复杂度,可测性更高。支持C、C++、Java、C#、JavaScript、Python、Objective-C、Ruby、PHP、Swift、Scala、Go、Lua共13种语言' > @@ -122,7 +120,7 @@ const CodeMetrics = (props: CodeMetricsProps) => { disabled={isSysTmpl} checked={data.cc_scan_enabled} onChange={(checked: boolean) => update({ - cc_scan_enabled: checked, + cc_scan_enabled: checked, }, `圈复杂度${checked ? '开启' : '关闭'}`)} /> @@ -135,8 +133,7 @@ const CodeMetrics = (props: CodeMetricsProps) => { label={ 检测阈值 - document.getElementById('container')} title='仅上报圈复杂度超过该阈值的方法,默认20' > @@ -152,8 +149,7 @@ const CodeMetrics = (props: CodeMetricsProps) => {

重复代码 - document.getElementById('container')} title='可以发现重复的代码,避免重复代码可以让代码更简洁,更易维护。支持C、C++、Java、JavaScript、Objective-C、PHP、Python、C#、Ruby、Kotlin、Go、Lua、Swift、Scala共14种语言' > @@ -167,7 +163,7 @@ const CodeMetrics = (props: CodeMetricsProps) => { size='small' checked={data.dup_scan_enabled} onChange={(checked: boolean) => update({ - dup_scan_enabled: checked, + dup_scan_enabled: checked, }, `重复代码${checked ? '开启' : '关闭'}`)} /> @@ -179,8 +175,7 @@ const CodeMetrics = (props: CodeMetricsProps) => { label={ 长度区间 - document.getElementById('container')} title='一个单词(变量或操作符)记为1' > @@ -210,8 +205,7 @@ const CodeMetrics = (props: CodeMetricsProps) => { label={ 重复次数 - document.getElementById('container')} title='当一段代码重复次数达到指定区间才认为是有风险的' > @@ -242,8 +236,7 @@ const CodeMetrics = (props: CodeMetricsProps) => { label={ 上报限制 - document.getElementById('container')} title='限制上报的重复代码块数,可以减少开发的压力,提高修复积极性' > @@ -260,8 +253,7 @@ const CodeMetrics = (props: CodeMetricsProps) => {

代码统计 - document.getElementById('container')} title='从目录和业务纬度统计代码行数,也可以获取提交记录便于代码Review。' > @@ -274,7 +266,7 @@ const CodeMetrics = (props: CodeMetricsProps) => { size='small' checked={data.cloc_scan_enabled} onChange={(checked: boolean) => update({ - cloc_scan_enabled: checked, + cloc_scan_enabled: checked, }, `代码统计${checked ? '开启' : '关闭'}`)} /> @@ -298,7 +290,7 @@ const CodeMetrics = (props: CodeMetricsProps) => { ) } - ); + ); }; export default CodeMetrics; diff --git a/web/packages/tca-analysis/src/modules/template/create.tsx b/web/packages/tca-analysis/src/modules/template/create.tsx index 66b5d0b52..9df7118ad 100644 --- a/web/packages/tca-analysis/src/modules/template/create.tsx +++ b/web/packages/tca-analysis/src/modules/template/create.tsx @@ -11,10 +11,11 @@ import React from 'react'; import { useHistory } from 'react-router-dom'; import { pick, trim } from 'lodash'; -import { Form, Input, Checkbox, Row, Col, Select, message, Modal, Radio } from 'coding-oa-uikit'; +import { Form, Input, Checkbox, Row, Col, Select, message, Modal } from 'coding-oa-uikit'; import { SCAN_LIST } from '../schemes/constants'; import { createTmpl } from '@src/services/template'; +import NodeTag from '@src/components/node-tag'; import style from './style.scss'; @@ -100,27 +101,12 @@ const CreatSchemeModal = (props: IProps) => { ))} - - - - {tags.map(item => item.public && ( -

- - {item.name} - - - ))} - - - - + tags={tags} + /> { useEffect(() => { (async () => { - setTags(get(await getTags(), 'results', [])); + setTags(get(await getTags(orgSid), 'results', [])); setLanguages(get(await getLanguages(), 'results', [])); })(); }, []); diff --git a/web/packages/tca-analysis/src/modules/template/index.tsx b/web/packages/tca-analysis/src/modules/template/index.tsx index c4f06451e..ffd4958fc 100644 --- a/web/packages/tca-analysis/src/modules/template/index.tsx +++ b/web/packages/tca-analysis/src/modules/template/index.tsx @@ -45,7 +45,7 @@ const Template = () => { getListData(); (async () => { - setTags(get(await getTags(), 'results', [])); + setTags(get(await getTags(orgSid), 'results', [])); setLanguages(get(await getLanguages(), 'results', [])); })(); }, []); diff --git a/web/packages/tca-analysis/src/modules/template/path-filter/branch.tsx b/web/packages/tca-analysis/src/modules/template/path-filter/branch.tsx index f0504f50d..21926cb99 100644 --- a/web/packages/tca-analysis/src/modules/template/path-filter/branch.tsx +++ b/web/packages/tca-analysis/src/modules/template/path-filter/branch.tsx @@ -49,7 +49,6 @@ const Branch = (props: IProps) => { 过滤对比分支引入的问题 document.body} title='常用于合流场景,对比MR目标分支的增量分析' > diff --git a/web/packages/tca-analysis/src/modules/template/path-filter/modal.tsx b/web/packages/tca-analysis/src/modules/template/path-filter/modal.tsx index 72d570a3a..09049e5f6 100644 --- a/web/packages/tca-analysis/src/modules/template/path-filter/modal.tsx +++ b/web/packages/tca-analysis/src/modules/template/path-filter/modal.tsx @@ -68,16 +68,15 @@ const UpdateModal = (props: IProps) => { 正则表达式 - document.getElementById('container')} title={
  • 请填写相对路径(基于代码库根目录),要求匹配到文件
  • 使用Unix通配符格式,示例如下: -
      +
      • 代码根目录
      • |-src
      • |- test
      • @@ -100,16 +99,15 @@ const UpdateModal = (props: IProps) => { 通配符 - document.getElementById('container')} title={
        • 请填写相对路径(基于代码库根目录),要求匹配到文件
        • 使用Unix通配符格式,示例如下: -
            +
            • 代码根目录
            • |-src
            • |- test
            • @@ -155,9 +153,8 @@ const UpdateModal = (props: IProps) => { include(包含) - document.getElementById('container')} title={`表示只分析,如只分析 src/ 目录:src/${getFieldValue('path_type') === 1 ? '' : '.'}*`} > @@ -166,9 +163,8 @@ const UpdateModal = (props: IProps) => { exclude(过滤) - document.getElementById('container')} title={`表示只屏蔽,如要屏蔽 src/lib/ 目录:src/lib/${getFieldValue('path_type') === 1 ? '' : '.'}*`} > diff --git a/web/packages/tca-analysis/src/modules/template/permission/index.tsx b/web/packages/tca-analysis/src/modules/template/permission/index.tsx index a5dd0ce2e..83281bc1a 100644 --- a/web/packages/tca-analysis/src/modules/template/permission/index.tsx +++ b/web/packages/tca-analysis/src/modules/template/permission/index.tsx @@ -23,7 +23,6 @@ import style from './style.scss'; const layout = { labelCol: { span: 6 }, wrapperCol: { span: 18 }, - colon: false, }; interface PermissionProps { @@ -66,99 +65,95 @@ const Permission = (props: PermissionProps) => { }; return ( - item.username), - execute_managers_list: data.execute_managers?.map((item: any) => item.username), - }} - onFinish={onFinish} - > - - - - - 管理员 - document.getElementById('main-container')} - title="拥有模板编辑权限" - > - - - - } + item.username), + execute_managers_list: data.execute_managers?.map((item: any) => item.username), + }} + onFinish={onFinish} + > + + + + + 管理员 + document.getElementById('main-container')} + title="拥有模板编辑权限" > - document.getElementById('main-container')} + options={teamMembers.map((item: any) => ({ + label: item.nickname, + value: item.username, + }))} + /> + - - 普通成员 - document.getElementById('main-container')} - title="可使用模板" - > - - - - } + + 普通成员 + document.getElementById('main-container')} + title="可使用模板" > - document.getElementById('main-container')} + options={teamMembers.map((item: any) => ({ + label: item.nickname, + value: item.username, + }))} + /> + + {!isSysTmpl && ( + + + + + )} + ); }; diff --git a/web/packages/tca-analysis/src/services/schemes.ts b/web/packages/tca-analysis/src/services/schemes.ts index 3fab0aa81..cc10ff2b8 100644 --- a/web/packages/tca-analysis/src/services/schemes.ts +++ b/web/packages/tca-analysis/src/services/schemes.ts @@ -35,7 +35,7 @@ export const getLabels = () => get(`${MAIN_SERVER_API}/labels/?limit=100`); /** * 获取运行环境列表 */ -export const getTags = () => get(`${MAIN_SERVER_API}/tags/?limit=50`); +export const getTags = (orgId: string, params: any = null) => get(`${MAIN_SERVER_API}/orgs/${orgId}/nodes/tags/`, params); /** * 拉取模板配置 diff --git a/web/packages/tca-analysis/src/utils/fetch.ts b/web/packages/tca-analysis/src/utils/fetch.ts index ce5612553..fd4dacaff 100644 --- a/web/packages/tca-analysis/src/utils/fetch.ts +++ b/web/packages/tca-analysis/src/utils/fetch.ts @@ -43,7 +43,6 @@ export default function fetch(url: string, options: any, absolutePath = false) { }; } - // @ts-ignore options.headers = appendXSRFTokenHeader(headers); return window.fetch(`${absolutePath ? '' : window.location.origin}${url}`, options); diff --git a/web/packages/tca-document/.vuepress/config.ts b/web/packages/tca-document/.vuepress/config.ts index 9d6e54bb0..8f7c6f178 100644 --- a/web/packages/tca-document/.vuepress/config.ts +++ b/web/packages/tca-document/.vuepress/config.ts @@ -30,7 +30,7 @@ export default defineUserConfig({ markdown: { toc: { - level: [2,3,4], + level: [2, 3, 4], } }, @@ -54,7 +54,7 @@ export default defineUserConfig({ repo: 'https://github.com/Tencent/CodeAnalysis', - docsDir: 'web/packages/tca-document', + docsDir: 'doc', // theme-level locales config locales: { @@ -73,6 +73,7 @@ export default defineUserConfig({ // sidebar sidebar: sidebar.en, + sidebarDepth: 1, // page meta editLinkText: 'Edit this page on GitHub', @@ -128,4 +129,4 @@ export default defineUserConfig({ }, }), -}) \ No newline at end of file +}) diff --git a/web/packages/tca-document/.vuepress/configs/navbar/en.ts b/web/packages/tca-document/.vuepress/configs/navbar/en.ts index 72ccb502b..1ddb3af9e 100644 --- a/web/packages/tca-document/.vuepress/configs/navbar/en.ts +++ b/web/packages/tca-document/.vuepress/configs/navbar/en.ts @@ -3,7 +3,7 @@ import type { NavbarConfig } from '@vuepress/theme-default' export const en: NavbarConfig = [ { text: '快速入门', - link: '/en/quickStarted/intro.html', + link: '/en/quickStarted/deploySever.md', }, { text: '帮助文档', @@ -13,12 +13,30 @@ export const en: NavbarConfig = [ text: 'API', link: '/en/api/', }, + { + text: '了解更多', + children: [ + { + text: '深入', + children: [ + '/en/advanced/任务分布式执行.md', + '/en/advanced/集成代码分析工具.md', + ], + }, + { + text: '文章', + children: [ + + ], + }, + ] + }, { text: '社区', children: [ - '/en/community/contribute.html', - '/en/community/changelog.html', - '/en/community/joingroup.html', + '/en/community/contribute.md', + '/en/community/changelog.md', + '/en/community/joingroup.md', { text: '体验官方版本', link: 'https://tca.tencent.com/', diff --git a/web/packages/tca-document/.vuepress/configs/navbar/zh.ts b/web/packages/tca-document/.vuepress/configs/navbar/zh.ts index c2a597e88..5a5974b7f 100644 --- a/web/packages/tca-document/.vuepress/configs/navbar/zh.ts +++ b/web/packages/tca-document/.vuepress/configs/navbar/zh.ts @@ -3,7 +3,7 @@ import type { NavbarConfig } from '@vuepress/theme-default' export const zh: NavbarConfig = [ { text: '快速入门', - link: '/zh/quickStarted/intro.html', + link: '/zh/quickStarted/deploySever.md', }, { text: '帮助文档', @@ -13,12 +13,30 @@ export const zh: NavbarConfig = [ text: 'API', link: '/zh/api/', }, + { + text: '了解更多', + children: [ + { + text: '深入', + children: [ + '/zh/advanced/任务分布式执行.md', + '/zh/advanced/集成代码分析工具.md', + ], + }, + { + text: '文章', + children: [ + + ], + }, + ] + }, { text: '社区', children: [ - '/zh/community/contribute.html', - '/zh/community/changelog.html', - '/zh/community/joingroup.html', + '/zh/community/contribute.md', + '/zh/community/changelog.md', + '/zh/community/joingroup.md', { text: '体验官方版本', link: 'https://tca.tencent.com/', diff --git a/web/packages/tca-document/.vuepress/configs/sidebar/en.ts b/web/packages/tca-document/.vuepress/configs/sidebar/en.ts index 077c37d6c..e21e4c30d 100644 --- a/web/packages/tca-document/.vuepress/configs/sidebar/en.ts +++ b/web/packages/tca-document/.vuepress/configs/sidebar/en.ts @@ -6,13 +6,15 @@ export const en: SidebarConfig = { text: '介绍', children: [ '/en/guide/README.md', + '/en/guide/快速入门/快速启动一次代码分析.md' ], }, { - text: '团队管理相关', + text: '团队管理', children: [ '/en/guide/团队管理/团队管理.md', '/en/guide/团队管理/成员权限.md', + '/en/guide/团队管理/节点管理.md', ] }, { @@ -22,7 +24,7 @@ export const en: SidebarConfig = { ] }, { - text: '分析方案 & 模板', + text: '分析方案', children: [ '/en/guide/分析方案/基础属性配置.md', '/en/guide/分析方案/代码检查配置.md', @@ -37,7 +39,6 @@ export const en: SidebarConfig = { text: '工具管理', children: [ '/en/guide/工具管理/工具管理说明.md', - '/en/guide/工具管理/工具列表.md', '/en/guide/工具管理/自定义规则.md', '/en/guide/工具管理/自定义工具.md', ] @@ -45,7 +46,13 @@ export const en: SidebarConfig = { { text: '后台管理', children: [ - '/en/guide/后台管理/后台管理说明.md', + '/en/guide/后台管理/用户管理.md', + '/en/guide/后台管理/团队管理.md', + '/en/guide/后台管理/项目管理.md', + '/en/guide/后台管理/分析记录管理.md', + '/en/guide/后台管理/节点管理.md', + '/en/guide/后台管理/工具管理.md', + '/en/guide/后台管理/OAuth管理.md', ] }, { @@ -59,8 +66,8 @@ export const en: SidebarConfig = { { text: '服务端', children: [ - '/en/guide/服务器/server.md', - '/en/guide/服务器/deploy_with_minio.md', + '/en/guide/服务端/server.md', + '/en/guide/服务端/deploy_with_minio.md', ] }, { @@ -73,31 +80,12 @@ export const en: SidebarConfig = { ], '/en/advanced/': [ { - text: '后台管理', - children: [ - '/en/advanced/manage/description.md', - ] - }, - { - text: '工具管理', - children: [ - '/en/advanced/tool/description.md', - '/en/advanced/tool/customrule.md', - '/en/advanced/tool/customtool.md', - ] - }, - { - text: '分布式执行', - children: [ - '/en/advanced/distributed.md', - ] - }, - { - text: '持久化存储', + text: '深入', children: [ - '/en/advanced/minio.md', - ] - }, + '/en/advanced/任务分布式执行.md', + '/en/advanced/集成代码分析工具.md', + ], + } ], '/en/community/': [ { @@ -127,15 +115,35 @@ export const en: SidebarConfig = { '/en/quickStarted/': [ { text: '快速入门', - children: [ - { - text: '概述', - link: '/en/quickStarted/intro.md', - }, - '/en/quickStarted/deploySever.md', - '/en/quickStarted/setup.md', - '/en/quickStarted/deployClient.md', + link: '/en/quickStarted/deploySever.md', + // children: [ + // { + // text: '快速入门', + // link: '/en/quickStarted/deploySever.md', + // }, + // ], + }, + { + text: '依赖安装参考', + // collapsible: true, + children: [ + '/en/quickStarted/references/install_python37_on_centos.md', + '/en/quickStarted/references/install_python37_on_ubuntu.md', + '/en/quickStarted/references/install_mysql_on_centos.md', + '/en/quickStarted/references/install_redis_on_centos.md', + '/en/quickStarted/references/install_redis_from_source.md', + '/en/quickStarted/references/install_nginx_from_source.md', + ], + }, + { + text: '其他', + // collapsible: true, + children: [ + '/en/quickStarted/intro.md', + '/en/quickStarted/tools.md', '/en/quickStarted/FAQ.md', + '/en/quickStarted/codeDeploy.md', + '/en/quickStarted/dockercomposeDeploy.md', ], }, ], diff --git a/web/packages/tca-document/.vuepress/configs/sidebar/zh.ts b/web/packages/tca-document/.vuepress/configs/sidebar/zh.ts index 963203830..68f594c80 100644 --- a/web/packages/tca-document/.vuepress/configs/sidebar/zh.ts +++ b/web/packages/tca-document/.vuepress/configs/sidebar/zh.ts @@ -6,13 +6,15 @@ export const zh: SidebarConfig = { text: '介绍', children: [ '/zh/guide/README.md', + '/zh/guide/快速入门/快速启动一次代码分析.md' ], }, { - text: '团队管理相关', + text: '团队管理', children: [ '/zh/guide/团队管理/团队管理.md', '/zh/guide/团队管理/成员权限.md', + '/zh/guide/团队管理/节点管理.md', ] }, { @@ -22,7 +24,7 @@ export const zh: SidebarConfig = { ] }, { - text: '分析方案 & 模板', + text: '分析方案', children: [ '/zh/guide/分析方案/基础属性配置.md', '/zh/guide/分析方案/代码检查配置.md', @@ -37,7 +39,6 @@ export const zh: SidebarConfig = { text: '工具管理', children: [ '/zh/guide/工具管理/工具管理说明.md', - '/zh/guide/工具管理/工具列表.md', '/zh/guide/工具管理/自定义规则.md', '/zh/guide/工具管理/自定义工具.md', ] @@ -45,7 +46,13 @@ export const zh: SidebarConfig = { { text: '后台管理', children: [ - '/zh/guide/后台管理/后台管理说明.md', + '/zh/guide/后台管理/用户管理.md', + '/zh/guide/后台管理/团队管理.md', + '/zh/guide/后台管理/项目管理.md', + '/zh/guide/后台管理/分析记录管理.md', + '/zh/guide/后台管理/节点管理.md', + '/zh/guide/后台管理/工具管理.md', + '/zh/guide/后台管理/OAuth管理.md', ] }, { @@ -59,8 +66,8 @@ export const zh: SidebarConfig = { { text: '服务端', children: [ - '/zh/guide/服务器/server.md', - '/zh/guide/服务器/deploy_with_minio.md', + '/zh/guide/服务端/server.md', + '/zh/guide/服务端/deploy_with_minio.md', ] }, { @@ -71,6 +78,15 @@ export const zh: SidebarConfig = { ] }, ], + '/zh/advanced/': [ + { + text: '深入', + children: [ + '/zh/advanced/任务分布式执行.md', + '/zh/advanced/集成代码分析工具.md', + ], + } + ], '/zh/community/': [ { text: '社区资源', @@ -99,15 +115,35 @@ export const zh: SidebarConfig = { '/zh/quickStarted/': [ { text: '快速入门', - children: [ - { - text: '概述', - link: '/zh/quickStarted/intro.md', - }, - '/zh/quickStarted/deploySever.md', - '/zh/quickStarted/setup.md', - '/zh/quickStarted/deployClient.md', + link: '/zh/quickStarted/deploySever.md', + // children: [ + // { + // text: '快速入门', + // link: '/zh/quickStarted/deploySever.md', + // }, + // ], + }, + { + text: '依赖安装参考', + // collapsible: true, + children: [ + '/zh/quickStarted/references/install_python37_on_centos.md', + '/zh/quickStarted/references/install_python37_on_ubuntu.md', + '/zh/quickStarted/references/install_mysql_on_centos.md', + '/zh/quickStarted/references/install_redis_on_centos.md', + '/zh/quickStarted/references/install_redis_from_source.md', + '/zh/quickStarted/references/install_nginx_from_source.md', + ], + }, + { + text: '其他', + // collapsible: true, + children: [ + '/zh/quickStarted/intro.md', + '/zh/quickStarted/tools.md', '/zh/quickStarted/FAQ.md', + '/zh/quickStarted/codeDeploy.md', + '/zh/quickStarted/dockercomposeDeploy.md', ], }, ], diff --git a/web/packages/tca-document/.vuepress/public/images/Logo.svg b/web/packages/tca-document/.vuepress/public/images/Logo.svg new file mode 100644 index 000000000..5a9af40da --- /dev/null +++ b/web/packages/tca-document/.vuepress/public/images/Logo.svg @@ -0,0 +1,20 @@ + + + logo-1280-2 + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/packages/tca-document/.vuepress/styles/index.scss b/web/packages/tca-document/.vuepress/styles/index.scss index 5d3726f8b..cbac022c5 100644 --- a/web/packages/tca-document/.vuepress/styles/index.scss +++ b/web/packages/tca-document/.vuepress/styles/index.scss @@ -1,13 +1,17 @@ :root { scroll-behavior: smooth; - --c-brand: #176DE6; - --c-brand-light: #257EFA; + --c-brand: #176de6; + --c-brand-light: #257efa; .home .hero .description { font-size: 18px; } } html.dark { - --c-brand: #257EFA; - --c-brand-light: #176DE6; + --c-brand: #257efa; + --c-brand-light: #176de6; +} + +.home .hero img { + max-height: 200px; } diff --git a/web/packages/tca-document/README.md b/web/packages/tca-document/README.md index 5e31beae1..cee92ccde 100644 --- a/web/packages/tca-document/README.md +++ b/web/packages/tca-document/README.md @@ -1,25 +1,82 @@ --- home: true -title: 腾讯云代码分析帮助文档 +title: 腾讯云代码分析文档 +heroImage: /images/Logo.svg actions: - text: 快速入门 - link: /zh/quickStarted/intro.html + link: /zh/quickStarted/deploySever.html type: primary - text: 帮助文档 link: /zh/guide/ type: secondary features: - title: 稳定可靠的架构 - details: 采用分布式云原生架构,支持灵活扩缩容,执行更快更稳定。 + details: 支持分布式云原生计算架构,支持灵活扩缩容,执行更快更稳定。 - title: 多工具支持 - details: 已集成众多自研、知名开源工具等,采用分层分离架构,满足快速自助管理工具。 + details: 已集成众多自研、知名开源工具等,采用分层分离架构,可满足团队快速自助管理工具。 - title: 多语言覆盖 - details: 支持29种编程语言,覆盖常见常用编程语言。 - - title: 自定义质量指标 - details: 自定义代码质量检测标准,逐步优化代码质量。 + details: 支持 Java/C++/Objective-C/C#/JavaScript/Python/Go/PHP 等数29种语言,覆盖常用编程语言。 - title: 增量全量分析 details: 增量分析快速发现问题,全量分析保证问题全覆盖。 + - title: 自定义指标 + details: 自定义代码标准,逐步优化代码。 - title: 全方位质量报告 details: 图形化可视报告,轻松监管代码综合质量趋势。 + - title: 全方位质量报告 + details: 图形化可视报告,轻松监管代码综合质量趋势。 + - title: 标准化 API 接口 + details: 提供标准化 API 接口,支持快速对接 DevOps 平台。 + - title: 分布式客户端 + details: 支持分布式客户端,包含 Linux、Mac、Windows,满足用户本地高频分析场景。 footer: MIT Licensed | Copyright © 1998-present Tencent. All Rights Reserved. --- + +### 腾讯工蜂代码库镜像库 + +[https://git.code.tencent.com/Tencent_Open_Source/CodeAnalysis.git](https://git.code.tencent.com/Tencent_Open_Source/CodeAnalysis.git) + +### 腾讯云代码分析简介 + +腾讯云代码分析(Tencent Cloud Code Analysis,简称TCA,内部曾用研发代号 **CodeDog** )是集众多分析工具的云原生、分布式、高性能的代码综合分析跟踪平台,包含服务端、Web端和客户端三个组件,已集成一批自研工具,同时也支持动态集成业界各编程语言的分析工具。 + +代码分析是通过词法分析、语法分析、控制流、数据流分析等技术对程序代码进行扫描,对代码进行综合分析,验证代码是否满足规范性、安全性、可靠性、可维护性等指标的一种代码分析技术。 + +使用TCA可以帮助团队用代码分析技术查找代码中的规范性、结构性、安全漏洞等问题,持续监控项目代码质量并进行告警。同时TCA开放API,支持与上下游系统对接,从而集成代码分析能力,为代码质量提供保障,更有益于传承优良的团队代码文化。 + +![组件图](https://tencent.github.io/CodeAnalysis/media/Components.png) + +![流程图](https://tencent.github.io/CodeAnalysis/media/Flow.png) + +### 体验 + +[官方版本](http://tca.tencent.com) + +### 快速入门 + +- [快速入门](./zh/quickStarted/intro.md) +- [如何在本地部署Server与Web](./zh/quickStarted/deploySever.md#通过源代码) +- [如何通过Docker-Compose部署Server与Web](./zh/quickStarted/deploySever.md#通过docker-compose) +- [如何使用客户端](./zh/quickStarted/deployClient.md) +- [部署常见问题与解决方式](./zh/quickStarted/FAQ.md) + +## 社区 + +- 微信公众号:「腾讯云静态分析」,关注并发送“进群”即可加入官方开源交流微信群 +- QQ交流群:361791391 +- [GitHub讨论区](https://github.com/Tencent/CodeAnalysis/discussions) +- [Wiki](https://github.com/Tencent/CodeAnalysis/wiki) +- [腾讯云代码分析白皮书](https://github.com/Tencent/CodeAnalysis/tree/main/腾讯云代码分析白皮书.pdf) + +## 更新日志 + +[Changelog](https://github.com/Tencent/CodeAnalysis/tree/main/CHANGELOG.md) + +## 贡献 + +- 查看我们的[贡献说明](https://github.com/Tencent/CodeAnalysis/tree/main/CONTRIBUTING.md) +- [腾讯开源摘星计划2022](https://github.com/weopenprojects/WeOpen-Star/issues/19#issue-1228583868)(活动时间:2022年5月~12月) +- [腾讯开源激励计划](https://opensource.tencent.com/contribution) 鼓励开发者的参与和贡献,期待你的加入 + +### License + +[MIT licensed](https://github.com/Tencent/CodeAnalysis/tree/main/LICENSE) diff --git a/web/packages/tca-document/en/README.md b/web/packages/tca-document/en/README.md index 5e31beae1..e698dd0cd 100644 --- a/web/packages/tca-document/en/README.md +++ b/web/packages/tca-document/en/README.md @@ -1,25 +1,82 @@ --- home: true -title: 腾讯云代码分析帮助文档 +title: 腾讯云代码分析文档 +heroImage: /images/Logo.svg actions: - text: 快速入门 - link: /zh/quickStarted/intro.html + link: /zh/quickStarted/deploySever.html type: primary - text: 帮助文档 link: /zh/guide/ type: secondary features: - title: 稳定可靠的架构 - details: 采用分布式云原生架构,支持灵活扩缩容,执行更快更稳定。 + details: 支持分布式云原生计算架构,支持灵活扩缩容,执行更快更稳定。 - title: 多工具支持 - details: 已集成众多自研、知名开源工具等,采用分层分离架构,满足快速自助管理工具。 + details: 已集成众多自研、知名开源工具等,采用分层分离架构,可满足团队快速自助管理工具。 - title: 多语言覆盖 - details: 支持29种编程语言,覆盖常见常用编程语言。 - - title: 自定义质量指标 - details: 自定义代码质量检测标准,逐步优化代码质量。 + details: 支持 Java/C++/Objective-C/C#/JavaScript/Python/Go/PHP 等数29种语言,覆盖常用编程语言。 - title: 增量全量分析 details: 增量分析快速发现问题,全量分析保证问题全覆盖。 + - title: 自定义指标 + details: 自定义代码标准,逐步优化代码。 - title: 全方位质量报告 details: 图形化可视报告,轻松监管代码综合质量趋势。 + - title: 全方位质量报告 + details: 图形化可视报告,轻松监管代码综合质量趋势。 + - title: 标准化 API 接口 + details: 提供标准化 API 接口,支持快速对接 DevOps 平台。 + - title: 分布式客户端 + details: 支持分布式客户端,包含 Linux、Mac、Windows,满足用户本地高频分析场景。 footer: MIT Licensed | Copyright © 1998-present Tencent. All Rights Reserved. --- + +### Repo Mirror + +[https://git.code.tencent.com/Tencent_Open_Source/CodeAnalysis.git](https://git.code.tencent.com/Tencent_Open_Source/CodeAnalysis.git) + +### What is TCA + +Tencent Cloud Code Analysis (TCA for short, code-named **CodeDog** inside the company early) is a comprehensive platform for code analysis and issue tracking. TCA consist of three components, server, web and client. It integrates of a number of self-developed tools, and also supports dynamic integration of code analysis tools in various programming languages. + +Code analysis is a technology, using lexical analysis, syntax analysis, control-flow analysis, data-flow analysis to make a comprehensive analysis of the code, so as to verify whether the code meets the requirements of normative, security, reliability, maintainability and other indicators. + +Using TCA can help team find normative, structural, security vulnerabilities and other issues in the code, continuously monitor the quality of the project code and issue alerts. At the same time, TCA opens up APIs to support connection with upstream and downstream systems, so as to integrate code analysis capabilities, ensure code quality, and be more conducive to inheriting an excellent team code culture. + +![组件图](https://tencent.github.io/CodeAnalysis/media/Components.png) + +![流程图](https://tencent.github.io/CodeAnalysis/media/Flow.png) + +### Experience + +[Experience Link](http://tca.tencent.com) + +### Getting Started + +- [How to get start](./quickStarted/intro.md) +- [How to deploy server and web](./quickStarted/deploySever.md#通过源代码) +- [How to deploy server and web with docker-compose](./quickStarted/deploySever.md#通过docker-compose) +- [How to use client](./quickStarted/deployClient.md) +- [Deploy Q&A](./quickStarted/FAQ.md) + +### Community + +- WeChat official account:腾讯云静态分析 +- QQ Group: 361791391 +- [Discussion](https://github.com/Tencent/CodeAnalysis/discussions) +- [Wiki](https://github.com/Tencent/CodeAnalysis/wiki) +- [White Paper](https://github.com/Tencent/CodeAnalysis/tree/main/腾讯云代码分析白皮书.pdf) + +### Changelogs + +- Check our [Changelog](https://github.com/Tencent/CodeAnalysis/tree/main/CHANGELOG.md) + +### Contributing + +- Check out [CONTRIBUTING](https://github.com/Tencent/CodeAnalysis/tree/main/CONTRIBUTING.md) to see how to develop with TCA. +- [Tencent WeOpen Star Project](https://github.com/weopenprojects/WeOpen-Star/issues/19#issue-1228583868)(From May 2022 to September 2022) +- [Tencent Open Source Incentive Program](https://opensource.tencent.com/contribution) encourages the participation and contribution of developers. We look forward to your active participation. + +### License + +TCA is [MIT licensed](https://github.com/Tencent/CodeAnalysis/tree/main/LICENSE) diff --git "a/web/packages/tca-document/en/advanced/\344\273\273\345\212\241\345\210\206\345\270\203\345\274\217\346\211\247\350\241\214.md" "b/web/packages/tca-document/en/advanced/\344\273\273\345\212\241\345\210\206\345\270\203\345\274\217\346\211\247\350\241\214.md" new file mode 100644 index 000000000..5af9d5ac8 --- /dev/null +++ "b/web/packages/tca-document/en/advanced/\344\273\273\345\212\241\345\210\206\345\270\203\345\274\217\346\211\247\350\241\214.md" @@ -0,0 +1,71 @@ +# 任务分布式执行 + +## 适用场景 + +- 以往的单机器单进程,性能比较低,工具排队等待时间过长。希望通过并行执行分析来提高分析效率。 + +- 希望尽量使用公共资源或使用专机资源。 + +**为了满足以上需求,TCA已经进行如下支持:** + +- 支持工具在多台机器上并行执行。 + +- 支持指定工具在指定的机器上运行。 + +- 支持与本地启动的任务衔接,加速本地任务扫描。 + +- 配套任务状态监控能力,及时重置初始化超时或机器掉线的任务。 + +:::tip +TCA客户端除了通过localscan命令启动单次的代码分析,也可以作为一个分布式分析节点启动,作为常驻进程,多个节点可以分布式并行执行服务端下发的任务,提高扫描效率。和本地分析一样,需要先安装环境和必要的工具,并配置好服务端地址。 +::: + +## 常驻节点配置 + +**前置步骤**:公共/专有机器上已具备客户端。 + +开源版客户端,需要配置相关环境和依赖,可查阅帮助文档中的开源版客户端使用说明(如下图) + + ![helpopensource](https://tencent.github.io/CodeAnalysis/media/helpopensource.png) + (界面右上角图标点击-帮助文档-Client 客户端) + +**1.配置 config.ini 文件** + +将``替换成实际的serve ip(可包含端口号)。 + +**2.启动代码分析常驻节点** + +1. 从TCA前端页面中获取 `token`,前往 个人中心-个人令牌-复制`Token` 。 + - **作为公共节点**:`token`需要具有超级管理员权限(界面右上角图标点-管理入口-用户管理),如使用CodeDog账户的`token`。 + + - **作为专机节点**:该节点仅能分析该token具有权限的项目。 + +2. 进入到client目录下,执行命令: + `python3 codepuppy.py -l codepuppy.log start -t ` + + ![order](https://tencent.github.io/CodeAnalysis/media/order.png) + +3. 启动后,可以在命令行输出或codepuppy.log中查看运行日志,如果未报异常,且输出`task loop is started.`,表示节点已经正常启动。 + +**3.配置节点** + +:::tip +常驻节点首次启动后,需要到节点管理页面设置节点状态(默认为不可用),将其设置为活跃,用于接收和执行任务。 +::: + +1. 进入TCA节点管理页面。可以看到当前在线的节点,可以修改节点名称、标签、负责人等信息。 + + ![NodeManagement](https://tencent.github.io/CodeAnalysis/media/Nodemanagement.png) + (界面右上角图标点击-管理入口-节点管理) + +- 常驻节点首次启动后,需将节点状态从不可用(失效)状态切换到活跃(在线)状态。 + + ![StateSwitch](https://tencent.github.io/CodeAnalysis/media/StateSwitch.png) + +- 可以进入工具进程配置页面,对节点支持的工具进程进行管理(默认会全部勾选),未勾选的工具进程,将不会在该节点上执行。 + + ![ProcessConfiguration](https://tencent.github.io/CodeAnalysis/media/ProcessConfiguration.png) + +- 节点所属标签会与分析方案中的运行环境标签进行匹配,只有相同标签的任务才会下发到该机器节点上。 + +本功能代码已提交开源版,欢迎使用! :+1: diff --git "a/web/packages/tca-document/en/advanced/\351\233\206\346\210\220\344\273\243\347\240\201\345\210\206\346\236\220\345\267\245\345\205\267.md" "b/web/packages/tca-document/en/advanced/\351\233\206\346\210\220\344\273\243\347\240\201\345\210\206\346\236\220\345\267\245\345\205\267.md" new file mode 100644 index 000000000..ca4463fe8 --- /dev/null +++ "b/web/packages/tca-document/en/advanced/\351\233\206\346\210\220\344\273\243\347\240\201\345\210\206\346\236\220\345\267\245\345\205\267.md" @@ -0,0 +1,215 @@ +# 集成代码分析工具 + +## 初识TCA任务执行机制 + +1. TCA server在接收到开启分析的请求后根据所选规则生成对应的task_request,每个task_request对应一个工具的任务 +2. TCA server将`task_request`分发到能够执行该工具的机器 +3. TCA client在收到task_request后提取出本次任务的工具名也就是其中的`task_name`字段,字段对应于工具的`name`字段 +4. TCA client按照`task_name`在client中的tool目录查找对应python启动脚本 +5. 执行python启动脚本中的内容 + +## 添加分析工具(以tca_ql_php_beta为例) + +根据上述的任务机制添加工具需要做到以下几点 + +1. 让server知道存在`tca_ql_php_beta`工具及其所含的规则 +2. 让server知道哪些客户端可以执行`tca_ql_php_beta`工具 +3. client下载/找到工具所在目录及需要的环境 +4. 让client知道`tca_ql_php_beta`对应的启动脚本是什么 + +### 如何让Server知道存在相应工具 + +1. 找到`server/projects/main/apps/scan_conf/management/commands/open_source`目录 + +2. 创建工具json文件,json文件名尽量对应工具名称方便查看 + +3. json文件内容为(以tca_ql_php_beta为例) + + ```python + [ + { + "name": "tca_ql_php_beta", + "display_name": "Hades_Beta_PHP(展示名称用于前端展示使用)", + "description": "工具描述", + "license": "工具license", + "libscheme_set": [], # 暂时不需要 + "task_processes": [ + "analyze", + "datahandle", + "compile" + ], # 工具进程,包含compile编译, analyze分析, datahandle数据处理 + "scan_app": "codelint", # 代码分析统一为codelint + "scm_url": "", # 暂时为空 + "run_cmd": "", + "envs": null, # 是否需要特殊环境,这里无需填写 + "build_flag": false, # 是否需要编译命令才能运行 + "checkrule_set": [ # 工具包含的规则 + { + "real_name": "deser", # 规则名 + "display_name": "反序列化漏洞", # 规则前端展示,考虑各工具规则名可能晦涩难懂,设置展示名称方便查找 + "severity": "error", # 规则等级 从上到下分为 fatal, error, warning, info 四个等级 + "category": "security", # 规则类别。correctness 功能 security安全 performance性能 usability可用性 accessibility无障碍化 i18n国际化 convention代码风格 other其他 + "rule_title": "反序列化漏洞", # 一句话概括规则简介 + "rule_params": null, # 规则参数 + "languages": [ # 支持语言 + "php" + ], + "solution": "", # 建议的解决方法 + "owner": "", + "labels": [], + "description": "", # 规则详细介绍 + } + ] + } + ] + ``` + +4. 在`server/projects/main/`目录执行`python manage.py loadcheckers --dir open_source tca_ql_php_beta` 加载工具进入数据库 + +## 让server知道哪些客户端可以执行`tca_ql_php_beta`工具 + +1. 进入节点管理页面 + +![节点管理](https://tencent.github.io/CodeAnalysis/media/node_mange.png) + +2. 选择其中一台机器 工具进程配置,勾选其工具进程 + +![工具进程](https://tencent.github.io/CodeAnalysis/media/tool.png) + +## client下载/找到工具所在目录及需要的环境 + +1. 找到puppy-tool-config若没有额外配置则为默认代码库 +2. 修改其中的 ini 配置文件,每个操作系统对应一个ini +3. 以tca_ql_php_beta为例需要做以下修改 + +``` +; env_path 主要填写存放工具文件所在的相对目录,一般都存放/拉取在tools下,会在工具执行前加载到环境变量中提供使用 +[env_path] +ZEUS_BETA_HOME : Zeus_Beta +HADES_BETA_HOME : Hades_Beta + +; toolz_url +[tool_url] 主要填写工具的git仓库,这里因为tca_ql_php_beta直接使用tools下的目录所以不用再进行额外拉取也无需再写 +CPPCHECK : ${base_value:git_url}/linux-cppcheck-1.78 + +; 各工具配置 以tca_ql_php_beta为例 +; env_path 填写上面需要加载的环境变量 +; env_value 通用环境变量,一般无需填写如果有需求需要现在 [env_value] 中定义好再填写 +; path 工具所在目录填写上面的定义 +; tool_url 工具git仓库,使用本地相对目录故为空 +[tca_ql_php_beta] +env_path : ZEUS_BETA_HOME;HADES_BETA_HOME +env_value : +path : ${env_path:ZEUS_BETA_HOME};${env_path:HADES_BETA_HOME} +tool_url : + +``` + +## 让client知道`tca_ql_php_beta`对应的启动脚本是什么 + +1. 以上述步骤在`client/tool`目录添加脚本`tca_ql_php_beta.py`作为启动脚本 注:启动脚本必须与工具名称相同 + +2. 编写脚本 + +### 脚本编写规范 + +以`tca_ql_php_beta`为例 + +``` + +from task.codelintmodel import CodeLintModel +from util.logutil import LogPrinter +from util.subprocc import SubProcController + +logger = LogPrinter() + + +class TcaQlPHPBeta(CodeLintModel): + # 代码分析工具集成基类CodeLintModel + def __init__(self, params): + logger.info("找到工具了Q_Q") + super().__init__(params) + + def compile(self, params): + logger.info("开始编译了Q_Q") + build_cmd = params.get('build_cmd', None) # 从params中获取编译命令, params内容可以在最后附录查看 + lang = "php" + do_some_things() + + def analyze(self, params): + logger.info("开始分析了Q_Q") + lang = "php" + HADES_HOME = envs.get("HADES_BETA_HOME", None) + output_json = "result.json" + sp = SubProcController( + command=["Hades", "analyze", "test.php", "-o", output_json], + cwd=HADES_HOME, + stdout_line_callback=subprocc_log, + stderr_line_callback=subprocc_log, + ) + sp.wait() # 执行工具分析命令 + issues = [] + # 工具结果输出到output_json,具体工具可能有所不同 + if os.path.exists(output_json): + with open(output_json, "r") as result_reader: + result = json.load(result_reader) + issues.extend(result) + return issues + +tool = TcaQlPHPBeta # 必须,必须包含tool变量并且为该工具的类 +``` + +1. 脚本必须包含analyze方法,如果有配置编译进程也需要相应的compile方法来做编译相关工作,datahandle函数不用自定义基类方法已经够用了。方法执行顺序为 compile -> analyze -> datahandle +2. params参数为`task_request`中的`task_params`字段,具体字段将在最后附录进行说明 +3. anlyze方法必须有返回值,返回值为issue列表,issue格式为 + +``` +{ + "path": "文件相对路径", + "line": "行号,int类型", + "column": "列号, int类型,如果工具没有输出列号信息,可以用0代替", + "msg": "提示信息", + "rule": "规则名称,可以根据需要输出不同的规则名", + "refs": [ + { + "line": "回溯行号", + "msg": "提示信息", + "tag": "用一个词简要标记该行信息,比如uninit_member,member_decl等,如果没有也可以都写成一样的", + "path": "回溯行所在文件绝对路径" + }, + ... + ] +} +说明: + refs:可选,记录问题回溯路径信息。比如当前文件的回溯路径其他的3行代码,可以将这三行的路径及提示信息,按顺序添加到refs数组中。 +``` + +# PR + +如果有意公开您添加的工具欢迎发起PR + +注:别忘了puppy-tool-config 也需要PR + +# 附录 + +## params 表格 + +| 字段 | 说明 | 类型 | +| --- | --- | --- | +| scan_languages | 语言 | 字符串列表如 ["python", "php"] | +| pre_cmd | 编译前置命令 | 字符串 | +| build_cmd | 编译命令 | 字符串 | +| envs | 额外环境变量 | 字符串 | +| scm_last_revision | 上次成功分析的代码版本,增量使用 | 字符串 | +| incr_scan | 是否为增量分析 | bool | +| rules | 规则名称列表,只有规则名 | 字符串列表 | +| rule_list | 详细的规则列表包含规则名和规则参数等 | 字典列表 | +| checktool | 工具详细信息,执行一般用不到 | 字典 | +| path_filters | 过滤路径 | 字典 | +| scm_url | 代码库url | 字符串 | +| source_dir | 代码库本地目录 | 字符串 | +| work_dir | 本次任务的work_dir目录 | 字符串 | +| project_id | 分析项目id | int | +| repo_id | 仓库id | int | +| task_id | 任务id | int | +| job_id | 本次分析的id | int | diff --git a/web/packages/tca-document/en/community/changelog.md b/web/packages/tca-document/en/community/changelog.md index 6029a2219..e3fd99512 100644 --- a/web/packages/tca-document/en/community/changelog.md +++ b/web/packages/tca-document/en/community/changelog.md @@ -1,47 +1,61 @@ # 更新日志 ## V1.2.0 (2022-4-27) + ### Features + - 【Web端】增加工具管理 - 【工具】增加logback检查的安全规则 - 【服务端】增加TCA server&web 一键部署脚本 - 【服务端】删除main部分异步任务;调整server nginx启动位置 - 【服务端】增加server健康监测 + ### Docs + - 完善部署和Q&A文档 - 上传工具列表 - ## V1.1.3 (2022-4-18) + ### Features + - 【工具】上传开源合规检查规则 - 【工具】新增PHP安全相关规则 - 【服务端】上线license鉴权 - 【客户端】支持对工具license校验 + ### Docs + - 更新文档内的工具默认路径 - 增加任务分布式执行能力操作文档 - 增加PR操作流程 - ## V1.1.2 (2022-4-2) + ### Features -- 【服务端】优化部署构建脚本 + +- 【服务端】优化部署构建脚本 + ### Docs -- 简化前端部署脚本&文档 -- 优化指引文档 +- 简化前端部署脚本&文档 +- 优化指引文档 ## V1.1.1 (2022-3-31) + ### Features + - 【工具】增加0daychecker工具 - 【工具】增加Log4j、LogBack漏洞检查规则包 ### Docs + - 完善部署文档说明,推荐使用Docker-Compose 2.3.3版本 ## V1.1.0 (2022-3-29) + ### Features + - 【客户端】client支持arm64架构执行环境 - 【客户端】client新增分布式节点模式 - 【客户端】修改参数isTotal(是否开启全量扫描)判断方式及参数startCommand(启动客户端命令)拼接方式 @@ -51,17 +65,21 @@ - 【Web端】web模块部署脚本问题修复及优化 - 【Web端】增加管理后台、增加在线分析 - 【Web端】调整前端部署脚本,支持传递nginx配置地址、前端资源部署地址 + ### Bugfixes + - Jenkins插件命令拼装逻辑修正 + ### Docs + - 调整pypi下载失败提示 - 调整前端部署文档及脚本 - 更新License - - ## V1.0.1 (2022-03-01) + ### Features + - feat: 【服务端】调整代码库登记ssh url链接格式适配 - feat: 【工具】上线支持PHP安全工具-Rips - feat: 【工具】调整androidlint部分规则描述 @@ -72,16 +90,17 @@ - feat: 【客户端】增加在docker中快速使用client的方式 ### Bugfixes + - fix: 【服务端】补充缺失的依赖 - fix: 【Web端】修复下载codedog.ini失败提示 - + ### Docs + - doc: 上线部署文档Q&A - doc: 优化部署文档、帮助文档说明 - doc: 增加产品白皮书 - doc: 补充redis和nginx源码安装参考文档 - - ## V1.0.0 -初始发布 \ No newline at end of file + +初始发布 diff --git a/web/packages/tca-document/en/community/pr.md b/web/packages/tca-document/en/community/pr.md index b650120f6..1c0fc8852 100644 --- a/web/packages/tca-document/en/community/pr.md +++ b/web/packages/tca-document/en/community/pr.md @@ -1,33 +1,42 @@ ![Welcome](../../images/Welcome.png) PR全称为Pull Request,它是一种代码库的协作方式。开发者可以通过PR将自己在代码库的修改通知到代码库负责人,由原作者评审代码并决定是否能合入。 -> Pull requests let you tell others about changes you've pushed to a branch in a repository on GitHub. Once a pull request is opened, you can discuss and review the potential changes with collaborators and add follow-up commits before your changes are merged into the base branch. + +:::tip +Pull requests let you tell others about changes you've pushed to a branch in a repository on GitHub. Once a pull request is opened, you can discuss and review the potential changes with collaborators and add follow-up commits before your changes are merged into the base branch. +::: # PR操作流程 + ## 一、Fork目标代码库 + ![fork](../../images/Fork.png) -点击Fork后,会在自己名下产生一个相同代码库,比如我Fork CodeAnalysis项目后,会在我名下多出一个CodeAnalysis代码库,地址为https://github.com/Lingghh/CodeAnalysis +点击Fork后,会在自己名下产生一个相同代码库,比如我Fork CodeAnalysis项目后,会在我名下多出一个CodeAnalysis代码库,地址为 ## 二、克隆Fork的代码库并创建分支 + 在本地克隆Fork的代码库并创建分支 -``` +```bash git clone https://github.com/Lingghh/CodeAnalysis git checkout -b dev/add_qa_20220301 ``` 注:也可以在自己Fork的代码库GitHub页面上创建分支。 - + ![fork1](../../images/fork1.png) 接下来就可以在本地修改代码,修改完成后先push到Fork的代码库中. ## 三、在目标项目中提交PR -### 1.进入到目标项目中,点击Pull requests Tab,再点击New pull request就会进入到创建PR的页面。 + +### 1.进入到目标项目中,点击Pull requests Tab,再点击New pull request就会进入到创建PR的页面 + ![New pull request](../../images/NewPullRequest.png) -### 2.进入PR页面后: +### 2.进入PR页面后 + - 点击compare across forks 。 - 点击head repository 。 - 选择自己Fork的代码库和比较的分支,比如我这里选择Lingghh/CodeAnalysis和待合入的分支dev/add_arm64_file 。 @@ -37,7 +46,6 @@ git checkout -b dev/add_qa_20220301 PR创建后,代码库管理员会评审你提交的代码,并决定是否接受该PR。 -## 更多信息请参阅[GitHub PullRequest官方文档](https://docs.github.com/cn/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests/) - -## TCA团队诚邀您的加入! +## 更多信息请参阅[GitHub PullRequest官方文档](https://docs.github.com/cn/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests/) +## TCA团队诚邀您的加入 diff --git a/web/packages/tca-document/en/guide/web/web.md b/web/packages/tca-document/en/guide/web/web.md index 76780308d..6cdd73816 100644 --- a/web/packages/tca-document/en/guide/web/web.md +++ b/web/packages/tca-document/en/guide/web/web.md @@ -4,9 +4,11 @@ TCA Web 采用 [Lerna](https://www.lernajs.cn/) 进行 `monorepo` 管理。 -> [Lerna GitHub地址](https://github.com/lerna/lerna) -> -> [Lerna 中文命令文档](http://www.febeacon.com/lerna-docs-zh-cn/) +:::tip +[Lerna GitHub地址](https://github.com/lerna/lerna) + +[Lerna 中文命令文档](http://www.febeacon.com/lerna-docs-zh-cn/) +::: 由 `framework`、`login`、`tca-layout`、`tca-analysis`、`tca-manage`微前端以及`tca-document`前端帮助文档组成。 diff --git "a/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/OAuth\347\256\241\347\220\206.md" "b/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/OAuth\347\256\241\347\220\206.md" new file mode 100644 index 000000000..d2cc8fbad --- /dev/null +++ "b/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/OAuth\347\256\241\347\220\206.md" @@ -0,0 +1,18 @@ +# OAuth管理 + +- 可**创建**、**编辑**、**清除**主流代码托管平台的Oauth应用配置,为使用者提供OAuth授权支持。 + +- 支持平台及如何创建OAuth应用: + + - 腾讯工蜂:[创建 OAuth 应用程序](https://code.tencent.com/help/oauth2/) + - GitHub:[创建 OAuth 应用程序](https://docs.github.com/cn/developers/apps/building-oauth-apps/creating-an-oauth-app) + - Gitee:[创建 OAuth 应用程序](https://gitee.com/api/v5/oauth_doc#/list-item-3) + - GitLab:[创建 OAuth 应用程序](https://docs.gitlab.com/ee/integration/oauth_provider.html) + +![OAuth管理](../../../images/manage_oauth_01.png) + +![OAuth管理](../../../images/manage_oauth_02.png) + +::: tip +配置OAuth应用时,回调地址栏需填入当前TCA平台配置的域名或IP地址(如当前页面非80端口,需要显式指定端口号),作为Git平台上OAuth应用的回调地址。 +::: diff --git "a/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\210\206\346\236\220\350\256\260\345\275\225\347\256\241\347\220\206.md" "b/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\210\206\346\236\220\350\256\260\345\275\225\347\256\241\347\220\206.md" new file mode 100644 index 000000000..82db6162f --- /dev/null +++ "b/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\210\206\346\236\220\350\256\260\345\275\225\347\256\241\347\220\206.md" @@ -0,0 +1,7 @@ +# 分析记录管理 + +- 可查看平台**全部分析记录**。 + +- 可点击查阅**分析记录详情**。 + +![分析记录列表](../../../images/manage_job_01.png) \ No newline at end of file diff --git "a/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\220\216\345\217\260\347\256\241\347\220\206\350\257\264\346\230\216.md" "b/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\220\216\345\217\260\347\256\241\347\220\206\350\257\264\346\230\216.md" deleted file mode 100644 index ef7506300..000000000 --- "a/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\220\216\345\217\260\347\256\241\347\220\206\350\257\264\346\230\216.md" +++ /dev/null @@ -1,72 +0,0 @@ -# 后台管理说明 - -仅**超级管理员**可进入后台管理页面 - -包含以下管理页面:`用户管理`、`分析记录管理`、`节点管理`、`工具管理` - -## 用户管理 - -- 可**查看**、**编辑**、**创建**平台用户。 - -- 可配置用户的**登录密码**、**用户级别**、**超级管理员**等。 - -![用户管理](../../../images/manage_01.png) - -## 分析记录管理 - -- 可查看平台**全部分析记录**。 - -- 可点击查阅**分析记录详情**。 - -![分析记录管理](../../../images/manage_02.png) - -## 节点管理 - -- 可查看**常驻节点状态**。 - -- 可**查看**、**编辑**、**删除**常驻节点。 - -- 可配置节点**工具进程**。 - -- 可配置**标签** - -![节点管理](../../../images/manage_03.png) - -## 工具管理 - -- 可查看**全部工具**(包含平台提供工具、团队自定义工具)。 - -- 可**查看**、**编辑**工具。 - -- 可变更工具**权限状态**。 - -![工具管理](../../../images/manage_04.png) - -::: tip -工具的权限状态仅能由**平台管理员**进行变更调整,需谨慎调整 - -- **团队内可用**:即工具配置了可用团队白名单的团队可以使用该工具,默认创建工具的团队已在白名单内 - -- **全平台可用**:即不同团队都可见可用该工具 - -- **支持自定义规则,全平台可用**:即该工具不同团队都可见可用,且支持用户添加团队所需的自定义规则,该自定义规则存在团队隔离,仅团队内可以,其他团队不可使用 -::: - -## OAuth管理 - -- 可**创建**、**编辑**、**清除**主流代码托管平台的Oauth应用配置,为使用者提供OAuth授权支持。 - -- 支持平台及如何创建OAuth应用: - - - 腾讯工蜂:[创建 OAuth 应用程序](https://code.tencent.com/help/oauth2/) - - GitHub:[创建 OAuth 应用程序](https://docs.github.com/cn/developers/apps/building-oauth-apps/creating-an-oauth-app) - - Gitee:[创建 OAuth 应用程序](https://gitee.com/api/v5/oauth_doc#/list-item-3) - - GitLab:[创建 OAuth 应用程序](https://docs.gitlab.com/ee/integration/oauth_provider.html) - -![OAuth管理](../../../images/manage_05.png) - -![OAuth管理](../../../images/manage_06.png) - -::: tip -配置OAuth应用时,回调地址栏需填入当前TCA平台配置的域名或IP地址(如当前页面非80端口,需要显式指定端口号),作为Git平台上OAuth应用的回调地址。 -::: \ No newline at end of file diff --git "a/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" "b/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" new file mode 100644 index 000000000..68a879e2b --- /dev/null +++ "b/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" @@ -0,0 +1,9 @@ +# 团队管理 + +- 可查看平台创建的团队列表,并提供了相应筛选 + +- 可**禁用**、**恢复**团队 + +![团队列表](../../../images/manage_org_01.png) + +![团队操作](../../../images/manage_org_02.png) \ No newline at end of file diff --git "a/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206.md" "b/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206.md" new file mode 100644 index 000000000..0b62da6b1 --- /dev/null +++ "b/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206.md" @@ -0,0 +1,19 @@ +# 工具管理 + +- 可查看**全部工具**(包含平台提供工具、团队自定义工具)。 + +- 可**查看**、**编辑**工具。 + +- 可变更工具**权限状态**。 + +![工具管理](../../../images/manage_tool_01.png) + +::: tip +工具的权限状态仅能由**平台管理员**进行变更调整,需谨慎调整 + +- **团队内可用**:即工具配置了可用团队白名单的团队可以使用该工具,默认创建工具的团队已在白名单内 + +- **全平台可用**:即不同团队都可见可用该工具 + +- **支持自定义规则,全平台可用**:即该工具不同团队都可见可用,且支持用户添加团队所需的自定义规则,该自定义规则存在团队隔离,仅团队内可以,其他团队不可使用 +::: \ No newline at end of file diff --git "a/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\347\224\250\346\210\267\347\256\241\347\220\206.md" "b/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\347\224\250\346\210\267\347\256\241\347\220\206.md" new file mode 100644 index 000000000..b51f8f02b --- /dev/null +++ "b/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\347\224\250\346\210\267\347\256\241\347\220\206.md" @@ -0,0 +1,9 @@ +# 用户管理 + +- 可**查看**、**编辑**、**创建**平台用户。 + +- 可配置用户的**登录密码**、**用户级别**、**超级管理员**等。 + +![用户列表](../../../images/manage_user_01.png) + +![用户编辑](../../../images/manage_user_02.png) \ No newline at end of file diff --git "a/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" "b/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" new file mode 100644 index 000000000..3a23e9ddc --- /dev/null +++ "b/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" @@ -0,0 +1,14 @@ +# 节点管理 + +- 可查看**常驻节点状态**,包含**公共节点**和**团队节点**。 + +- 可**查看**、**编辑**、**删除**常驻节点。 + +- 可配置节点**工具进程**。 + +- 可配置**节点标签** + +![节点管理](../../../images/manage_node_01.png) +![节点管理](../../../images/manage_node_02.png) +![节点管理](../../../images/manage_node_03.png) +![节点管理](../../../images/manage_node_04.png) \ No newline at end of file diff --git "a/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\351\241\271\347\233\256\347\256\241\347\220\206.md" "b/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\351\241\271\347\233\256\347\256\241\347\220\206.md" new file mode 100644 index 000000000..3f6e4fbf2 --- /dev/null +++ "b/web/packages/tca-document/en/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\351\241\271\347\233\256\347\256\241\347\220\206.md" @@ -0,0 +1,7 @@ +# 项目管理 + +- 可查看平台创建的项目列表,并提供了提供相应筛选 + +- 可**禁用**、**恢复**项目 + +![项目列表](../../../images/manage_team_01.png) \ No newline at end of file diff --git "a/web/packages/tca-document/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" "b/web/packages/tca-document/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" index 234c9e649..274a04e62 100644 --- "a/web/packages/tca-document/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" +++ "b/web/packages/tca-document/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" @@ -1,4 +1,4 @@ -# 团队管理 +# 团队说明 ![成员权限](../../../images/team_member.png) diff --git "a/web/packages/tca-document/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\346\210\220\345\221\230\346\235\203\351\231\220.md" "b/web/packages/tca-document/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\346\210\220\345\221\230\346\235\203\351\231\220.md" index 5967036a1..d4dfc5d66 100644 --- "a/web/packages/tca-document/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\346\210\220\345\221\230\346\235\203\351\231\220.md" +++ "b/web/packages/tca-document/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\346\210\220\345\221\230\346\235\203\351\231\220.md" @@ -1,6 +1,6 @@ # 成员权限 -## 团队成员管理 +## 团队成员 ![成员权限](../../../images/team_member.png) @@ -10,7 +10,7 @@ **团队普通成员**:可以创建项目,可以访问自己有权限的项目。创建项目的人会自动成为这个项目的项目管理员。 -## 项目成员管理 +## 项目成员 项目成员分为**项目管理员**和**项目普通成员**。 diff --git "a/web/packages/tca-document/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" "b/web/packages/tca-document/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" new file mode 100644 index 000000000..e9684cd13 --- /dev/null +++ "b/web/packages/tca-document/en/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" @@ -0,0 +1,105 @@ +# 节点与标签 + +除了使用**公共节点**执行代码分析外,团队还可以利用**团队标签**注册并使用**团队节点**。 + +## 名词释义与特点 + +- 团队节点是**团队注册并管理**的**私有**节点。 + +- 团队节点**仅会运行**当前团队所属的分析任务。 + +- 团队标签是用于关联节点机器与分析项目。 + ::: tip + 当一个分析项目在方案中配置运行环境为团队标签后,该项目创建的任务就会下发到团队标签关联的节点机器上运行 + ::: + +## 适用场景 + +1. 业务项目**不想**在公共机器上**排队**等待 + +2. 业务项目**代码比较敏感**,不能在公共机器上运行 + +3. 业务项目需要**依赖特定**的**机器环境**(比如CPU架构、操作系统等) + +4. ... + +以上场景,均可考虑使用团队节点,业务团队提供机器资源接入作为团队节点,仅分析自己业务的代码库,**保证执行效率**,**保护源码不泄漏**,**支持项目特殊依赖**等 + +## 团队节点注册 + +- 根据环境下载客户端二进制文件或拉取源码,参考[客户端](../客户端/配置说明.md)。 + +- 通过终端启动客户端: + + - 客户端二进制启动 + + ```bash + ./codepuppy start -t TOKEN --org-sid ORG_SID + ``` + + - 客户端源码启动 + + ```bash + python3 codepuppy.py start -t TOKEN --org-sid ORG_SID + ``` + + ::: tip + 1. TOKEN 可以从平台**个人中心-个人令牌**页面获取 + 2. ORG_SID 可以从**页面链接**中获取 + ::: + +## 团队节点管理 + +完成团队节点注册后,可以在当前团队下看到对应的节点信息,同时**需要进行配置** + +::: warning +- 团队节点**首次注册**时,需要手动在平台上配置**所属标签**、**节点可用性**、**工具进程**等。 +- 将节点的**节点可用性**调整为**活跃**后,运行客户端节点的终端会输出**心跳上报成功**的日志 +::: + +- 首次注册团队节点,节点状态为不可用 + + ![注册团队节点](../../../images/org_node_manager_1.png) + +- 调整后的节点 + + ![注册团队节点](../../../images/org_node_manager_2.png) + +- 配置节点关联的工具进程: + + ![配置工具进程](../../../images/org_node_process.png) + +::: tip +1. 团队节点使用的**所属标签**均为当前团队内创建的标签,可参见[团队标签管理](#团队标签管理) +2. 团队标签可以参考`CodeDog`标签为不同的系统类型(Linux、MacOS、Windows)建立标签,比如`专属标签-Linux`、`专属标签-Mac`等 +::: + +### 团队节点执行任务范围 + +::: warning 使用团队节点运行分析任务的前提 +对应分析项目使用的分析方案中,需要配置分析方案中的**运行环境**为该团队节点配置的所属标签。 +::: + +::: warning 团队节点执行的任务范围取决于该节点的负责人 +- 如果节点负责人为团队管理员,该节点可以执行当前团队所有项目的分析任务 +- 如果节点负责人为项目管理员,该节点只能运行指定项目下的分析任务 +- 如果节点负责人为部分代码库的管理员,该节点只能运行对应代码库的分析任务 +::: + + +## 团队标签管理 + +您可以创建一个团队标签,并配置到您的团队节点和您的分析方案中 + +- 创建团队标签。 + + ![创建团队标签](../../../images/org_tag_manager.png) + +- 配置团队节点所属标签。 + + ![节点配置团队标签](../../../images/org_tag_node.png) + +- 配置分析方案运行环境。 + + ![方案配置团队标签](../../../images/org_tag_scheme.png) + diff --git "a/web/packages/tca-document/en/guide/\345\256\242\346\210\267\347\253\257/\351\205\215\347\275\256\350\257\246\346\203\205.md" "b/web/packages/tca-document/en/guide/\345\256\242\346\210\267\347\253\257/\351\205\215\347\275\256\350\257\246\346\203\205.md" new file mode 100644 index 000000000..8cbe12b5a --- /dev/null +++ "b/web/packages/tca-document/en/guide/\345\256\242\346\210\267\347\253\257/\351\205\215\347\275\256\350\257\246\346\203\205.md" @@ -0,0 +1,26 @@ +## 在线分析 + +在线分析即是通过Server端将分析任务注册到执行队列中,并将任务分配到平台配置的常驻节点上,在常驻节点执行分析,分析完毕后将分析结果上报入库。 + +::: tip +平台需要存在常驻节点,请查阅 [常驻节点分析](./常驻节点分析.md) + +否则任务因没有机器而无法完成分配,超时后任务会注销。 +::: + +## 客户端分析 + +客户端分析即是本地分析,可直接配置本地的客户端配置文件,或在平台上配置好对应信息后,下载配置文件,替换客户端配置问题,并启动客户端分析。分析完毕后会将数据上报入库。 + +- 下载配置文件 + + ![下载配置文件](../../../images/start_scan_03.png) + +- 替换客户端配置文件,并启动客户端分析。 + +::: tip +本地需要下载客户端,请查阅 + +- [部署与配置客户端](../../quickStarted/deployClient.md) +- [客户端使用说明文档](./本地分析.md) +::: \ No newline at end of file diff --git "a/web/packages/tca-document/en/guide/\345\256\242\346\210\267\347\253\257/\351\205\215\347\275\256\350\257\264\346\230\216.md" "b/web/packages/tca-document/en/guide/\345\256\242\346\210\267\347\253\257/\351\205\215\347\275\256\350\257\264\346\230\216.md" index 887b8fa1f..34bd7bc6e 100644 --- "a/web/packages/tca-document/en/guide/\345\256\242\346\210\267\347\253\257/\351\205\215\347\275\256\350\257\264\346\230\216.md" +++ "b/web/packages/tca-document/en/guide/\345\256\242\346\210\267\347\253\257/\351\205\215\347\275\256\350\257\264\346\230\216.md" @@ -3,23 +3,27 @@ ## 一、基础配置 ### 1. 机器配置推荐 + | 操作系统 | 推荐配置 | | --------: | :------------------------------------------- | -| Linux | 8核16G内存,硬盘空间256G(可用空间不低于100G) | +| Linux | 8核16G内存,硬盘空间256G(可用空间不低于100G) | | Mac | 8核16G内存,硬盘空间256G(可用空间不低于100G) | | Windows | 8核16G内存,硬盘空间256G(可用空间不低于100G) | 以上为推荐配置,实际情况需要考虑扫描对象代码库的大小,按实际情况增加磁盘空间。 ### 2. 配置client/config.ini文件 + -(1)将``替换成实际的serve ip(可包含端口号)。 -(2)国内使用github拉取网络较慢,推荐使用腾讯工蜂拉取,需要修改以下配置: -- 修改 TOOL_CONFIG_URL=https://git.code.tencent.com/TCA/tca-tools/puppy-tools-config.git + +- 修改 TOOL_CONFIG_URL= - 前往[腾讯工蜂网站](https://git.code.tencent.com)注册账号,作为拉取工具使用(已经注册过的可以直接使用) - 将腾讯工蜂的账号密码填写到`TOOL_LOAD_ACCOUNT`中(由于腾讯工蜂的开源仓库也要求使用账号才能拉取,所以此处必须填写账号密码) ### 3. 配置client/codedog.ini文件(分布式节点模式无需配置) + 填写以下必填项:`token`,`org_sid`,`team_name`,`source_dir` | 字段名 | 填写说明 | @@ -31,28 +35,40 @@ 其他可选项按需填写。 - ## 二、使用docker环境快速体验 -> 适用于快速上手体验。使用docker运行,可以免去客户端环境依赖的安装,避免环境兼容性问题。 -> 但是由于环境受限于docker,会无法复用本地的编译环境,部分需要编译的工具无法使用。 + +:::tip +适用于快速上手体验。使用docker运行,可以免去客户端环境依赖的安装,避免环境兼容性问题。 + +但是由于环境受限于docker,会无法复用本地的编译环境,部分需要编译的工具无法使用。 +::: + ### 1. 下载和安装Docker + 参考Docker官方文档:[Docker下载和安装](https://docs.docker.com/get-started/) ### 2. 构建docker镜像 + 在`client`目录下,执行以下命令:`docker build -t tca-client .` ### 3. 执行docker容器,扫描代码,可选以下两种方式 + #### (1)直接使用docker运行 + - 在client目录下,执行以下命令: - (注意:按照实际情况填写`SOURCE_DIR`环境变量值) + ```bash export SOURCE_DIR=需要扫描的代码目录绝对路径 docker run -it --rm -v $PWD:/workspace/client -v $SOURCE_DIR:/workspace/src --name tca-client tca-client ``` + #### (2)使用docker内bash终端运行 + - 通过以下方式,进入容器内的bash终端后,通过命令行启动client代码: - 在client目录下,执行以下命令: - (注意:按照实际情况填写`SOURCE_DIR`环境变量值) + ```bash export SOURCE_DIR=需要扫描的代码目录绝对路径 docker run -it --rm -v $PWD:/workspace/client -v $SOURCE_DIR:/workspace/src --name tca-client tca-client bash @@ -60,53 +76,66 @@ docker run -it --rm -v $PWD:/workspace/client -v $SOURCE_DIR:/workspace/src -- python3 codepuppy.py localscan ``` - ## 三、使用本地机器环境运行 -> 适用于深度体验,可以复用本地编译环境,使用编译型代码分析工具。 -> 可能会有系统环境兼容问题。 + +:::tip +适用于深度体验,可以复用本地编译环境,使用编译型代码分析工具。 + +可能会有系统环境兼容问题。 +::: ### 1. 安装Python环境和第三方库 -- (1) 预装Python3.7、pip,支持 `python3` 和 `pip3` 命令 + +- (1) 预装Python3.7、pip,支持 `python3` 和 `pip3` 命令 - (2) 安装依赖:`pip3 install -r client/requirements/app_reqs.pip` ### 2. 安装第三方工具 + - (1) 进入到`client/requirements`目录 - (2) 在命令行中执行安装脚本`install.sh`(linux/mac环境)或`install.bat`(windows环境) - ### 3. 启动代码分析 + - (1) 进入到`client`目录下 - (2) 执行命令:`python3 codepuppy.py localscan` - ## 四、使用分布式节点模式执行客户端 -> TCA客户端除了通过`localscan`命令启动单次的代码分析,也可以作为一个分布式分析节点启动,作为常驻进程,多个节点可以分布式并行执行服务端下发的任务,提高扫描效率。 -> 和本地执行任务一样,需要先安装环境和必要的工具,并配置好服务端地址。 - + +:::tip + +- CA客户端除了通过`localscan`命令启动单次的代码分析,也可以作为一个分布式分析节点启动,作为常驻进程,多个节点可以分布式并行执行服务端下发的任务,提高扫描效率。 +- 和本地执行任务一样,需要先安装环境和必要的工具,并配置好服务端地址。 +::: + ### 1. 安装Python环境和第三方库 -- (1) 预装Python3.7、pip,支持 `python3` 和 `pip3` 命令 + +- (1) 预装Python3.7、pip,支持 `python3` 和 `pip3` 命令 - (2) 安装依赖:`pip3 install -r client/requirements/app_reqs.pip` ### 2. 安装第三方工具 + - 进入到`client/requirements`目录 - 在命令行中执行安装脚本`install.sh`(linux/mac环境)或`install.bat`(windows环境) ### 3. 启动代码分析节点 + - (1)从tca页面`个人中心`-`个人令牌`-复制Token - (2)进入到`client`目录下,执行命令:`python3 codepuppy.py -l codepuppy.log start -t ` - (3)启动后,可以在命令行输出或`codepuppy.log`中查看运行日志,如果未报异常,且输出`task loop is started.`,表示节点已经正常启动。 ### 4. 配置节点 + - 从tca页面`管理入口`-`节点管理`,可以看到当前在线的节点,可以修改节点名称、标签、负责人等信息。 - 可以进入工具进程配置页面,对节点支持的工具进程进行管理(默认会全部勾选),未勾选的工具进程,将不会在该节点上执行。 - 节点所属标签会与分析方案中的运行环境标签进行匹配,只有相同标签的任务才会下发到该机器节点上。 - ## 五、其他配置与用法 ### 1. 配置使用本地工具 -> 如果由于网络原因,执行时无法从github自动拉取工具,或拉取比较慢,可以参考基础配置腾讯工蜂工具地址,或使用以下方式预先下载好工具,配置使用本地工具目录。 +:::warning +如果由于网络原因,执行时无法从github自动拉取工具,或拉取比较慢,可以参考基础配置腾讯工蜂工具地址,或使用以下方式预先下载好工具,配置使用本地工具目录。 +::: - (1)下载工具配置库 `https://github.com/TCATools/puppy-tools-config.git` ,存放到 `client/data/tools`目录下(如果未生成,可先创建该目录)。 - (2)根据当前机器操作系统,查看`puppy-tools-config`目录下的`linux_tools.ini`或`mac_tools.ini`或`windows_tools.ini`文件,将`[tool_url]`中声明的所有工具下载到 `client/data/tools`目录下。 @@ -114,7 +143,9 @@ python3 codepuppy.py localscan ### 2. 使用自建git server存放工具 -> 如果自己搭建了一套git server,可以将工具配置库 `https://github.com/TCATools/puppy-tools-config.git` 以及里面声明的工具仓库,存放到自建git serevr上。 +:::warning +如果自己搭建了一套git server,可以将工具配置库 `https://github.com/TCATools/puppy-tools-config.git` 以及里面声明的工具仓库,存放到自建git serevr上。 +::: - (1)将工具配置库 `https://github.com/TCATools/puppy-tools-config.git` 上传到自建git仓库。 - (2)按所需的操作系统,将`puppy-tools-config`仓库下的`linux_tools.ini`或`mac_tools.ini`或`windows_tools.ini`文件中`[tool_url]`声明的所有工具库,上传到自建git仓库。 diff --git "a/web/packages/tca-document/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206\350\257\264\346\230\216.md" "b/web/packages/tca-document/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206\350\257\264\346\230\216.md" index 329e9608e..45082ec7f 100644 --- "a/web/packages/tca-document/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206\350\257\264\346\230\216.md" +++ "b/web/packages/tca-document/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206\350\257\264\346\230\216.md" @@ -20,6 +20,10 @@ 需平台管理员在**后台管理**-**工具管理**中找到对应工具,并将其权限状态调整为**支持自定义规则**。 ::: +## 自定义工具 +### 工具白名单 +默认自定义工具只能当前团队内使用,添加 `工具白名单` 后可以让其他团队使用。 + ## 使用场景说明 ::: tip diff --git "a/web/packages/tca-document/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\345\267\245\345\205\267.md" "b/web/packages/tca-document/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\345\267\245\345\205\267.md" index 199aff88c..249135666 100644 --- "a/web/packages/tca-document/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\345\267\245\345\205\267.md" +++ "b/web/packages/tca-document/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\345\267\245\345\205\267.md" @@ -1,6 +1,6 @@ # 自定义工具 -腾讯云代码分析平台支持用户自助添加管理工具。 +腾讯云代码分析平台支持用户自助添加代码分析工具。 适用场景:自定义规则无法满足团队业务复杂需求,需要更多的代码逻辑来匹配目标代码的情况。通常需要团队业务方自行实现对应代码分析工具。 @@ -10,62 +10,63 @@ 2. 提交工具到 git 代码库 3. 在页面创建新工具 4. 为工具添加规则 -5. 在项目分析方案中添加规则 +5. 将工具配置到执行节点 +6. 在项目分析方案中添加规则 ## 自定义工具步骤说明 ### 第一步,编写代码,实现分析工具逻辑 根据需要匹配的目标代码场景,编写对应的工具逻辑。 -可以参考 Python 写的 [Demo 项目](https://github.com/TCATools/demo_tool) +可以参考 Python 实现的 [Demo 项目](https://github.com/TCATools/demo_tool)。 **必要:** -- **运行方式**:支持命令行执行,比如 python run.py 或 run.exe,执行命令的工作目录为工具代码的根目录 +- **运行方式**:支持命令行执行,比如 python run.py 或 run.exe,执行命令的工作目录为工具代码的根目录。 -- **运行环境说明**: - - 平台已内置**Python 环境**和**Java 环境**。 - - ::: tip - 已内置 Python 环境(可以指定 Python 版本),如果依赖第三方 pip 包,不能保证已内置 - - 已内置 Java 环境(内置 JDK_8_HOME 环境变量,并在 PATH 环境变量中添加了 JDK_8_HOME 的 bin 目录,可以按需获取使用) - ::: - - **以上内置环境无法支持?** - - - 建议把依赖包内置在工具 git 库中,自行加载所需环境(比如执行时,自动将内置依赖目录,添加到 PATH 环境变量) - - - 也可以将工具打包成可执行程序(比如用 pyinstaller 打包 python 代码) +- **运行环境说明**: + - 建议将工具打包编译成可执行程序,拉取下来直接可以执行。 + - 如果工具需要在特定的环境中运行,比如python、java环境,平台提供了丰富的工具依赖包,可以在`工具管理`-`工具依赖`中查看,创建工具时可供选择,执行时会自动配置好依赖环境。 + - 如果现有的工具依赖包未支持所需依赖,也可以创建新的工具依赖使用。 - **平台已提供的环境变量** - 使用方式请参考 [Demo 项目](https://github.com/TCATools/demo_tool) - - ```python - # TCA 已提供的环境变量 + - 获取及使用方式请参考 [Demo 项目](https://github.com/TCATools/demo_tool)。 + ``` SOURCE_DIR:要扫描的代码目录路径 - DIFF_FILES: 增量文件列表 - TASK_REQUEST: 任务参数json文件路径 - - # 获取环境变量demo(Python 示例代码): - import os - source_dir = os.environ.get("SOURCE_DIR", None) + DIFF_FILES: 值为一个json文件路径,文件内容为增量扫描的文件列表(增量扫描时可用) + SCAN_FILES: 值为一个json文件路径,文件内容为需要扫描的文件列表(增量或全量扫描均可用) + TASK_REQUEST: 值为一个json文件路径,文件内容为当前扫描任务参数 ``` - + +- **工具命令声明** + + 在工具仓库根目录下,添加一个`tool.json`文件,声明工具的检查和扫描命令,比如: + ```json + { + "check_cmd": "python src/main.py check", + "run_cmd": "python src/main.py scan" + } + ``` + 参数说明: + - `check_cmd`: + - 功能:判断当前执行环境是否满足工具要求(如果不需要检查,也可以没有这个命令)。 + 比如某些工具只能在linux下执行,需要判断当前是否为linux环境。 + - 输出:将判断结果输出到`check_result.json`文件中,文件内容为`{"usable": true}`或`{"usable": false}`。 + - `run_cmd`: + - 功能:扫描代码,执行自定义检查器逻辑(该命令必须存在)。 + - 输出:按照指定格式,输出结果到`result.json`文件中。 + - **工具输出格式要求** - 将结果输出到当前工作目录下的`result.json`文件中(Python 示例代码) - + - 将扫描结果输出到当前工作目录下的`result.json`文件中(Python 示例代码) ```python import json with open("result.json", "w") as fp: json.dump(result, fp, indent=2) ``` - `result.json` 文件格式如下: - + - `result.json` 文件格式如下: ```json [ { @@ -90,25 +91,29 @@ **`refs`** 字段说明: - 非必需项,可无。该字段记录问题回溯路径信息。比如当前文件的回溯路径其他的 3 行代码,可以将这三行的路径及提示信息,按顺序添加到 refs 数组中。 + 非必需项,可无。该字段记录问题回溯路径信息。比如当前行的代码问题,是经过上下文的三行代码执行路径而导致的,可以将这三行的位置及提示信息,按顺序添加到 refs 数组中。 ### 第二步,提交工具到 git 代码库 -- 创建代码库,将工具源代码或编译打包后的可执行文件提交到代码库中 +- 创建代码库,将工具源代码或编译打包后的可执行文件,提交到代码仓库中(建议提交到master分支,TCA默认拉取的是master分支)。 - 建议代码库中加入 README.md 文件,说明工具功能和维护人 +- 建议代码库中加入 README.md 文件,说明工具功能和维护人。 -- 后续需要修改规则实现逻辑,可以直接更新代码库,TCA 平台在执行该工具时,会自动拉取最新工具版本 +- 后续需要修改工具实现逻辑,可以直接更新代码库,TCA 平台在执行该工具时,会自动拉取最新工具代码版本。 ### 第三步,在工具管理页面中创建工具 - 进入工具管理页面,点击创建工具 + ![enter image description here](../../../images/customtool_01.png) + - 填写工具信息 + ![enter image description here](../../../images/customtool_02.png) + **部分参数说明:** - - **工具仓库地址**,即前述步骤中提交的工具 git 代码库地址 + - **工具仓库地址**,即前述步骤中提交的工具 git 代码库地址,默认拉取的是master分支,如果是其他分支,需要在仓库地址后加上`#分支名`,比如:`https://github.com/xxx/xxx.git#main` - **工具认证**,授权拉取工具仓库的权限 @@ -116,23 +121,30 @@ - **环境变量**,工具执行所需的环境变量 - ::: tip - 我们已提供以下公共环境变量 + - **License**,如果是开源工具,填写工具遵循的开源协议,或者填写自研共建 - ```python - python_version = 可选,如果工具是python执行,可以指定python版本,可选值:2,3,3.8(3指的是3.7) - ``` + - **是否为编译型工具**,表示在使用该工具对用户代码进行分析时,是否要求代码需要编译或可执行编译 - ::: - - **License**,如果是开源工具,填写工具遵循的开源协议,或者填写自研共建 +- 添加工具依赖 + + ![enter image description here](../../../images/customtool_03.png) + + 添加完成后,会展示已添加的依赖方案: + + ![enter image description here](../../../images/customtool_04.png) + +**工具依赖说明:** +- 比如当前的demo工具,只需要依赖python3运行,而且支持在linux x86_64、linux arm64、mac和windows下执行,那么只需要配置一个依赖方案(如上图),并配置为默认方案。在不同的操作系统中,会自动加载对应操作系统的python环境。 +- 如果需要根据扫描项目设置的环境变量,加载不同的依赖配置,则可以配置不同的判断条件,使用多个依赖方案。 - - **是否为编译型工具**,表示在使用该工具对用户代码进行分析时,是否要求代码需要编译或可执行编译 ### 第四步,为工具添加规则 - 完成工具创建后,进入规则列表,为工具添加规则 + ![enter image description here](../../../images/customtool_05.png) + - 填写规则信息 **部分参数说明:** @@ -148,22 +160,24 @@ ### 第五步,将工具配置到执行节点 ::: tip -需要联系平台管理员协助操作,在节点管理-工具进程配置中找到对应工具,将其配置到对应机器上。 +需要联系平台管理员协助操作,在`管理入口`-`节点管理`中进入需要配置的机器节点的`工具进程配置`中,找到对应工具,勾选工具进程。 -完成节点配置工具进程后,才能在项目中采用该工具进行分析 +完成节点配置工具进程后,才能在项目中采用该工具进行分析。 ::: +![enter image description here](../../../images/customtool_06.png) + ### 第六步,完成上述操作,在项目中使用工具规则 -- 进入到项目中,在分析方案-代码检查进行规则配置 +- 进入到项目中,在`分析方案`-`代码检查`进行规则配置。 -- 点击添加规则,找到对应工具规则进行添加 +- 点击添加规则,找到对应工具规则进行添加。 -- 添加完成后,启动分析,建议启动一次全量分析 +- 添加完成后,启动分析,为了将规则应用到所有代码文件,建议启动一次全量分析(增量分析只会分析自上次扫描后变更的文件)。 ## 自定义工具权限说明 -- **默认自定义工具仅团队管理员可操作,团队内所有成员可使用,** +- **默认自定义工具仅团队管理员可操作,团队内所有成员可使用。** - 团队管理员才能创建工具,添加工具规则等,具备该工具全部权限 diff --git "a/web/packages/tca-document/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\350\247\204\345\210\231.md" "b/web/packages/tca-document/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\350\247\204\345\210\231.md" index ac81f02f2..6912b5b43 100644 --- "a/web/packages/tca-document/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\350\247\204\345\210\231.md" +++ "b/web/packages/tca-document/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\350\247\204\345\210\231.md" @@ -27,8 +27,7 @@ 1. **根据团队业务需求设计正则表达式** ::: tip - > - > 建议先测试好正则表达式是否正确,正则表达式测试网站推荐:[http://tool.oschina.net/regex](http://tool.oschina.net/regex) + 建议先测试好正则表达式是否正确,正则表达式测试网站推荐:[http://tool.oschina.net/regex](http://tool.oschina.net/regex) 规则示例: diff --git "a/web/packages/tca-document/en/guide/\345\277\253\351\200\237\345\205\245\351\227\250/\345\277\253\351\200\237\345\220\257\345\212\250\344\270\200\346\254\241\344\273\243\347\240\201\345\210\206\346\236\220.md" "b/web/packages/tca-document/en/guide/\345\277\253\351\200\237\345\205\245\351\227\250/\345\277\253\351\200\237\345\220\257\345\212\250\344\270\200\346\254\241\344\273\243\347\240\201\345\210\206\346\236\220.md" index 97bd61bf6..70bdc82cd 100644 --- "a/web/packages/tca-document/en/guide/\345\277\253\351\200\237\345\205\245\351\227\250/\345\277\253\351\200\237\345\220\257\345\212\250\344\270\200\346\254\241\344\273\243\347\240\201\345\210\206\346\236\220.md" +++ "b/web/packages/tca-document/en/guide/\345\277\253\351\200\237\345\205\245\351\227\250/\345\277\253\351\200\237\345\220\257\345\212\250\344\270\200\346\254\241\344\273\243\347\240\201\345\210\206\346\236\220.md" @@ -6,7 +6,9 @@ ![创建团队](../../../images/create_team.png) - > 还没有团队?点击了解[团队管理](../团队管理/团队管理.md) + :::tip + 还没有团队?点击了解[团队管理](../团队管理/团队管理.md) + ::: - **为团队创建一个项目,或选择一个已有项目,并进入项目内** diff --git "a/web/packages/tca-document/en/guide/\346\234\215\345\212\241\345\231\250/deploy_with_minio.md" "b/web/packages/tca-document/en/guide/\346\234\215\345\212\241\347\253\257/deploy_with_minio.md" similarity index 100% rename from "web/packages/tca-document/en/guide/\346\234\215\345\212\241\345\231\250/deploy_with_minio.md" rename to "web/packages/tca-document/en/guide/\346\234\215\345\212\241\347\253\257/deploy_with_minio.md" diff --git "a/web/packages/tca-document/en/guide/\346\234\215\345\212\241\347\253\257/deploy_without_migrate.md" "b/web/packages/tca-document/en/guide/\346\234\215\345\212\241\347\253\257/deploy_without_migrate.md" new file mode 100644 index 000000000..1eef61455 --- /dev/null +++ "b/web/packages/tca-document/en/guide/\346\234\215\345\212\241\347\253\257/deploy_without_migrate.md" @@ -0,0 +1,15 @@ +在实际的生产环境的部署过程中,团队的MySQL的管理员可能不会给到应用账号create等比较敏感的权限,这种情况下,我们可以通过手动迁移数据的方式起到和等同Django migrate的效果。 + +操作步骤: + +1. 进入Server服务工作目录后(假设工作目录为 ``/data/CodeAnalysis/server/``,以下路径均为工作目录内的相对路径) +2. 在开发环境一个有全部权限的MySQL地址,初始化数据(MySQL版本运行版本:5.7) + - 执行``vi ./scripts/config.sh``:填写一个有全部权限的MySQL数据库地址和Redis信息以及根据需要调整配置信息,主要的工程配置已提供默认值,字段说明可以查看[文档](../server/README.md) + - 执行``bash ./scripts/deploy.sh init``:初始化DB、安装依赖和运行初始化脚本 + - 使用MySQLDump工具导出表结构与数据:``mysqldump -u user -p –databases codedog_main codedog_analysis codedog_file codedog_login > codedog_all.sql`` +3. 在生产环境建数据库,详情见:``server/sql/init.sql`` +4. 连接MySQL,导入数据: + - 临时关闭外键检查: ``SET SESSION FOREIGN_KEY_CHECKS=0``,否则会因为数据中有外键关联导致导入失败 + - 导入表结构与数据: ``source /youdir/codedog_all.sql;`` + - 开启外键检查: ``SET SESSION FOREIGN_KEY_CHECKS=1`` +5. 启动服务: 直接执行 ``bash ./scripts/deploy.sh start``,无需执行 ``init``方法,否则会导致数据重复写入 diff --git "a/web/packages/tca-document/en/guide/\346\234\215\345\212\241\345\231\250/server.md" "b/web/packages/tca-document/en/guide/\346\234\215\345\212\241\347\253\257/server.md" similarity index 97% rename from "web/packages/tca-document/en/guide/\346\234\215\345\212\241\345\231\250/server.md" rename to "web/packages/tca-document/en/guide/\346\234\215\345\212\241\347\253\257/server.md" index 6ffc73417..8d1b25a01 100644 --- "a/web/packages/tca-document/en/guide/\346\234\215\345\212\241\345\231\250/server.md" +++ "b/web/packages/tca-document/en/guide/\346\234\215\345\212\241\347\253\257/server.md" @@ -130,3 +130,4 @@ File存储引擎配置 - SCMPROXY_HOST:ScmProxy服务的HOST,默认为``0.0.0.0`` - SCMPROXY_PORT:ScmProxy服务监听端口,默认为``8009`` - SCMPROXY_SENTRY_URL:ScmProxy服务异常日志上报至sentry配置 +- SCMPROXY: 通过本环境变量去指定其他服务调用ScmProxy服务的地址,默认值为``127.0.0.1:8009`` diff --git a/web/packages/tca-document/en/quickStarted/FAQ.md b/web/packages/tca-document/en/quickStarted/FAQ.md index 6ce3c01ad..481d771b8 100644 --- a/web/packages/tca-document/en/quickStarted/FAQ.md +++ b/web/packages/tca-document/en/quickStarted/FAQ.md @@ -1,7 +1,10 @@ -# TCA部署与使用常见问题 +# 常见问题 -> 该Q&A文档会持续更新,非常欢迎您的建议与共建!~ -> 如果您遇到任何未在此处列出的部署或使用问题,请在 GitHub issue 系统中进行搜索。如果仍未找到该错误消息,您可以通过[社区](../community/joingroup.md)提出问题,获得帮助。 +:::tip +该Q&A文档会持续更新,非常欢迎您的建议与共建! + +如果您遇到任何未在此处列出的部署或使用问题,请在 GitHub issue 系统中进行搜索。如果仍未找到该错误消息,您可以通过[社区](../community/joingroup.md)提出问题,获得帮助。 +::: [[toc]] @@ -140,13 +143,16 @@ RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak && \ - [Ubuntu安装Python3.7文档](https://github.com/Tencent/CodeAnalysis/blob/main/doc/references/install_python37_on_ubuntu.md) #### 1.6 执行``compose_init.sh``脚本的``pip install``提示``sha256``不匹配错误 + 在构建镜像的``pip install``步骤提示以下报错时: + ``` ERROR: THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE. If you have updated the package versions, please update the hashes. Otherwise, examine the package contents carefully; someone may have tampered with them. setuptools from https://mirrors.cloud.tencent.com/pypi/packages/fb/58/9efbfe68482dab9557c49d433a60fff9efd7ed8835f829eba8297c2c124a/setuptools-62.1.0-py3-none-any.whl#sha256=26ead7d1f93efc0f8c804d9fafafbe4a44b179580a7105754b245155f9af05a8: Expected sha256 26ead7d1f93efc0f8c804d9fafafbe4a44b179580a7105754b245155f9af05a8 Got ddaacc49de5c08c09d744573240a9a49f24f65c5c72380e972433784caa68d98 ``` + 可以执行``export ORIGIN=normal``,然后再执行``./compose_init.sh`` >注:执行``export``命令的作用是调整为``pypi``默认官方下载源进行``pip install`` @@ -187,7 +193,6 @@ ln -s /usr/local/python3/bin/gunicorn /usr/local/bin/gunicorn ln -s /usr/local/python3/bin/celery /usr/local/bin/celery ``` - ### 2. 服务启动与初始化 #### 2.1 ``compose_init.sh``脚本需要填写的密码是什么? diff --git a/web/packages/tca-document/en/quickStarted/README.md b/web/packages/tca-document/en/quickStarted/README.md deleted file mode 100644 index 678408b38..000000000 --- a/web/packages/tca-document/en/quickStarted/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# 腾讯云代码分析 - -**腾讯云代码分析**(**Code Analysis, TCA**)支持Linux、Windows和macOS等多种平台。通过以下步骤,您可以将腾讯云代码分析部署到本地,快速启动并运行您的代码分析项目。 - -> 如果您在部署或使用腾讯云代码分析的过程中遇到了问题,可以参考[常见问题](FAQ.md)。 - -## 部署Server和Web - -拉取[代码库](https://github.com/Tencent/CodeAnalysis)后,您可以通过以下两种方式部署腾讯云代码分析的Server和Web服务: - -- [通过源代码](deploySever.html#通过源代码) - -- [通过Docker-Compose](deploySever.md#通过docker-compose) - -## 创建首个代码分析项目 - -成功部署并启动Server与Web服务后,您可以使用管理员凭据登陆到腾讯云代码分析平台,开始[创建您的首个代码分析项目](.)。 - -## 部署与配置客户端 - -在启动您的首个代码分析项目前,您需要在本地部署腾讯云代码分析的客户端。完成客户端的项目配置后,即可启动您的首个代码分析项目,并在腾讯云代码分析平台上查看您的分析结果。 - -您可以通过以下三种方式部署并使用腾讯云代码分析的客户端: - -- [通过源代码](deployClient.md#通过源代码) - -- [通过Docker-Compose](deployClient.md#通过docker-compose) - -- [通过可执行文件](deployClient.md#通过可执行文件) - - -## 了解更多 - -更多关于腾讯云代码分析平台的使用指南和配置说明,参见[帮助文档](../guide/README.md)。 \ No newline at end of file diff --git a/web/packages/tca-document/en/quickStarted/codeDeploy.md b/web/packages/tca-document/en/quickStarted/codeDeploy.md new file mode 100644 index 000000000..95cd0ae1c --- /dev/null +++ b/web/packages/tca-document/en/quickStarted/codeDeploy.md @@ -0,0 +1,90 @@ +# 源码部署 +兼容旧版的部署方式 +#### 依赖环境 + +- 系统环境 + - Linux + - 最低配置:2核4G内存、100G硬盘存储空间 + +- 环境准备 +> 目前TCA脚本已封装好Python、Mariadb、Redis与Nginx安装步骤,可以按“操作说明”内容进行操作 + + - **Python 3.7**,[安装指引](./references/install_python37_on_centos.md) + + - **MySQL服务(MySQL5.7.8以上版本或Mariadb 10.5以上版本)**,[安装指引](./references/install_mysql_on_centos.md) + + - **Redis服务(4.0版本以上)**,[安装指引](./references/install_redis_on_centos.md) + + - **Nginx服务** + + :::warning + 仅适用于本地部署体验,生产环境建议使用专业的 MySQL、Redis 等服务 + ::: + +- 权限准备 + + - 环境权限:安装 Server 依赖软件(python、nginx、yum 等软件包)需要使用 ROOT 权限 + - 启动 Server服务时可以使用非 ROOT 用户运行 + - 数据库权限:Server 服务执行数据库初始化需要依赖 ``CREATE、ALTER、INDEX、DELETE、LOCK TABLES、SELECT、INSERT、REFERENCES、UPDATE`` 权限 +- 端口使用:需要开放80端口的访问权限(80为TCA平台默认访问端口),或调整 Web 服务默认的访问端口地址 + +#### 操作说明 + +##### 首次启动操作 + +1. 进入CodeAnalysis工作目录(例如``~/CodeAnalysis``),以下路径均为目录内的相对路径 +2. 安装基础软件与部署TCA(可根据脚本选项确定是否要安装相关基础软件),执行 + ```bash + $ bash ./quick_install.sh local deploy + ``` + 执行该命令会做以下事情: + - 检测本地Python3.7、Mariadb/MySQL、Redis与Nginx,如果不存在会提示安装(install) + - 部署TCA Server、Web与Client,并进行初始化(install) + - 启动TCA Server、Web与Client(start) + - 检测TCA的运行状态(check) + + >注:在运行过程中,脚本会检测本地是否安装了相关基础软件(Python3.7、MySQL/Mariadb、Redis、Nignx),如果未安装会输出以下类似提示语: + >``` + >Do you want to install [Redis] by this script? + >Please enter:[Y/N] + >``` + >如果确定通过脚本安装可以输入`Y`。 +3. 执行完成,无其他报错,即可登录: + - TCA 平台初始登录账号是``CodeDog``,密码是``admin``, + +##### 更新操作 +1. 更新代码 +2. 执行以下命令: + - `bash ./quick_install.sh local install tca`:更新相关配置 + - `bash ./quick_install.sh local start`:启动服务(会自动关闭之前的服务) + - `bash ./quick_install.sh local check`:检查服务是否启动失败 + +注: +1. `local install`命令行参数说明: + - `base`:安装Python、Mariadb/MySQL、Redis与Nginx + - `tca`:初始化或更新TCA Server、Web、Client相关配置和数据 + - `server`:初始化或更新TCA Server相关配置和数据 + - `web`:初始化或更新TCA Web相关配置和数据 + - `client`:初始化或更新TCA Client相关配置和数据 + - 不填参数,默认会执行`base`、`tca`相关操作 + +##### 启动和停止服务 + +- 启动所有服务:`bash ./quick_install.sh local start` +- 启动Main相关服务:`bash ./quick_install.sh local start main` + - `local start`支持启动指定服务,如上述的启动Main服务,还支持`mysql/redis/analysis/file/login/scmproxy/nginx/client/all` +- 停止所有服务:`.bash /quick_install.sh local stop` +- 停止Main相关服务:`bash ./quick_install.sh local stop main` + - `local stop`支持停止指定服务,如上述的停止Main服务,还支持`analysis/file/login/scmproxy/nginx/client/all` + +注: +1. 启动时会自动关闭之前已经运行的服务 +2. `local start`支持启动指定服务,如上述的启动Main服务,还支持`mysql/redis/main/analysis/file/login/scmproxy/nginx/all` + - `mysql`和`redis`默认会使用`systemctl`进行启动,如果`systemctl`无法使用,则会直接使用`nohup`方式运行相关服务 + +##### 检查服务运行状态 +检查服务运行状态:`bash ./quick_install.sh local check` + - 目前支持检查server与web,暂不支持client + +##### 获取服务输出日志 +打印TCA Server各个服务的日志路径: `bash ./quick_install.sh local log` \ No newline at end of file diff --git a/web/packages/tca-document/en/quickStarted/deployClient.md b/web/packages/tca-document/en/quickStarted/deployClient.md index 9dde5a9f2..723dd738e 100644 --- a/web/packages/tca-document/en/quickStarted/deployClient.md +++ b/web/packages/tca-document/en/quickStarted/deployClient.md @@ -4,86 +4,115 @@ ### 依赖环境 -- 系统要求 +- 系统环境 - - Linux,Windows或macOS - - [Python 3.7](https://docs.python.org/zh-cn/3.7/using/unix.html) + - Linux,Windows或macOS +- 环境准备 -### 部署步骤 + - **Python 3.7**,[安装指引](./references/install_python37_on_centos.md) -#### 部署客户端 +### 使用步骤 -1. 安装Python环境和第三方库 +#### 安装第三方库 - - 安装[Python3.7](https://docs.python.org/zh-cn/3.7/using/unix.html)、[pip3](https://pip.pypa.io/en/stable/installation/) - > 通过``python3 --version``和``pip3 --version``检查是否正确配置环境。 - - 在本地源码目录下安装依赖 - ```bash - pip3 install -r client/requirements/app_reqs.pip - ``` +```bash +# 源码根目录下执行 +pip3 install -r client/requirements/app_reqs.pip +``` -2. 安装第三方工具 +#### 安装第三方工具 - - 进入到`client/requirements`目录 - - 在命令行中执行安装脚本 - ```bash - #Linux/macOS环境 - ./install.sh - #Windows环境 - ./install.bat - ``` +```bash +# 源码根目录 +cd client/requirements + +# 执行安装脚本 +# Linux/macOS环境 +./install.sh +# Windows环境 +./install.bat +``` #### 配置客户端 -- 配置client/config.ini文件 -将``替换成实际的serve ip(可包含端口号)。 - +- 配置 `client/config.ini` 文件 + + 将 `` 替换成实际的serve ip(可包含端口号)。 + + ![客户端执行环境配置](https://tencent.github.io/CodeAnalysis/media/clientConfigIni.png) + +- 配置 `client/codedog.ini` 文件 -- 配置client/codedog.ini文件 - - 填写以下必填项:`token`,`org_sid`,`team_name`,`source_dir` - - `token`: 从tca页面获取,前往[个人中心]-[个人令牌]-复制Token - ![personalToken](../../images/personalToken.png) - - `org_sid`(团队编号),`team_name`(项目名称): 从tca项目概览页面URL中获取,项目概览URL格式:http://{域名}/t/{org_sid}/p/{team_name}/profile - ![orgsid](../../images/orgsid.png) - - `source_dir`: 本地代码目录路径 -- 按需填写其他可选项,也可以不填,按默认配置执行 + 必填项:`token`、`org_sid`、`team_name`、`source_dir` + - **个人令牌** - `token`:从 TCA 页面获取,前往[个人中心]-[个人令牌]-复制Token + + ![personalToken](../../images/personalToken.png) + + - **团队编号** - `org_sid`:进入 TCA 项目概览页,从 URL 中获取 + + - **项目名称** - `team_name`::进入 TCA 项目概览页,从 URL 中获取 + + :::tip + 项目概览URL格式:`http://{域名}/t/{org_sid}/p/{team_name}/profile` + ::: + + - **分析路径** - `source_dir`: 本地代码目录路径 + + :::tip + - 如果项目代码为编译型语言(比如:C/C++,C#,Go,Java,Kotlin,Objective-C等),且使用的分析方案中配置了编译型工具(如图,使用了OC推荐规则包),需要填写`build_cmd`编译命令。 + + - 其他可选项按需填写,不填写时按默认配置执行 + ::: + #### 启动客户端 -进入到`client`目录下,执行客户端脚本 ```bash +# 源码根目录 +cd client + +# 执行客户端脚本 python3 codepuppy.py localscan ``` -> 使用`localscan`命令启动本地单次的代码分析,如需启动分布式并行分析任务,请参考[常驻节点分析](../guide/%E5%AE%A2%E6%88%B7%E7%AB%AF/%E5%B8%B8%E9%A9%BB%E8%8A%82%E7%82%B9%E5%88%86%E6%9E%90.md)进行配置。 + +:::warning +Client 的实现及启动脚本均依赖 Python3 版本为 3.7,可执行 ``python3 --version`` 查看版本。若版本有误,可安装版本为3.7的python并软链接到python3命令。 +::: + +:::tip + +- `codedog.ini` 各项参数可由命令行传入,获取详细参数说明可运行 `python3 codepuppy.py localscan -h` + +- 使用`localscan`命令启动本地单次的代码分析,如需启动分布式并行分析任务,请参考[使用分布式节点模式](../client/README.md#五使用分布式节点模式执行客户端)进行配置。 +::: ## 通过Docker-Compose -> 适用于快速上手体验。使用docker运行,可以免去客户端环境依赖的安装,避免环境兼容性问题。 -> 但是由于环境受限于docker,会无法复用本地的编译环境,部分需要编译的工具无法使用。 +:::tip +适用于快速上手体验。使用docker运行,可以免去客户端环境依赖的安装,避免环境兼容性问题。 +但是由于环境受限于docker,会无法复用本地的编译环境,部分需要编译的工具无法使用。 +::: -### 部署步骤 +### 使用步骤 #### 配置客户端 -- 配置client/config.ini文件 -将``替换成实际的serve ip(可包含端口号)。 - +- 配置 `client/config.ini` 文件 + +- 配置 `client/codedog.ini` 文件 -- 配置client/codedog.ini文件 - - 填写以下必填项:`token`,`org_sid`,`team_name`,`source_dir` - - `token`: 从tca页面获取,前往[个人中心]-[个人令牌]-复制Token - ![personalToken](../../images/personalToken.png) - - `org_sid`(团队编号),`team_name`(项目名称): 从tca项目概览页面URL中获取,项目概览URL格式:http://{域名}/t/{org_sid}/p/{team_name}/profile - ![orgsid](../../images/orgsid.png) - - `source_dir`: 本地代码目录路径 -- 按需填写其他可选项,也可以不填,按默认配置执行 +:::tip +同通过源代码使用-[配置客户端](./deployClient.md#配置客户端) +::: -#### 构建客户端镜像 +#### 构建镜像 1. 安装Docker,安装教程:[官方文档](https://docs.docker.com/engine/install/) + 2. 安装Docker-Compose,安装教程:[官方文档](https://docs.docker.com/compose/install/) + 3. 进入`client`目录,构建docker镜像 ```bash @@ -94,7 +123,8 @@ docker build -t tca-client . ##### 方案一:直接使用docker运行 -1. 进入`client`目录,执行以下命令 +进入`client`目录,执行以下命令 + ```bash # 按照实际情况填写`SOURCE_DIR`环境变量值 export SOURCE_DIR=需要扫描的代码目录绝对路径 @@ -104,26 +134,28 @@ docker run -it --rm -v $PWD:/workspace/client -v $SOURCE_DIR:/workspace/src -- ##### 方案二:使用docker内bash终端运行 1. 进入docker容器内的bash终端 -```bash -# 按照实际情况填写`SOURCE_DIR`环境变量值 -export SOURCE_DIR=需要扫描的代码目录绝对路径 -docker run -it --rm -v $PWD:/workspace/client -v $SOURCE_DIR:/workspace/src --name tca-client tca-client bash -``` + + ```bash + # 按照实际情况填写`SOURCE_DIR`环境变量值 + export SOURCE_DIR=需要扫描的代码目录绝对路径 + docker run -it --rm -v $PWD:/workspace/client -v $SOURCE_DIR:/workspace/src --name tca-client tca-client bash + ``` + 2. 通过命令行启动client代码 -```bash -python3 codepuppy.py localscan -``` + + ```bash + python3 codepuppy.py localscan + ``` ## 通过可执行文件 ### 依赖环境 -- 系统要求 - - - Linux,Windows或macOS +- 系统环境 + - Linux,Windows或macOS -### 部署步骤 +### 使用步骤 #### 下载客户端 @@ -133,23 +165,18 @@ python3 codepuppy.py localscan #### 配置客户端 -- 配置client/config.ini文件 -将``替换成实际的serve ip(可包含端口号)。 - +- 配置 `client/config.ini` 文件 -- 配置client/codedog.ini文件 - - 填写以下必填项:`token`,`org_sid`,`team_name`,`source_dir` - - `token`: 从tca页面获取,前往[个人中心]-[个人令牌]-复制Token - ![personalToken](../../images/personalToken.png) - - `org_sid`(团队编号),`team_name`(项目名称): 从tca项目概览页面URL中获取,项目概览URL格式:http://{域名}/t/{org_sid}/p/{team_name}/profile - ![orgsid](../../images/orgsid.png) - - `source_dir`: 本地代码目录路径 -- 按需填写其他可选项,也可以不填,按默认配置执行 +- 配置 `client/codedog.ini` 文件 + +:::tip +同通过源代码使用-[配置客户端](./deployClient.md#配置客户端) +::: #### 启动客户端 进入到`client`目录下,执行客户端 + ```bash ./codepuppy localscan ``` -> 使用`localscan`命令启动本地单次的代码分析,如需启动分布式并行分析任务,请参考[常驻节点分析](../guide/%E5%AE%A2%E6%88%B7%E7%AB%AF/%E5%B8%B8%E9%A9%BB%E8%8A%82%E7%82%B9%E5%88%86%E6%9E%90.md)进行配置。 \ No newline at end of file diff --git a/web/packages/tca-document/en/quickStarted/deploySever.md b/web/packages/tca-document/en/quickStarted/deploySever.md index d1e8f2ec9..7ee1a6e54 100644 --- a/web/packages/tca-document/en/quickStarted/deploySever.md +++ b/web/packages/tca-document/en/quickStarted/deploySever.md @@ -1,137 +1,130 @@ -# 部署Server和Web +# 部署 TCA +TCA提供部署脚本,支持一键式快速部署Server、Web、Client。 +脚本共提供三种部署方式:Docker部署(推荐)、[Docker-Compose部署](./dockercomposeDeploy.md)、[源码部署](./codeDeploy.md),可根据您的具体使用场景任意选择其一进行部署。 -## 通过源代码 +## Docker快速部署 -### 依赖环境 - -- 系统要求 - - - Linux - - [Pythn 3.7](https://docs.python.org/zh-cn/3.7/using/unix.html) - - [MySQL 5.7.8](https://dev.mysql.com/doc/mysql-installation-excerpt/5.7/en/) 或更高版本 - - [Redis 4.0](https://redis.io/docs/getting-started/installation/install-redis-on-linux/) 或更高版本 - - [Nginx 1.20.2](https://nginx.org/en/docs/install.html) 或更高版本 - -- 硬件要求 - - - 2核4G内存 - - 100G可用硬盘存储空间 - -- 权限要求 +:::warning +仅适用于Docker部署体验,生产环境建议使用专业的 MySQL、Redis 等服务 +::: - - 安装Server依赖软件(python、nginx、yum软件包)需要使用ROOT权限(启动Server服务时可以使用非ROOT用户运行) - - 需要开放80端口的访问权限(80为TCA平台访问端口) - - Server服务执行数据库初始化需要依赖``CREATE、ALTER、INDEX、DELETE、LOCK TABLES、SELECT、INSERT、REFERENCES、UPDATE``权限 +### 依赖环境 +- 系统环境 + - Linux、macOS、Windows + - 最低配置:2核4G内存、100G硬盘存储空间 +- 环境准备 + - Docker +- 权限准备 + - 需要开放80、8000端口的访问权限(80为TCA平台访问端口,8000为TCA Server访问端口) -### 安装步骤 +### 部署对象 +Server、Web 与 Client -#### 部署Server +### 操作说明 +#### 首次启动操作 -1. 进入Server服务工作目录(例如 ``~/CodeAnalysis/server/``),以下路径均为目录内的相对路径 -2. 配置MySQL和Redis服务,初始化数据(MySQL版本运行版本:5.7) - - 填写数据库和Redis信息以及根据需要调整配置信息,主要的工程配置已提供默认值,字段说明详见[TCA Server](../references/parameters/server.md)。 +1. 进入CodeAnalysis工作目录(例如``~/CodeAnalysis``),以下路径均为目录内的相对路径 +2. 执行命令: ```bash - vi ./scripts/config.sh - ``` - - 初始化DB、安装依赖和运行初始化脚本 - ```bash - bash ./scripts/deploy.sh init - ``` - - 将安装好的``celery``与``gunicorn``可执行文件建立软链接到``/usr/local/bin``路径下 - ```bash - # /path/to/需要替换为celery可执行命令实际的路径,一般在python安装路径的bin目录下,如/usr/local/python3/bin/ - ln -s /path/to/celery /usr/local/bin/celery - # /path/to/需要替换为gunicorn可执行命令实际的路径,一般在python安装路径的bin目录下,如/usr/local/python3/bin/ - ln -s /path/to/gunicorn /usr/local/bin/gunicorn - ``` - - 使环境变量生效,避免出现unknown command错误 - ```bash - export PATH=/usr/local/bin:$PATH - ``` -3. 启动/停止服务 - ```bash - # 启动服务 - bash ./scripts/deploy.sh start - # 停止服务 - bash ./scripts/deploy.sh stop + bash ./quick_install.sh docker deploy ``` +::: tip +通过Docker部署默认会在当前根目录下的挂载三个路径: +- `.docker_temp/logs`:容器内的`/var/log/tca/`,存放TCA平台的日输出文件; +- `.docker_temp/data`:容器内的`/var/opt/tca/`, 存放TCA平台的服务数据,主要是Mariadb、Redis; +- `.docker_temp/configs`:容器内的``/etc/tca``,存放TCA平台的配置文件,主要是`config.sh` +::: -#### 部署Web +#### 更新操作 +1. 更新代码 +2. 执行以下命令: + - `TCA_IMAGE_BUILD=true ./quick_install.sh docker deploy`:重新构建并启动tca容器 +::: tip +`TCA_IMAGE_BUILD=true`表示从本地构建TCA镜像运行 +::: +#### 运行容器 +如果已经在机器上执行过``docker deploy``,并保留容器数据的,可以执行以下命令启动容器,继续运行TCA -1. 完成部署Server并启动服务,后端服务默认登陆账号/密码为:`CodeDog/admin`。 +```bash +bash ./quick_install.sh docker start +``` -2. 进入web服务部署目录(例如 ``~/CodeAnalysis/web/tca-deploy-source``),以下路径均为目录内的相对路径 +#### 停止容器 +如果容器正在运行,希望停止容器,可以运行 -3. 部署/更新前端服务 +```bash +bash ./quick_install.sh docker stop +``` - ```bash - # 部署、更新都使用此命令 - bash ./scripts/deploy.sh init -d - ``` +# 使用TCA +成功部署TCA后,请开始您的代码分析。 +## 进入平台页面 - 具体请查阅部署脚本内容,可根据业务调整配置。 +在浏览器输入`http://部署机器IP/`,点击立即体验,完成登录后即可跳转到团队列表页 -3. **额外说明** +:::tip +默认平台登录账号/密码:CodeDog/admin - `tca-deploy-source/scripts/config.sh` 已配置默认环境变量,用户可根据需要调整环境变量再部署前端服务,具体可查阅脚本内容。 +如部署过程中,已调整默认账号密码,请按照调整后的账号密码进行登录 +::: +## 创建团队及项目 -## 通过Docker-Compose +- 完成团队创建 -### 依赖环境 +- 完成项目创建 -- 系统要求 +## 登记代码库 - - Linux,Windows或macOS - - [Docker](https://docs.docker.com/engine/install/) - > Compose file format需要为3.0及以上,Docker版本要求可以参考[官方文档](https://docs.docker.com/compose/compose-file/compose-file-v3/#compose-and-docker-compatibility-matrix) - - [Docker-Compose 1.26](https://docs.docker.com/compose/install/) 或更高版本 +登记代码库,输入代码库地址以及凭证信息等,完成代码库登记。 -- 硬件要求 +![registerCodeRepo](../../images/registerCodeRepo.png) - - 2核4G内存 - - 100G可用硬盘存储空间 +## 创建分析项目 -- 权限要求 - - - 需要开放80、8000端口的访问权限(80为TCA平台访问端口,8000为TCA Server访问端口) +![开始分析](../../images/start_scan_02.png) -### 部署步骤 +::: tip +1. 用户可选择使用分析方案模板,或创建分析方案的方式,利用方案的分析配置进行代码分析。 +2. 点击确认时,平台会首先创建该代码库的分析方案,然后根据代码库分支、当前分析方案创建分支项目。 +::: -#### 方案一:一键部署 +### 分析方案说明 -拉取代码进入源码根目录,执行``./quick_start.sh``命令,即可自动安装Docker、Docker-Compose和启动Server与Web服务 +- 分析方案是用于对代码库进行分析的一套配置集合。 ->- ``quick_start.sh``脚本中会自动下载[Docker安装脚本](https://get.docker.com)、启动Docker服务、下载``docker-compose``可执行文件以及执行``compose_init.sh``脚本启动Server、Web服务 ->- 如果提示脚本没有执行权限,可以在源码执行命令:``chmod +x compose_init.sh quick_start.sh`` +- 更多分析方案配置可查阅[帮助文档-分析方案](../guide/分析方案/基础属性配置.md) +![creataAnalysePlan](../../images/creataAnalysePlan.png) -#### 方案二:手动部署 +::: tip +本次部署会默认启动运行环境为「Codedog_Linux」的客户端,若需扩展更多运行环境,详见客户端[常驻节点分析](../guide/客户端/常驻节点分析.md) +::: -1. 安装Docker,安装教程:[官方文档](https://docs.docker.com/engine/install/) -2. 安装Docker-Compose,安装教程:[官方文档](https://docs.docker.com/compose/install/) -3. 拉取代码并进入源码根目录后,执行 ``./compose_init.sh`` 命令,即可启动Server与Web服务 +![planPage](../../images/planPage.png) +## 执行代码分析 ->- 如果提示脚本没有执行权限,可以在源码执行命令:``chmod +x compose_init.sh`` ->- 首次启动会构建相关镜像,耗时会比较久 +初始化创建项目后,可通过 `在线分析` 或 `客户端分析` 来启动代码分析。 -``compose_init.sh``脚本会包含各个服务的初始化操作 +![代码分析](../../images/start_scan_06.png) -#### 启动/停止服务 +::: tip +- TCA推荐使用`在线分析`,您可根据具体使用场景选择其一。 +- `在线分析`表示配置代码库链接后,TCA客户端拉取代码后进行分析;`客户端分析`在配置本地待扫描代码路径后,无需代码拉取直接分析本地代码。 +- `在线分析`与`客户端分析`具体详情及配置参考[TCA客户端配置详情](../guide/客户端/配置详情.md) +::: -进入源码目录后,执行``docker-compose up -d``命令,即可启动Server与Web服务。执行``docker-compose stop``命令,即可停止Server与Web服务。 +## 查看分析历史 -### 常见问题 +分析结束后,数据会上报到服务端。可进入分析历史页面查看分析记录以及分析结果。 -- Q:如何查看服务启动的日志? +![分析历史](../../images/start_scan_05.png) - A:可以先找服务名称,执行``docker-compose logs -f xxx``,xxx即服务的名称,比如``main-server``、``main-worker``等 +## 查看分析概览 -- Q:TCA初始登录账号密码是什么? - - A:初始登录账号是``CodeDog``,密码是``admin``,如果想要自定义,在初始化前,可以在``server/dockerconfs/.env.local``对``TCA_DEFAULT_ADMIN``和``TCA_DEFAULT_PASSWORD``变量值进行调整。如果初始化完成后需要调整,则需要登录到平台的``用户管理``页面进行调整。 +分析结束后,进入分支概览可以查看该分支指定分析方案的概览数据以及 [问题列表](../guide/代码检查/分析结果查看.md) 等。 -**详细Q&A文档可以查阅[TCA使用常见问题](FAQ.md)** +![分支概览](../../images/start_scan_04.png) \ No newline at end of file diff --git a/web/packages/tca-document/en/quickStarted/dockercomposeDeploy.md b/web/packages/tca-document/en/quickStarted/dockercomposeDeploy.md new file mode 100644 index 000000000..e9062bb65 --- /dev/null +++ b/web/packages/tca-document/en/quickStarted/dockercomposeDeploy.md @@ -0,0 +1,49 @@ +# Docker-Compose快速部署 +#### 部署对象 +Server、Web 与 Client + +:::warning +仅适用于Docker-Compose部署体验,生产环境建议使用专业的 MySQL、Redis 等服务 +兼容之前的部署方式 +::: + +#### 操作说明 +##### 首次启动操作 + +1. 进入CodeAnalysis工作目录(例如``~/CodeAnalysis``),以下路径均为目录内的相对路径 +2. 执行命令: + - `bash ./quick_install.sh docker-compose deploy`:启动tca_server容器 + +注意:通过Docker-Compose部署默认会在当前根目录下的挂载三个路径: + +- `.docker_data/logs`:存放TCA平台的各个服务日志输出目录; +- `.docker_data/mysql`:存放TCA平台的MySQL数据 +- `.docker_data/redis`:存放TCA平台的Redis数据 +- `.docker_data/filedata`:存放TCA平台文件服务器的文件 + +##### 更新操作 +1. 更新代码 +2. 执行以下命令: + - `bash ./quick_install.sh docker-compose build`:重新构建TCA相关镜像 + - `bash ./quick_install.sh docker-compose deploy`: 重新部署TCA相关容器与初始化(或刷新数据) + +##### 运行操作 +如果已经在机器上执行过``docker-compose deploy``,并保留容器数据的,可以执行以下命令启动容器,继续运行TCA + +```bash +bash ./quick_install.sh docker-compose start +``` + +##### 停止操作 +如果容器正在运行,希望停止容器,可以执行以下命令 + +```bash +bash ./quick_install.sh docker-compose stop +``` + +##### 构建镜像操作 +如果希望构建镜像,可以执行以下命令 + +``` +bash ./quick_install.sh docker-compose build +``` \ No newline at end of file diff --git a/web/packages/tca-document/en/quickStarted/initRepo.md b/web/packages/tca-document/en/quickStarted/initRepo.md new file mode 100644 index 000000000..f96467f0f --- /dev/null +++ b/web/packages/tca-document/en/quickStarted/initRepo.md @@ -0,0 +1,45 @@ +# 创建代码分析项目 + +成功部署并启动 Server 与 Web 服务后,通过以下步骤创建您的第一个代码分析项目。 + +## 进入平台页面 + +在浏览器输入`http://部署机器IP/`,点击立即体验,完成登录后即可跳转到团队列表页 + +:::tip +默认平台登录账号/密码:CodeDog/admin + +如部署过程中,已调整默认账号密码,请按照调整后的账号密码进行登录 +::: + +## 创建团队及项目 + +- 完成团队创建 + +- 完成项目创建 + +## 登记代码库 + +登记代码库,输入代码库地址以及凭证信息等,完成代码库登记。 + +![registerCodeRepo](../../images/registerCodeRepo.png) + +## 创建分析项目 + +![开始分析](../../images/start_scan_02.png) + +::: tip + +1. 用户可选择使用分析方案模板,或创建分析方案的方式,利用方案的分析配置进行代码分析。 +2. 点击确认时,平台会首先创建该代码库的分析方案,然后根据代码库分支、当前分析方案创建分支项目。 +::: + +### 分析方案说明 + +- 分析方案是用于对代码库进行分析的一套配置集合。 + +- 更多分析方案配置可查阅[帮助文档-分析方案](../guide/分析方案/基础属性配置.md) + +![creataAnalysePlan](../../images/creataAnalysePlan.png) + +![planPage](../../images/planPage.png) diff --git a/web/packages/tca-document/en/quickStarted/intro.md b/web/packages/tca-document/en/quickStarted/intro.md index 13a951a3d..055ce7737 100644 --- a/web/packages/tca-document/en/quickStarted/intro.md +++ b/web/packages/tca-document/en/quickStarted/intro.md @@ -1,34 +1,36 @@ -# 腾讯云代码分析 +# 平台概述 -**腾讯云代码分析**(**Code Analysis, TCA**)支持Linux、Windows和macOS等多种平台。通过以下步骤,您可以将腾讯云代码分析部署到本地,快速启动并运行您的代码分析项目。 +**腾讯云代码分析**(Tencent Cloud Code Analysis,简称TCA,内部曾用研发代号 **CodeDog** )是集众多分析工具的云原生、分布式、高性能的代码综合分析跟踪平台,包含服务端、Web端和客户端三个组件,已集成一批自研工具,同时也支持动态集成业界各编程语言的分析工具。 -> 如果您在部署或使用腾讯云代码分析的过程中遇到了问题,可以参考[常见问题](FAQ.md)。 +### 使用TCA Action快速体验 +使用TCA Action,只需要在代码仓库中添加`.github/workflows/tca.yml`文件,就可以直接在GitHub工作流中快速体验代码分析。请参考:[TCA-action指引](https://github.com/TCATools/TCA-action/blob/main/README.md) -## 部署Server和Web +### 部署TCA -拉取[代码库](https://github.com/Tencent/CodeAnalysis)后,您可以通过以下两种方式部署腾讯云代码分析的Server和Web服务: +拉取 [代码库](https://github.com/Tencent/CodeAnalysis) 后,您可以通过以下三种方式部署腾讯云代码分析平台: -- [通过源代码](deploySever.html#通过源代码) +- [通过 Docker 部署](./deploySever.md#通过docker) -- [通过Docker-Compose](deploySever.md#通过docker-compose) +- [通过源代码](./codeDeploy.md#通过源代码) -## 创建首个代码分析项目 +- [通过 Docker-Compose 部署](./dockercomposeDeploy.md#通过docker-compose) -成功部署并启动Server与Web服务后,您可以使用管理员凭据登陆到腾讯云代码分析平台,按照[指引](setup.md)创建您的首个代码分析项目。 +### 创建首个代码分析项目 -## 部署与配置客户端 +成功部署并启动TCA后,您可以按照 [指引](./deploySever.md) 创建您的首个代码分析项目。 -在启动您的首个代码分析项目前,您需要在本地部署腾讯云代码分析的客户端。完成客户端的项目配置后,即可启动您的首个代码分析项目,并在腾讯云代码分析平台上查看您的分析结果。 +:::tip +默认平台登录账号/密码:CodeDog/admin +::: -您可以通过以下三种方式部署并使用腾讯云代码分析的客户端: +### 快速扩展客户端 -- [通过源代码](deployClient.md#通过源代码) +TCA客户端支持通过可执行文件进行快速扩展部署,详见[通过可执行文件](./deployClient.md#通过可执行文件) -- [通过Docker-Compose](deployClient.md#通过docker-compose) +:::tip +客户端可在本地执行代码分析,也可以作为[在线常驻节点](../advanced/任务分布式执行.md)进行在线分析。 +::: -- [通过可执行文件](deployClient.md#通过可执行文件) +### 了解更多 - -## 了解更多 - -更多关于腾讯云代码分析平台的使用指南和配置说明,参见[帮助文档](../guide/README.md)。 \ No newline at end of file +更多关于腾讯云代码分析平台的使用指南和配置说明,参见[帮助文档](../guide/README.md)。 diff --git a/web/packages/tca-document/en/quickStarted/references/install_mysql_on_centos.md b/web/packages/tca-document/en/quickStarted/references/install_mysql_on_centos.md new file mode 100644 index 000000000..0337b5cfc --- /dev/null +++ b/web/packages/tca-document/en/quickStarted/references/install_mysql_on_centos.md @@ -0,0 +1,102 @@ +# 在 CentOS 安装 MySQL + +## 注意 + +本文档仅供参考,不适用于正式环境部署,正式环境建议使用专业的MySQL服务(比如[腾讯云的MySQL产品](https://cloud.tencent.com/product/cdb)) + +## 环境 + +CentOS 7.3 版本 + +## 安装 mysql yum源 + +```bash +wget https://repo.mysql.com//mysql57-community-release-el7-11.noarch.rpm +yum localinstall mysql57-community-release-el7-11.noarch.rpm +``` + +安装成功后,查看MySQL版本: + +```bash +yum repolist all | grep mysql +``` + +输出结果: + +```bash +mysql-cluster-7.5-community/x86_64 MySQL Cluster 7.5 Community 禁用 +mysql-cluster-7.5-community-source MySQL Cluster 7.5 Community - 禁用 +mysql-cluster-7.6-community/x86_64 MySQL Cluster 7.6 Community 禁用 +mysql-cluster-7.6-community-source MySQL Cluster 7.6 Community - 禁用 +!mysql-connectors-community/x86_64 MySQL Connectors Community 启用: 221 +mysql-connectors-community-source MySQL Connectors Community - S 禁用 +!mysql-tools-community/x86_64 MySQL Tools Community 启用: 135 +mysql-tools-community-source MySQL Tools Community - Source 禁用 +mysql-tools-preview/x86_64 MySQL Tools Preview 禁用 +mysql-tools-preview-source MySQL Tools Preview - Source 禁用 +mysql55-community/x86_64 MySQL 5.5 Community Server 禁用 +mysql55-community-source MySQL 5.5 Community Server - S 禁用 +mysql56-community/x86_64 MySQL 5.6 Community Server 禁用 +mysql56-community-source MySQL 5.6 Community Server - S 禁用 +!mysql57-community/x86_64 MySQL 5.7 Community Server 启用: 544 +mysql57-community-source MySQL 5.7 Community Server - S 禁用 +mysql80-community/x86_64 MySQL 8.0 Community Server 禁用 +mysql80-community-source MySQL 8.0 Community Server - S 禁用 +``` + +## 安装MySQL + +```bash +yum install mysql-community-server +``` + +>1.如遇以下报错,可尝试运行`yum install mysql-community-server --nogpgcheck`安装 +> Public key for mysql-community-libs-compat-5.7.37-1.el7.x86_64.rpm is not installed +> Failing package is: mysql-community-libs-compat-5.7.37-1.el7.x86_64 +> GPG Keys are configured as: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql +>2.如遇以下报错,可执行`yum module disable mysql`后重试安装 +>All matches were filtered out by modular filtering for argument: mysql-community-serve +>Error: Unable to find a match: mysql-community-server + +## 配置MySQL服务 + +安装好的MySQL配置文件路径是``/etc/my.cnf``,这里可以根据需要调整,比如可以调整: + +- datadir:MySQL存放数据的路径,如果有额外挂载磁盘,可以考虑指向相关路径 + +## 启动MySQL服务 + +```bash +systemctl start mysqld +``` + +确认MySQL正常启动 + +```bash +systemctl status mysqld +``` + +查看生成 MySQL root用户临时密码: + +```bash +grep 'temporary password' /var/log/mysqld.log +``` + +### 修改root用户密码 + +连接MySQL服务 + +```bash +$ mysql -uroot -p +# 输出上述查询到的临时密码 +``` + +修改root用户的密码(下面是改成 ``Password@2021``,这里根据自行需要进行调整): + +```SQL +ALTER USER 'root'@'localhost' IDENTIFIED BY 'Password@2021'; +``` + +## 参考文档 + +- [Installing MySQL on Linux Using the MySQL Yum Repository](https://dev.mysql.com/doc/refman/5.7/en/linux-installation-yum-repo.html) diff --git a/web/packages/tca-document/en/quickStarted/references/install_nginx_from_source.md b/web/packages/tca-document/en/quickStarted/references/install_nginx_from_source.md new file mode 100644 index 000000000..ca924bf4a --- /dev/null +++ b/web/packages/tca-document/en/quickStarted/references/install_nginx_from_source.md @@ -0,0 +1,136 @@ +# 源码安装 Nginx + +## 运行环境 + +1. 基于x86_64的CentOS +2. 基于aarch64(鲲鹏920)的UOS V20 +3. 基于aarch64(飞腾2000)的TencentOS Server + +## 环境准备 + +安装编译打包需要的工具 + +```bash +yum -y install gcc zlib-devel pcre-devel bzip2-devel openssl-devel readline-devel +``` +> Ubuntu: ``apt install gcc libssl-dev zlib1g-dev libpcre3-dev libbz2-dev libreadline-dev`` + +## 下载源码 + +```bash +wget http://nginx.org/download/nginx-1.20.2.tar.gz +``` + +## 解压安装 + +```bash +# 解压 +$ tar zvxf nginx-1.20.2.tar.gz -C /usr/local/src + +# 进入源码目录 +$ cd /usr/local/src/nginx-1.20.2 + +# 配置 +$ ./configure \ +--sbin-path=/usr/local/nginx/nginx \ +--conf-path=/etc/nginx/nginx.conf \ +--pid-path=/run/nginx.pid \ +--with-stream \ +--with-http_ssl_module --with-http_v2_module --with-http_auth_request_module + +# 构建nginx +$ make -j4 +$ make install +$ make clean + +# 建立软链 +$ ln -s /usr/local/nginx/nginx /usr/local/bin/nginx +``` + +## 添加nginx配置文件 + +```bash +mkdir /etc/nginx/conf.d/ +vi /etc/nginx/nginx.conf +``` + +检查``nginx.conf``配置文件: + +1. 检查``pid /run/nginx.pid``,如果缺失或被注释则加上,加上位置如下所示: +2. 检查是否缺失这一行``include conf.d/*.conf;``,如果缺失则加上,加上位置如下所示: + +```bash +# ...省略内容 +#pid logs/nginx.pid; # 默认有的 +pid /run/nginx.pid; + +events { + # ...省略内容 +} + +# ...省略内容 + +http { + # ...省略内容 + # + include conf.d/*.conf; + server { + # ...省略内容 + } +} + +``` + +后续可以将nginx配置文件放置到``/etc/nginx/conf.d/``目录下 + + + +## 配置开机自动启动 + +```bash +vim /usr/lib/systemd/system/nginx.service +``` + +输入以下内容: + +```bash +[Unit] +Description=The nginx HTTP and reverse proxy server +After=network-online.target remote-fs.target nss-lookup.target +Wants=network-online.target + +[Service] +Type=forking +PIDFile=/run/nginx.pid +# Nginx will fail to start if /run/nginx.pid already exists but has the wrong +# SELinux context. This might happen when running `nginx -t` from the cmdline. +# https://bugzilla.redhat.com/show_bug.cgi?id=1268621 +ExecStartPre=/bin/rm -f /run/nginx.pid +ExecStartPre=/usr/local/bin/nginx -t +ExecStart=/usr/local/bin/nginx +ExecReload=/usr/local/bin/nginx -s reload +ExecStop=/usr/local/bin/nginx -s stop +KillSignal=SIGQUIT +TimeoutStopSec=5 +KillMode=process +PrivateTmp=true + +[Install] +WantedBy=multi-user.target +``` + +启动nginx: + +```bash +systemctl start nginx +``` + +开机自动启动nginx: + +```bash +systemctl enable nginx +``` + +## 参考文档 + +- [Nginx官方文档](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/#downloading-the-sources) diff --git a/web/packages/tca-document/en/quickStarted/references/install_python37_on_centos.md b/web/packages/tca-document/en/quickStarted/references/install_python37_on_centos.md new file mode 100644 index 000000000..ac782a828 --- /dev/null +++ b/web/packages/tca-document/en/quickStarted/references/install_python37_on_centos.md @@ -0,0 +1,109 @@ +# 在 CentOS 安装 Python3.7 + +## 下载Python源码包 + +```bash +wget https://www.python.org/ftp/python/3.7.12/Python-3.7.12.tgz +``` + +## 安装前准备 + +安装依赖组件 + +```bash +yum -y install wget zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make libffi-devel xz-devel +``` + +## 解压安装 + +```bash +# 解压到/usr/local/src目录 +$ tar zvxf Python-3.7.12.tgz -C /usr/local/src +$ cd /usr/local/src/Python-3.7.12 +# 编译前配置 +$ ./configure prefix=/usr/local/python3 --enable-shared +# 编译构建 +$ make -j8 +# 安装Python +$ make install +# 清理编译产出的中间文件 +$ make clean +# 链接构建产出的Python可执行文件到/usr/local/bin目录 +$ ln -s /usr/local/python3/bin/python3 /usr/local/bin/python +# 链接构建产出的pip3可执行文件到/usr/local/bin目录 +$ ln -s /usr/local/python3/bin/pip3 /usr/local/bin/pip +# 链接构建产出的Python动态库 +$ ln -s /usr/local/python3/lib/libpython3.7m.so.1.0 /usr/lib/libpython3.7m.so.1.0 +# 配置动态库 +$ ldconfig +``` + +## 检查 + +检查Python版本是否安装成功 + +```bash +$ python --version +Python 3.7.12 # 正常输出,表示安装成功 +``` + +注: + +1. 链接到/usr/local/bin/目录不会影响系统软件(比如yum)的使用,因为 yum 工具指定的Python路径是``/usr/bin/python`` +2. 一般情况下,PATH配置是先``/usr/local/bin``再``/usr/bin`` +3. 检查``python -v``输出结果是否为``Python 3.7.12``版本,如果不是该版本,可能影响后续依赖安装和服务运行 + +## pypi下载源配置 + +pip默认是到``pypi``官方源下载第三方依赖包,下载速度可能会比较慢,可以考虑调整为腾讯云的``pypi``下载源,调整方式: + +```bash +mkdir ~/.pip/ +echo "extra-index-url = https://mirrors.cloud.tencent.com/pypi/simple" >> ~/.pip/pip.conf +``` + +## 一键安装脚本 +以下脚本内容是上面的步骤集合,省去了复制粘贴的重复动作。 +1. 创建文件 `install_py37.sh`,写入以下 shell 脚本 +2. 赋予执行权限,`chmox +x install_py37.sh` +3. 执行脚本,`./install_py37.sh` + +```bash +#!/bin/env bash + +## 下载 Python 源码,如果已下载源码在脚本当前目录下,可注释跳过下载步骤 +wget https://www.python.org/ftp/python/3.7.12/Python-3.7.12.tgz + +## 安装编译依赖组件 +yum -y install wget zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make libffi-devel xz-devel + +## 解压安装 +# 解压到/usr/local/src目录 +tar zvxf Python-3.7.12.tgz -C /usr/local/src +cd /usr/local/src/Python-3.7.12 +# 编译前配置 +./configure prefix=/usr/local/python3 --enable-shared +# 编译构建 +make -j8 +# 安装Python +make install +# 清理编译产出的中间文件 +make clean +# 链接构建产出的Python可执行文件到/usr/local/bin目录 +ln -s /usr/local/python3/bin/python3 /usr/local/bin/python +# 链接构建产出的pip3可执行文件到/usr/local/bin目录 +ln -s /usr/local/python3/bin/pip3 /usr/local/bin/pip +# 链接构建产出的Python动态库 +ln -s /usr/local/python3/lib/libpython3.7m.so.1.0 /usr/lib/libpython3.7m.so.1.0 +# 配置动态库 +ldconfig + +## 检查Python版本是否安装成功 +echo -e "\033[1;42;37m[$(date "+%Y/%m/%d %H:%M:%S")] [Check]: 检查Python版本\033[0m" +python --version +echo -e "\033[1;42;37m[$(date "+%Y/%m/%d %H:%M:%S")] [Check]: 检查Python版本\033[0m" + +## pypi下载源配置 +mkdir ~/.pip/ +echo "extra-index-url = https://mirrors.cloud.tencent.com/pypi/simple" >> ~/.pip/pip.conf +``` diff --git a/web/packages/tca-document/en/quickStarted/references/install_python37_on_ubuntu.md b/web/packages/tca-document/en/quickStarted/references/install_python37_on_ubuntu.md new file mode 100644 index 000000000..14459aaf1 --- /dev/null +++ b/web/packages/tca-document/en/quickStarted/references/install_python37_on_ubuntu.md @@ -0,0 +1,66 @@ +# 在 Ubuntu 安装 Python3.7 + +> 注:当前Ubuntu版本为18.04 + +## 下载Python源码包 + +```bash +wget https://www.python.org/ftp/python/3.7.12/Python-3.7.12.tgz +``` + +## 安装前准备 + +安装依赖组件 + +```bash +apt-get update +apt-get install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libsqlite3-dev libreadline-dev libffi-dev wget libbz2-dev tk-dev gcc make +``` + +## 解压安装 + +```bash +# 解压到/usr/local/src目录 +$ tar zvxf Python-3.7.12.tgz -C /usr/local/src +$ cd /usr/local/src/Python-3.7.12 +# 编译前配置 +$ ./configure prefix=/usr/local/python3 --enable-shared +# 编译构建 +$ make -j8 +# 安装Python +$ make install +# 清理编译产出的中间文件 +$ make clean +# 链接构建产出的Python可执行文件到/usr/local/bin目录 +$ ln -s /usr/local/python3/bin/python3 /usr/local/bin/python +# 链接构建产出的pip3可执行文件到/usr/local/bin目录 +$ ln -s /usr/local/python3/bin/pip3 /usr/local/bin/pip +# 链接构建产出的Python动态库 +$ ln -s /usr/local/python3/lib/libpython3.7m.so.1.0 /usr/lib/libpython3.7m.so.1.0 +# 配置动态库 +$ ldconfig +``` + +## 检查 + +检查Python版本是否安装成功 + +```bash +$ python --version +Python 3.7.12 # 正常输出,表示安装成功 +``` + +注: + +1. 链接到/usr/local/bin/目录不会影响系统软件 +2. 一般情况下,PATH配置是先``/usr/local/bin``再``/usr/bin`` +3. 检查``python -v``输出结果是否为``Python 3.7.12``版本,如果不是该版本,可能影响后续依赖安装和服务运行 + +## pypi下载源配置 + +pip默认是到``pypi``官方源下载第三方依赖包,下载速度可能会比较慢,可以考虑调整为腾讯云的``pypi``下载源,调整方式: + +```bash +mkdir ~/.pip/ +echo "[global]\nindex-url = https://mirrors.cloud.tencent.com/pypi/simple" >> ~/.pip/pip.conf +``` diff --git a/web/packages/tca-document/en/quickStarted/references/install_redis_from_source.md b/web/packages/tca-document/en/quickStarted/references/install_redis_from_source.md new file mode 100644 index 000000000..3ad90db98 --- /dev/null +++ b/web/packages/tca-document/en/quickStarted/references/install_redis_from_source.md @@ -0,0 +1,107 @@ +# 源码安装 Redis + +## 运行环境 + +1. 基于x86_64的CentOS +2. 基于鲲鹏920(aarch64)的UOS V20 +3. 基于飞腾2000(aarch64)的TencentOS Server + +## 环境准备 + +安装编译打包需要的工具 + +```bash +yum install -y gcc make tcl wget +``` + +## 下载源码 + +```bash +wget http://download.redis.io/releases/redis-5.0.4.tar.gz +``` + +## 编译安装 + +```bash +# 解压 +$ tar zvxf redis-5.0.4.tar.gz -C /usr/local/src + +# 进入源码目录 +$ cd /usr/local/src/redis-5.0.4 + +# 构建redis依赖库 +$ cd deps; make -j4 hiredis jemalloc linenoise lua +$ cd .. + +# 构建redis +$ make -j4 +$ make install +$ make clean +``` + +安装后,可以在``/usr/local/src/redis-5.0.4/src``目录和``/usr/local/bin/``目录下找到``redis-server``与``redis-cli``两个文件 + +## 调整配置 + +```bash +cp /usr/local/src/redis/redis.conf /etc/redis.conf +vim /usr/local/src/redis/redis.conf +``` + +```bash +# 设置Redis密码 +requirepass 123456 + +# 将 daemonize no 调整为 daemonize yes,将redis-server调整为默认后台启动 +daemonize yes + +# 配置日志 +logfile '/var/log/redis/redis-server.log' +``` + +## 启动服务 + +```bash +redis-server /etc/redis.conf +``` + +## 配置开机自动启动 + +```bash +vim /etc/systemd/system/redis.service +``` + +输入以下内容: + +```service +[Unit] +Description=redis-server +After=network.target + +[Service] +Type=forking +ExecStart=/usr/local/bin/redis-server /etc/redis.conf +ExecStop=/usr/local/bin/redis-cli shutdown +Restart=always + +PrivateTmp=true + +[Install] +WantedBy=multi-user.target +``` + +启动redis-server: + +```bash +systemctl start redis +``` + +开机自动启动redis: + +```bash +systemctl enable redis +``` + +## 参考文档 + +- [Redis官方文档](https://redis.io/topics/quickstart) diff --git a/web/packages/tca-document/en/quickStarted/references/install_redis_on_centos.md b/web/packages/tca-document/en/quickStarted/references/install_redis_on_centos.md new file mode 100644 index 000000000..1a10cf9be --- /dev/null +++ b/web/packages/tca-document/en/quickStarted/references/install_redis_on_centos.md @@ -0,0 +1,48 @@ +# 在 CentOS 安装 Redis + +## 注意 + +本文档仅供参考,不适用于正式环境部署,正式环境建议使用专业的Redis服务(比如[腾讯云的Redis产品](https://cloud.tencent.com/product/crs)) + +## 环境 + +CentOS 7.3 版本 + +## yum 安装 redis + +```bash +yum install redis +``` + +注:安装redis可能会出现"no package redis available"的错误提示,请执行``yum install epel-release``后重试redis安装命令。 + +## 修改redis密码 + +```bash +$ vi /etc/redis.conf + +# 找到 requirepass foobared +# 复制一行并根据自己需要调整密码,比如 +requirepass tca123 +``` + +## 启动redis + +```bash +systemctl start redis +``` + +查看redis运行状态 + +```bash +systemctl status redis +``` + +## 访问redis + +```bash +$ redis-cli + +127.0.0.1:6379> auth tca123 +OK # 鉴权通过 +``` diff --git a/web/packages/tca-document/en/quickStarted/runProject.md b/web/packages/tca-document/en/quickStarted/runProject.md new file mode 100644 index 000000000..3fa362da5 --- /dev/null +++ b/web/packages/tca-document/en/quickStarted/runProject.md @@ -0,0 +1,26 @@ +# 启动代码分析 + +在完成 Server、Web 和 Client 相关部署和配置后,可通过平台执行代码分析。 + +## 执行代码分析 + +初始化创建项目后,可通过 `在线分析` 或 `客户端分析` 来启动代码分析。 + +![代码分析](../../images/start_scan_06.png) + +注: +- TCA推荐使用`在线分析`,您可根据具体使用场景选择其一。 +- `在线分析`表示配置代码库链接后,TCA客户端拉取代码后进行分析;`客户端分析`在配置本地待扫描代码路径后,无需代码拉取直接分析本地代码。 +- `在线分析`与`客户端分析`具体详情及配置参考[TCA客户端配置详情](../guide/客户端/客户端配置详情.md) + +## 查看分析历史 + +分析结束后,数据会上报到服务端。可进入分析历史页面查看分析记录以及分析结果。 + +![分析历史](../../images/start_scan_05.png) + +## 查看分析概览 + +分析结束后,进入分支概览可以查看该分支指定分析方案的概览数据以及 [问题列表](../guide/代码检查/分析结果查看.md) 等。 + +![分支概览](../../images/start_scan_04.png) diff --git a/web/packages/tca-document/en/quickStarted/server.md b/web/packages/tca-document/en/quickStarted/server.md deleted file mode 100644 index 867f39148..000000000 --- a/web/packages/tca-document/en/quickStarted/server.md +++ /dev/null @@ -1,76 +0,0 @@ -# 源代码安装服务端 - -## 依赖环境 - -- 系统要求 - - - Linux - - [Pythn 3.7](https://docs.python.org/zh-cn/3.7/using/unix.html) - - [MySQL 5.7.8](https://dev.mysql.com/doc/mysql-installation-excerpt/5.7/en/) 或更高版本 - - [Redis 4.0](https://redis.io/docs/getting-started/installation/install-redis-on-linux/) 或更高版本 - - [Nginx 1.20.2](https://nginx.org/en/docs/install.html) 或更高版本 - -- 硬件要求 - - - 2核4G内存 - - 100G可用硬盘存储空间 - -- 权限要求 - - - 安装Server依赖软件(python、nginx、yum软件包)需要使用ROOT权限(启动Server服务时可以使用非ROOT用户运行) - - 需要开放80端口的访问权限(80为TCA平台访问端口) - - Server服务执行数据库初始化需要依赖``CREATE、ALTER、INDEX、DELETE、LOCK TABLES、SELECT、INSERT、REFERENCES、UPDATE``权限 - - -## 安装步骤 - -### 部署Server - -1. 进入Server服务工作目录(例如 ``~/CodeAnalysis/server/``),以下路径均为目录内的相对路径 -2. 配置MySQL和Redis服务,初始化数据(MySQL版本运行版本:5.7) - - 填写数据库和Redis信息以及根据需要调整配置信息,主要的工程配置已提供默认值,字段说明详见[TCA Server](../references/parameters/server.md)。 - ```bash - vi ./scripts/config.sh - ``` - - 初始化DB、安装依赖和运行初始化脚本 - ```bash - bash ./scripts/deploy.sh init - ``` - - 将安装好的``celery``与``gunicorn``可执行文件建立软链接到``/usr/local/bin``路径下 - ```bash - # /path/to/需要替换为celery可执行命令实际的路径,一般在python安装路径的bin目录下,如/usr/local/python3/bin/ - ln -s /path/to/celery /usr/local/bin/celery - # /path/to/需要替换为gunicorn可执行命令实际的路径,一般在python安装路径的bin目录下,如/usr/local/python3/bin/ - ln -s /path/to/gunicorn /usr/local/bin/gunicorn - ``` - - 使环境变量生效,避免出现unknown command错误 - ```bash - export PATH=/usr/local/bin:$PATH - ``` -3. 启动/停止服务 - ```bash - # 启动服务 - bash ./scripts/deploy.sh start - # 停止服务 - bash ./scripts/deploy.sh stop - ``` - -### 部署Web - - -1. 完成部署Server并启动服务,后端服务默认登陆账号/密码为:`CodeDog/admin`。 - -2. 进入web服务部署目录(例如 ``~/CodeAnalysis/web/tca-deploy-source``),以下路径均为目录内的相对路径 - -3. 部署/更新前端服务 - - ```bash - # 部署、更新都使用此命令 - bash ./scripts/deploy.sh init -d - ``` - - 具体请查阅部署脚本内容,可根据业务调整配置。 - -3. **额外说明** - - `tca-deploy-source/scripts/config.sh` 已配置默认环境变量,用户可根据需要调整环境变量再部署前端服务,具体可查阅脚本内容。 diff --git a/web/packages/tca-document/en/quickStarted/setup.md b/web/packages/tca-document/en/quickStarted/setup.md deleted file mode 100644 index 0f5b90db9..000000000 --- a/web/packages/tca-document/en/quickStarted/setup.md +++ /dev/null @@ -1,24 +0,0 @@ -# 创建代码分析项目 - -部署并启动服务端后,通过以下步骤创建您的第一个代码分析项目。 - -**1. 在浏览器输入http://<部署机IP>/,进入TCA主页,开始项目配置。** -![homepage](../../images/homepage.png) - - -**2. 创建团队及项目。** - - - -**3. 登记代码库。输入代码库地址及配置凭证信息。** - - - -> 注:在本地扫描模式下,待扫描的代码库需在客户端配置文件中配置本地路径,步骤3中登记的代码库并非最终实际扫描的代码库,详见客户端使用文档。 - -**4. 创建分析方案,配置代码检查规则包。首次扫描可勾选「普通创建」,根据待分析的语言及问题类型勾选「分析语言」及「功能」。** - - - - -更多配置及使用说明详见[帮助文档](/zh/guide/README.md)。 \ No newline at end of file diff --git "a/web/packages/tca-document/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\345\210\227\350\241\250.md" b/web/packages/tca-document/en/quickStarted/tools.md similarity index 74% rename from "web/packages/tca-document/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\345\210\227\350\241\250.md" rename to web/packages/tca-document/en/quickStarted/tools.md index 4b3f042d7..a4c47ba0f 100644 --- "a/web/packages/tca-document/en/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\345\210\227\350\241\250.md" +++ b/web/packages/tca-document/en/quickStarted/tools.md @@ -1,9 +1,10 @@ -# TCA 工具列表 -目前TCA支持以下工具: +# 工具列表 + +目前 TCA 支持以下工具: | 官方工具 | 第三方工具 | | :--------: | :-------: | -|[0daychecker](../tools/codedog_0Day_checker)| androidlint | +|[0daychecker](https://github.com/Tencent/CodeAnalysis/tree/main/tools/codedog_0Day_checker)| androidlint | |clangwarning| checkstyle | |codecount| clang | |customfilescan| cobra | @@ -12,10 +13,10 @@ |javawarning| cpplint | |regexfilescan| dart_code_metrics | |regexscan| dartanalyzer | -|[tca_ql_php_beta](../tools/Hades_Beta/README.md)| detekt | +|[tca_ql_php_beta](https://github.com/Tencent/CodeAnalysis/tree/main/tools/Hades_Beta)| detekt | |unusedresource| eslint | -|[collie](../tools/collie/README.md)| eslint_typescript | -|[compass](../tools/compass/README.md)| eslint_vue | +|[collie](https://github.com/Tencent/CodeAnalysis/tree/main/tools/collie/)| eslint_typescript | +|[compass](https://github.com/Tencent/CodeAnalysis/tree/main/tools/compass)| eslint_vue | || findbugs | || flake8 | || [flawfinder](https://github.com/TCATools/flawfinder) | diff --git a/web/packages/tca-document/images/creataAnalysePlan.png b/web/packages/tca-document/images/creataAnalysePlan.png index cc548ea39..82cdbeeb9 100644 Binary files a/web/packages/tca-document/images/creataAnalysePlan.png and b/web/packages/tca-document/images/creataAnalysePlan.png differ diff --git a/web/packages/tca-document/images/customtool_01.png b/web/packages/tca-document/images/customtool_01.png new file mode 100644 index 000000000..80d9364f2 Binary files /dev/null and b/web/packages/tca-document/images/customtool_01.png differ diff --git a/web/packages/tca-document/images/customtool_02.png b/web/packages/tca-document/images/customtool_02.png new file mode 100644 index 000000000..6e5d788ac Binary files /dev/null and b/web/packages/tca-document/images/customtool_02.png differ diff --git a/web/packages/tca-document/images/customtool_03.png b/web/packages/tca-document/images/customtool_03.png new file mode 100644 index 000000000..eca12883a Binary files /dev/null and b/web/packages/tca-document/images/customtool_03.png differ diff --git a/web/packages/tca-document/images/customtool_04.png b/web/packages/tca-document/images/customtool_04.png new file mode 100644 index 000000000..47f34630e Binary files /dev/null and b/web/packages/tca-document/images/customtool_04.png differ diff --git a/web/packages/tca-document/images/customtool_05.png b/web/packages/tca-document/images/customtool_05.png new file mode 100644 index 000000000..25bd2c203 Binary files /dev/null and b/web/packages/tca-document/images/customtool_05.png differ diff --git a/web/packages/tca-document/images/customtool_06.png b/web/packages/tca-document/images/customtool_06.png new file mode 100644 index 000000000..af530ad1b Binary files /dev/null and b/web/packages/tca-document/images/customtool_06.png differ diff --git a/web/packages/tca-document/images/logo.png b/web/packages/tca-document/images/logo.png new file mode 100644 index 000000000..aab4be80c Binary files /dev/null and b/web/packages/tca-document/images/logo.png differ diff --git a/web/packages/tca-document/images/manage_01.png b/web/packages/tca-document/images/manage_01.png deleted file mode 100644 index 43e880497..000000000 Binary files a/web/packages/tca-document/images/manage_01.png and /dev/null differ diff --git a/web/packages/tca-document/images/manage_02.png b/web/packages/tca-document/images/manage_02.png deleted file mode 100644 index 7827cc622..000000000 Binary files a/web/packages/tca-document/images/manage_02.png and /dev/null differ diff --git a/web/packages/tca-document/images/manage_03.png b/web/packages/tca-document/images/manage_03.png deleted file mode 100644 index e3f0eb25e..000000000 Binary files a/web/packages/tca-document/images/manage_03.png and /dev/null differ diff --git a/web/packages/tca-document/images/manage_04.png b/web/packages/tca-document/images/manage_04.png deleted file mode 100644 index 31a394651..000000000 Binary files a/web/packages/tca-document/images/manage_04.png and /dev/null differ diff --git a/web/packages/tca-document/images/manage_05.png b/web/packages/tca-document/images/manage_05.png deleted file mode 100644 index ddce7d441..000000000 Binary files a/web/packages/tca-document/images/manage_05.png and /dev/null differ diff --git a/web/packages/tca-document/images/manage_06.png b/web/packages/tca-document/images/manage_06.png deleted file mode 100644 index d04552d8c..000000000 Binary files a/web/packages/tca-document/images/manage_06.png and /dev/null differ diff --git a/web/packages/tca-document/images/manage_job_01.png b/web/packages/tca-document/images/manage_job_01.png new file mode 100644 index 000000000..973333db7 Binary files /dev/null and b/web/packages/tca-document/images/manage_job_01.png differ diff --git a/web/packages/tca-document/images/manage_node_01.png b/web/packages/tca-document/images/manage_node_01.png new file mode 100644 index 000000000..5012308be Binary files /dev/null and b/web/packages/tca-document/images/manage_node_01.png differ diff --git a/web/packages/tca-document/images/manage_node_02.png b/web/packages/tca-document/images/manage_node_02.png new file mode 100644 index 000000000..217aa7d0c Binary files /dev/null and b/web/packages/tca-document/images/manage_node_02.png differ diff --git a/web/packages/tca-document/images/manage_node_03.png b/web/packages/tca-document/images/manage_node_03.png new file mode 100644 index 000000000..1b6067e8d Binary files /dev/null and b/web/packages/tca-document/images/manage_node_03.png differ diff --git a/web/packages/tca-document/images/manage_node_04.png b/web/packages/tca-document/images/manage_node_04.png new file mode 100644 index 000000000..d6d2c3a6d Binary files /dev/null and b/web/packages/tca-document/images/manage_node_04.png differ diff --git a/web/packages/tca-document/images/manage_oauth_01.png b/web/packages/tca-document/images/manage_oauth_01.png new file mode 100644 index 000000000..cbc3b1e7b Binary files /dev/null and b/web/packages/tca-document/images/manage_oauth_01.png differ diff --git a/web/packages/tca-document/images/manage_oauth_02.png b/web/packages/tca-document/images/manage_oauth_02.png new file mode 100644 index 000000000..0092c52a1 Binary files /dev/null and b/web/packages/tca-document/images/manage_oauth_02.png differ diff --git a/web/packages/tca-document/images/manage_org_01.png b/web/packages/tca-document/images/manage_org_01.png new file mode 100644 index 000000000..37e62f2a0 Binary files /dev/null and b/web/packages/tca-document/images/manage_org_01.png differ diff --git a/web/packages/tca-document/images/manage_org_02.png b/web/packages/tca-document/images/manage_org_02.png new file mode 100644 index 000000000..52161dea1 Binary files /dev/null and b/web/packages/tca-document/images/manage_org_02.png differ diff --git a/web/packages/tca-document/images/manage_team_01.png b/web/packages/tca-document/images/manage_team_01.png new file mode 100644 index 000000000..ff5908bbb Binary files /dev/null and b/web/packages/tca-document/images/manage_team_01.png differ diff --git a/web/packages/tca-document/images/manage_tool_01.png b/web/packages/tca-document/images/manage_tool_01.png new file mode 100644 index 000000000..2cf005f66 Binary files /dev/null and b/web/packages/tca-document/images/manage_tool_01.png differ diff --git a/web/packages/tca-document/images/manage_user_01.png b/web/packages/tca-document/images/manage_user_01.png new file mode 100644 index 000000000..8596ee282 Binary files /dev/null and b/web/packages/tca-document/images/manage_user_01.png differ diff --git a/web/packages/tca-document/images/manage_user_02.png b/web/packages/tca-document/images/manage_user_02.png new file mode 100644 index 000000000..9a955cc49 Binary files /dev/null and b/web/packages/tca-document/images/manage_user_02.png differ diff --git a/web/packages/tca-document/images/node_mange.png b/web/packages/tca-document/images/node_mange.png new file mode 100644 index 000000000..f9fcd4760 Binary files /dev/null and b/web/packages/tca-document/images/node_mange.png differ diff --git a/web/packages/tca-document/images/org_node_manager_1.png b/web/packages/tca-document/images/org_node_manager_1.png new file mode 100644 index 000000000..375447db2 Binary files /dev/null and b/web/packages/tca-document/images/org_node_manager_1.png differ diff --git a/web/packages/tca-document/images/org_node_manager_2.png b/web/packages/tca-document/images/org_node_manager_2.png new file mode 100644 index 000000000..db937ee4e Binary files /dev/null and b/web/packages/tca-document/images/org_node_manager_2.png differ diff --git a/web/packages/tca-document/images/org_node_process.png b/web/packages/tca-document/images/org_node_process.png new file mode 100644 index 000000000..758092622 Binary files /dev/null and b/web/packages/tca-document/images/org_node_process.png differ diff --git a/web/packages/tca-document/images/org_tag_manager.png b/web/packages/tca-document/images/org_tag_manager.png new file mode 100644 index 000000000..f3cfdaa27 Binary files /dev/null and b/web/packages/tca-document/images/org_tag_manager.png differ diff --git a/web/packages/tca-document/images/org_tag_node.png b/web/packages/tca-document/images/org_tag_node.png new file mode 100644 index 000000000..890af1469 Binary files /dev/null and b/web/packages/tca-document/images/org_tag_node.png differ diff --git a/web/packages/tca-document/images/org_tag_scheme.png b/web/packages/tca-document/images/org_tag_scheme.png new file mode 100644 index 000000000..b8c352587 Binary files /dev/null and b/web/packages/tca-document/images/org_tag_scheme.png differ diff --git a/web/packages/tca-document/package.json b/web/packages/tca-document/package.json index 19b045a37..624cf0989 100644 --- a/web/packages/tca-document/package.json +++ b/web/packages/tca-document/package.json @@ -10,10 +10,10 @@ "scripts": { "dev": "vuepress dev", "build:comment": "echo '构建帮助文档,默认base前缀为document,可根据部署需要进行相应调整'", - "build": "BASE=CodeAnalysis vuepress build -d ./dist" + "build": "BASE=document vuepress build -d ./dist" }, "devDependencies": { "@vuepress/plugin-search": "^2.0.0-beta.43", "vuepress": "^2.0.0-beta.42" } -} +} \ No newline at end of file diff --git a/web/packages/tca-document/zh/README.md b/web/packages/tca-document/zh/README.md index 5e31beae1..794436693 100644 --- a/web/packages/tca-document/zh/README.md +++ b/web/packages/tca-document/zh/README.md @@ -1,25 +1 @@ ---- -home: true -title: 腾讯云代码分析帮助文档 -actions: - - text: 快速入门 - link: /zh/quickStarted/intro.html - type: primary - - text: 帮助文档 - link: /zh/guide/ - type: secondary -features: - - title: 稳定可靠的架构 - details: 采用分布式云原生架构,支持灵活扩缩容,执行更快更稳定。 - - title: 多工具支持 - details: 已集成众多自研、知名开源工具等,采用分层分离架构,满足快速自助管理工具。 - - title: 多语言覆盖 - details: 支持29种编程语言,覆盖常见常用编程语言。 - - title: 自定义质量指标 - details: 自定义代码质量检测标准,逐步优化代码质量。 - - title: 增量全量分析 - details: 增量分析快速发现问题,全量分析保证问题全覆盖。 - - title: 全方位质量报告 - details: 图形化可视报告,轻松监管代码综合质量趋势。 -footer: MIT Licensed | Copyright © 1998-present Tencent. All Rights Reserved. ---- + \ No newline at end of file diff --git "a/web/packages/tca-document/zh/advanced/\344\273\273\345\212\241\345\210\206\345\270\203\345\274\217\346\211\247\350\241\214.md" "b/web/packages/tca-document/zh/advanced/\344\273\273\345\212\241\345\210\206\345\270\203\345\274\217\346\211\247\350\241\214.md" new file mode 100644 index 000000000..5af9d5ac8 --- /dev/null +++ "b/web/packages/tca-document/zh/advanced/\344\273\273\345\212\241\345\210\206\345\270\203\345\274\217\346\211\247\350\241\214.md" @@ -0,0 +1,71 @@ +# 任务分布式执行 + +## 适用场景 + +- 以往的单机器单进程,性能比较低,工具排队等待时间过长。希望通过并行执行分析来提高分析效率。 + +- 希望尽量使用公共资源或使用专机资源。 + +**为了满足以上需求,TCA已经进行如下支持:** + +- 支持工具在多台机器上并行执行。 + +- 支持指定工具在指定的机器上运行。 + +- 支持与本地启动的任务衔接,加速本地任务扫描。 + +- 配套任务状态监控能力,及时重置初始化超时或机器掉线的任务。 + +:::tip +TCA客户端除了通过localscan命令启动单次的代码分析,也可以作为一个分布式分析节点启动,作为常驻进程,多个节点可以分布式并行执行服务端下发的任务,提高扫描效率。和本地分析一样,需要先安装环境和必要的工具,并配置好服务端地址。 +::: + +## 常驻节点配置 + +**前置步骤**:公共/专有机器上已具备客户端。 + +开源版客户端,需要配置相关环境和依赖,可查阅帮助文档中的开源版客户端使用说明(如下图) + + ![helpopensource](https://tencent.github.io/CodeAnalysis/media/helpopensource.png) + (界面右上角图标点击-帮助文档-Client 客户端) + +**1.配置 config.ini 文件** + +将``替换成实际的serve ip(可包含端口号)。 + +**2.启动代码分析常驻节点** + +1. 从TCA前端页面中获取 `token`,前往 个人中心-个人令牌-复制`Token` 。 + - **作为公共节点**:`token`需要具有超级管理员权限(界面右上角图标点-管理入口-用户管理),如使用CodeDog账户的`token`。 + + - **作为专机节点**:该节点仅能分析该token具有权限的项目。 + +2. 进入到client目录下,执行命令: + `python3 codepuppy.py -l codepuppy.log start -t ` + + ![order](https://tencent.github.io/CodeAnalysis/media/order.png) + +3. 启动后,可以在命令行输出或codepuppy.log中查看运行日志,如果未报异常,且输出`task loop is started.`,表示节点已经正常启动。 + +**3.配置节点** + +:::tip +常驻节点首次启动后,需要到节点管理页面设置节点状态(默认为不可用),将其设置为活跃,用于接收和执行任务。 +::: + +1. 进入TCA节点管理页面。可以看到当前在线的节点,可以修改节点名称、标签、负责人等信息。 + + ![NodeManagement](https://tencent.github.io/CodeAnalysis/media/Nodemanagement.png) + (界面右上角图标点击-管理入口-节点管理) + +- 常驻节点首次启动后,需将节点状态从不可用(失效)状态切换到活跃(在线)状态。 + + ![StateSwitch](https://tencent.github.io/CodeAnalysis/media/StateSwitch.png) + +- 可以进入工具进程配置页面,对节点支持的工具进程进行管理(默认会全部勾选),未勾选的工具进程,将不会在该节点上执行。 + + ![ProcessConfiguration](https://tencent.github.io/CodeAnalysis/media/ProcessConfiguration.png) + +- 节点所属标签会与分析方案中的运行环境标签进行匹配,只有相同标签的任务才会下发到该机器节点上。 + +本功能代码已提交开源版,欢迎使用! :+1: diff --git "a/web/packages/tca-document/zh/advanced/\351\233\206\346\210\220\344\273\243\347\240\201\345\210\206\346\236\220\345\267\245\345\205\267.md" "b/web/packages/tca-document/zh/advanced/\351\233\206\346\210\220\344\273\243\347\240\201\345\210\206\346\236\220\345\267\245\345\205\267.md" new file mode 100644 index 000000000..ca4463fe8 --- /dev/null +++ "b/web/packages/tca-document/zh/advanced/\351\233\206\346\210\220\344\273\243\347\240\201\345\210\206\346\236\220\345\267\245\345\205\267.md" @@ -0,0 +1,215 @@ +# 集成代码分析工具 + +## 初识TCA任务执行机制 + +1. TCA server在接收到开启分析的请求后根据所选规则生成对应的task_request,每个task_request对应一个工具的任务 +2. TCA server将`task_request`分发到能够执行该工具的机器 +3. TCA client在收到task_request后提取出本次任务的工具名也就是其中的`task_name`字段,字段对应于工具的`name`字段 +4. TCA client按照`task_name`在client中的tool目录查找对应python启动脚本 +5. 执行python启动脚本中的内容 + +## 添加分析工具(以tca_ql_php_beta为例) + +根据上述的任务机制添加工具需要做到以下几点 + +1. 让server知道存在`tca_ql_php_beta`工具及其所含的规则 +2. 让server知道哪些客户端可以执行`tca_ql_php_beta`工具 +3. client下载/找到工具所在目录及需要的环境 +4. 让client知道`tca_ql_php_beta`对应的启动脚本是什么 + +### 如何让Server知道存在相应工具 + +1. 找到`server/projects/main/apps/scan_conf/management/commands/open_source`目录 + +2. 创建工具json文件,json文件名尽量对应工具名称方便查看 + +3. json文件内容为(以tca_ql_php_beta为例) + + ```python + [ + { + "name": "tca_ql_php_beta", + "display_name": "Hades_Beta_PHP(展示名称用于前端展示使用)", + "description": "工具描述", + "license": "工具license", + "libscheme_set": [], # 暂时不需要 + "task_processes": [ + "analyze", + "datahandle", + "compile" + ], # 工具进程,包含compile编译, analyze分析, datahandle数据处理 + "scan_app": "codelint", # 代码分析统一为codelint + "scm_url": "", # 暂时为空 + "run_cmd": "", + "envs": null, # 是否需要特殊环境,这里无需填写 + "build_flag": false, # 是否需要编译命令才能运行 + "checkrule_set": [ # 工具包含的规则 + { + "real_name": "deser", # 规则名 + "display_name": "反序列化漏洞", # 规则前端展示,考虑各工具规则名可能晦涩难懂,设置展示名称方便查找 + "severity": "error", # 规则等级 从上到下分为 fatal, error, warning, info 四个等级 + "category": "security", # 规则类别。correctness 功能 security安全 performance性能 usability可用性 accessibility无障碍化 i18n国际化 convention代码风格 other其他 + "rule_title": "反序列化漏洞", # 一句话概括规则简介 + "rule_params": null, # 规则参数 + "languages": [ # 支持语言 + "php" + ], + "solution": "", # 建议的解决方法 + "owner": "", + "labels": [], + "description": "", # 规则详细介绍 + } + ] + } + ] + ``` + +4. 在`server/projects/main/`目录执行`python manage.py loadcheckers --dir open_source tca_ql_php_beta` 加载工具进入数据库 + +## 让server知道哪些客户端可以执行`tca_ql_php_beta`工具 + +1. 进入节点管理页面 + +![节点管理](https://tencent.github.io/CodeAnalysis/media/node_mange.png) + +2. 选择其中一台机器 工具进程配置,勾选其工具进程 + +![工具进程](https://tencent.github.io/CodeAnalysis/media/tool.png) + +## client下载/找到工具所在目录及需要的环境 + +1. 找到puppy-tool-config若没有额外配置则为默认代码库 +2. 修改其中的 ini 配置文件,每个操作系统对应一个ini +3. 以tca_ql_php_beta为例需要做以下修改 + +``` +; env_path 主要填写存放工具文件所在的相对目录,一般都存放/拉取在tools下,会在工具执行前加载到环境变量中提供使用 +[env_path] +ZEUS_BETA_HOME : Zeus_Beta +HADES_BETA_HOME : Hades_Beta + +; toolz_url +[tool_url] 主要填写工具的git仓库,这里因为tca_ql_php_beta直接使用tools下的目录所以不用再进行额外拉取也无需再写 +CPPCHECK : ${base_value:git_url}/linux-cppcheck-1.78 + +; 各工具配置 以tca_ql_php_beta为例 +; env_path 填写上面需要加载的环境变量 +; env_value 通用环境变量,一般无需填写如果有需求需要现在 [env_value] 中定义好再填写 +; path 工具所在目录填写上面的定义 +; tool_url 工具git仓库,使用本地相对目录故为空 +[tca_ql_php_beta] +env_path : ZEUS_BETA_HOME;HADES_BETA_HOME +env_value : +path : ${env_path:ZEUS_BETA_HOME};${env_path:HADES_BETA_HOME} +tool_url : + +``` + +## 让client知道`tca_ql_php_beta`对应的启动脚本是什么 + +1. 以上述步骤在`client/tool`目录添加脚本`tca_ql_php_beta.py`作为启动脚本 注:启动脚本必须与工具名称相同 + +2. 编写脚本 + +### 脚本编写规范 + +以`tca_ql_php_beta`为例 + +``` + +from task.codelintmodel import CodeLintModel +from util.logutil import LogPrinter +from util.subprocc import SubProcController + +logger = LogPrinter() + + +class TcaQlPHPBeta(CodeLintModel): + # 代码分析工具集成基类CodeLintModel + def __init__(self, params): + logger.info("找到工具了Q_Q") + super().__init__(params) + + def compile(self, params): + logger.info("开始编译了Q_Q") + build_cmd = params.get('build_cmd', None) # 从params中获取编译命令, params内容可以在最后附录查看 + lang = "php" + do_some_things() + + def analyze(self, params): + logger.info("开始分析了Q_Q") + lang = "php" + HADES_HOME = envs.get("HADES_BETA_HOME", None) + output_json = "result.json" + sp = SubProcController( + command=["Hades", "analyze", "test.php", "-o", output_json], + cwd=HADES_HOME, + stdout_line_callback=subprocc_log, + stderr_line_callback=subprocc_log, + ) + sp.wait() # 执行工具分析命令 + issues = [] + # 工具结果输出到output_json,具体工具可能有所不同 + if os.path.exists(output_json): + with open(output_json, "r") as result_reader: + result = json.load(result_reader) + issues.extend(result) + return issues + +tool = TcaQlPHPBeta # 必须,必须包含tool变量并且为该工具的类 +``` + +1. 脚本必须包含analyze方法,如果有配置编译进程也需要相应的compile方法来做编译相关工作,datahandle函数不用自定义基类方法已经够用了。方法执行顺序为 compile -> analyze -> datahandle +2. params参数为`task_request`中的`task_params`字段,具体字段将在最后附录进行说明 +3. anlyze方法必须有返回值,返回值为issue列表,issue格式为 + +``` +{ + "path": "文件相对路径", + "line": "行号,int类型", + "column": "列号, int类型,如果工具没有输出列号信息,可以用0代替", + "msg": "提示信息", + "rule": "规则名称,可以根据需要输出不同的规则名", + "refs": [ + { + "line": "回溯行号", + "msg": "提示信息", + "tag": "用一个词简要标记该行信息,比如uninit_member,member_decl等,如果没有也可以都写成一样的", + "path": "回溯行所在文件绝对路径" + }, + ... + ] +} +说明: + refs:可选,记录问题回溯路径信息。比如当前文件的回溯路径其他的3行代码,可以将这三行的路径及提示信息,按顺序添加到refs数组中。 +``` + +# PR + +如果有意公开您添加的工具欢迎发起PR + +注:别忘了puppy-tool-config 也需要PR + +# 附录 + +## params 表格 + +| 字段 | 说明 | 类型 | +| --- | --- | --- | +| scan_languages | 语言 | 字符串列表如 ["python", "php"] | +| pre_cmd | 编译前置命令 | 字符串 | +| build_cmd | 编译命令 | 字符串 | +| envs | 额外环境变量 | 字符串 | +| scm_last_revision | 上次成功分析的代码版本,增量使用 | 字符串 | +| incr_scan | 是否为增量分析 | bool | +| rules | 规则名称列表,只有规则名 | 字符串列表 | +| rule_list | 详细的规则列表包含规则名和规则参数等 | 字典列表 | +| checktool | 工具详细信息,执行一般用不到 | 字典 | +| path_filters | 过滤路径 | 字典 | +| scm_url | 代码库url | 字符串 | +| source_dir | 代码库本地目录 | 字符串 | +| work_dir | 本次任务的work_dir目录 | 字符串 | +| project_id | 分析项目id | int | +| repo_id | 仓库id | int | +| task_id | 任务id | int | +| job_id | 本次分析的id | int | diff --git a/web/packages/tca-document/zh/community/changelog.md b/web/packages/tca-document/zh/community/changelog.md index 6029a2219..e3fd99512 100644 --- a/web/packages/tca-document/zh/community/changelog.md +++ b/web/packages/tca-document/zh/community/changelog.md @@ -1,47 +1,61 @@ # 更新日志 ## V1.2.0 (2022-4-27) + ### Features + - 【Web端】增加工具管理 - 【工具】增加logback检查的安全规则 - 【服务端】增加TCA server&web 一键部署脚本 - 【服务端】删除main部分异步任务;调整server nginx启动位置 - 【服务端】增加server健康监测 + ### Docs + - 完善部署和Q&A文档 - 上传工具列表 - ## V1.1.3 (2022-4-18) + ### Features + - 【工具】上传开源合规检查规则 - 【工具】新增PHP安全相关规则 - 【服务端】上线license鉴权 - 【客户端】支持对工具license校验 + ### Docs + - 更新文档内的工具默认路径 - 增加任务分布式执行能力操作文档 - 增加PR操作流程 - ## V1.1.2 (2022-4-2) + ### Features -- 【服务端】优化部署构建脚本 + +- 【服务端】优化部署构建脚本 + ### Docs -- 简化前端部署脚本&文档 -- 优化指引文档 +- 简化前端部署脚本&文档 +- 优化指引文档 ## V1.1.1 (2022-3-31) + ### Features + - 【工具】增加0daychecker工具 - 【工具】增加Log4j、LogBack漏洞检查规则包 ### Docs + - 完善部署文档说明,推荐使用Docker-Compose 2.3.3版本 ## V1.1.0 (2022-3-29) + ### Features + - 【客户端】client支持arm64架构执行环境 - 【客户端】client新增分布式节点模式 - 【客户端】修改参数isTotal(是否开启全量扫描)判断方式及参数startCommand(启动客户端命令)拼接方式 @@ -51,17 +65,21 @@ - 【Web端】web模块部署脚本问题修复及优化 - 【Web端】增加管理后台、增加在线分析 - 【Web端】调整前端部署脚本,支持传递nginx配置地址、前端资源部署地址 + ### Bugfixes + - Jenkins插件命令拼装逻辑修正 + ### Docs + - 调整pypi下载失败提示 - 调整前端部署文档及脚本 - 更新License - - ## V1.0.1 (2022-03-01) + ### Features + - feat: 【服务端】调整代码库登记ssh url链接格式适配 - feat: 【工具】上线支持PHP安全工具-Rips - feat: 【工具】调整androidlint部分规则描述 @@ -72,16 +90,17 @@ - feat: 【客户端】增加在docker中快速使用client的方式 ### Bugfixes + - fix: 【服务端】补充缺失的依赖 - fix: 【Web端】修复下载codedog.ini失败提示 - + ### Docs + - doc: 上线部署文档Q&A - doc: 优化部署文档、帮助文档说明 - doc: 增加产品白皮书 - doc: 补充redis和nginx源码安装参考文档 - - ## V1.0.0 -初始发布 \ No newline at end of file + +初始发布 diff --git a/web/packages/tca-document/zh/community/pr.md b/web/packages/tca-document/zh/community/pr.md index b650120f6..1c0fc8852 100644 --- a/web/packages/tca-document/zh/community/pr.md +++ b/web/packages/tca-document/zh/community/pr.md @@ -1,33 +1,42 @@ ![Welcome](../../images/Welcome.png) PR全称为Pull Request,它是一种代码库的协作方式。开发者可以通过PR将自己在代码库的修改通知到代码库负责人,由原作者评审代码并决定是否能合入。 -> Pull requests let you tell others about changes you've pushed to a branch in a repository on GitHub. Once a pull request is opened, you can discuss and review the potential changes with collaborators and add follow-up commits before your changes are merged into the base branch. + +:::tip +Pull requests let you tell others about changes you've pushed to a branch in a repository on GitHub. Once a pull request is opened, you can discuss and review the potential changes with collaborators and add follow-up commits before your changes are merged into the base branch. +::: # PR操作流程 + ## 一、Fork目标代码库 + ![fork](../../images/Fork.png) -点击Fork后,会在自己名下产生一个相同代码库,比如我Fork CodeAnalysis项目后,会在我名下多出一个CodeAnalysis代码库,地址为https://github.com/Lingghh/CodeAnalysis +点击Fork后,会在自己名下产生一个相同代码库,比如我Fork CodeAnalysis项目后,会在我名下多出一个CodeAnalysis代码库,地址为 ## 二、克隆Fork的代码库并创建分支 + 在本地克隆Fork的代码库并创建分支 -``` +```bash git clone https://github.com/Lingghh/CodeAnalysis git checkout -b dev/add_qa_20220301 ``` 注:也可以在自己Fork的代码库GitHub页面上创建分支。 - + ![fork1](../../images/fork1.png) 接下来就可以在本地修改代码,修改完成后先push到Fork的代码库中. ## 三、在目标项目中提交PR -### 1.进入到目标项目中,点击Pull requests Tab,再点击New pull request就会进入到创建PR的页面。 + +### 1.进入到目标项目中,点击Pull requests Tab,再点击New pull request就会进入到创建PR的页面 + ![New pull request](../../images/NewPullRequest.png) -### 2.进入PR页面后: +### 2.进入PR页面后 + - 点击compare across forks 。 - 点击head repository 。 - 选择自己Fork的代码库和比较的分支,比如我这里选择Lingghh/CodeAnalysis和待合入的分支dev/add_arm64_file 。 @@ -37,7 +46,6 @@ git checkout -b dev/add_qa_20220301 PR创建后,代码库管理员会评审你提交的代码,并决定是否接受该PR。 -## 更多信息请参阅[GitHub PullRequest官方文档](https://docs.github.com/cn/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests/) - -## TCA团队诚邀您的加入! +## 更多信息请参阅[GitHub PullRequest官方文档](https://docs.github.com/cn/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests/) +## TCA团队诚邀您的加入 diff --git a/web/packages/tca-document/zh/guide/web/web.md b/web/packages/tca-document/zh/guide/web/web.md index 76780308d..6cdd73816 100644 --- a/web/packages/tca-document/zh/guide/web/web.md +++ b/web/packages/tca-document/zh/guide/web/web.md @@ -4,9 +4,11 @@ TCA Web 采用 [Lerna](https://www.lernajs.cn/) 进行 `monorepo` 管理。 -> [Lerna GitHub地址](https://github.com/lerna/lerna) -> -> [Lerna 中文命令文档](http://www.febeacon.com/lerna-docs-zh-cn/) +:::tip +[Lerna GitHub地址](https://github.com/lerna/lerna) + +[Lerna 中文命令文档](http://www.febeacon.com/lerna-docs-zh-cn/) +::: 由 `framework`、`login`、`tca-layout`、`tca-analysis`、`tca-manage`微前端以及`tca-document`前端帮助文档组成。 diff --git "a/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/OAuth\347\256\241\347\220\206.md" "b/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/OAuth\347\256\241\347\220\206.md" new file mode 100644 index 000000000..d2cc8fbad --- /dev/null +++ "b/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/OAuth\347\256\241\347\220\206.md" @@ -0,0 +1,18 @@ +# OAuth管理 + +- 可**创建**、**编辑**、**清除**主流代码托管平台的Oauth应用配置,为使用者提供OAuth授权支持。 + +- 支持平台及如何创建OAuth应用: + + - 腾讯工蜂:[创建 OAuth 应用程序](https://code.tencent.com/help/oauth2/) + - GitHub:[创建 OAuth 应用程序](https://docs.github.com/cn/developers/apps/building-oauth-apps/creating-an-oauth-app) + - Gitee:[创建 OAuth 应用程序](https://gitee.com/api/v5/oauth_doc#/list-item-3) + - GitLab:[创建 OAuth 应用程序](https://docs.gitlab.com/ee/integration/oauth_provider.html) + +![OAuth管理](../../../images/manage_oauth_01.png) + +![OAuth管理](../../../images/manage_oauth_02.png) + +::: tip +配置OAuth应用时,回调地址栏需填入当前TCA平台配置的域名或IP地址(如当前页面非80端口,需要显式指定端口号),作为Git平台上OAuth应用的回调地址。 +::: diff --git "a/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\210\206\346\236\220\350\256\260\345\275\225\347\256\241\347\220\206.md" "b/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\210\206\346\236\220\350\256\260\345\275\225\347\256\241\347\220\206.md" new file mode 100644 index 000000000..82db6162f --- /dev/null +++ "b/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\210\206\346\236\220\350\256\260\345\275\225\347\256\241\347\220\206.md" @@ -0,0 +1,7 @@ +# 分析记录管理 + +- 可查看平台**全部分析记录**。 + +- 可点击查阅**分析记录详情**。 + +![分析记录列表](../../../images/manage_job_01.png) \ No newline at end of file diff --git "a/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\220\216\345\217\260\347\256\241\347\220\206\350\257\264\346\230\216.md" "b/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\220\216\345\217\260\347\256\241\347\220\206\350\257\264\346\230\216.md" deleted file mode 100644 index ef7506300..000000000 --- "a/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\220\216\345\217\260\347\256\241\347\220\206\350\257\264\346\230\216.md" +++ /dev/null @@ -1,72 +0,0 @@ -# 后台管理说明 - -仅**超级管理员**可进入后台管理页面 - -包含以下管理页面:`用户管理`、`分析记录管理`、`节点管理`、`工具管理` - -## 用户管理 - -- 可**查看**、**编辑**、**创建**平台用户。 - -- 可配置用户的**登录密码**、**用户级别**、**超级管理员**等。 - -![用户管理](../../../images/manage_01.png) - -## 分析记录管理 - -- 可查看平台**全部分析记录**。 - -- 可点击查阅**分析记录详情**。 - -![分析记录管理](../../../images/manage_02.png) - -## 节点管理 - -- 可查看**常驻节点状态**。 - -- 可**查看**、**编辑**、**删除**常驻节点。 - -- 可配置节点**工具进程**。 - -- 可配置**标签** - -![节点管理](../../../images/manage_03.png) - -## 工具管理 - -- 可查看**全部工具**(包含平台提供工具、团队自定义工具)。 - -- 可**查看**、**编辑**工具。 - -- 可变更工具**权限状态**。 - -![工具管理](../../../images/manage_04.png) - -::: tip -工具的权限状态仅能由**平台管理员**进行变更调整,需谨慎调整 - -- **团队内可用**:即工具配置了可用团队白名单的团队可以使用该工具,默认创建工具的团队已在白名单内 - -- **全平台可用**:即不同团队都可见可用该工具 - -- **支持自定义规则,全平台可用**:即该工具不同团队都可见可用,且支持用户添加团队所需的自定义规则,该自定义规则存在团队隔离,仅团队内可以,其他团队不可使用 -::: - -## OAuth管理 - -- 可**创建**、**编辑**、**清除**主流代码托管平台的Oauth应用配置,为使用者提供OAuth授权支持。 - -- 支持平台及如何创建OAuth应用: - - - 腾讯工蜂:[创建 OAuth 应用程序](https://code.tencent.com/help/oauth2/) - - GitHub:[创建 OAuth 应用程序](https://docs.github.com/cn/developers/apps/building-oauth-apps/creating-an-oauth-app) - - Gitee:[创建 OAuth 应用程序](https://gitee.com/api/v5/oauth_doc#/list-item-3) - - GitLab:[创建 OAuth 应用程序](https://docs.gitlab.com/ee/integration/oauth_provider.html) - -![OAuth管理](../../../images/manage_05.png) - -![OAuth管理](../../../images/manage_06.png) - -::: tip -配置OAuth应用时,回调地址栏需填入当前TCA平台配置的域名或IP地址(如当前页面非80端口,需要显式指定端口号),作为Git平台上OAuth应用的回调地址。 -::: \ No newline at end of file diff --git "a/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" "b/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" new file mode 100644 index 000000000..68a879e2b --- /dev/null +++ "b/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" @@ -0,0 +1,9 @@ +# 团队管理 + +- 可查看平台创建的团队列表,并提供了相应筛选 + +- 可**禁用**、**恢复**团队 + +![团队列表](../../../images/manage_org_01.png) + +![团队操作](../../../images/manage_org_02.png) \ No newline at end of file diff --git "a/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206.md" "b/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206.md" new file mode 100644 index 000000000..0b62da6b1 --- /dev/null +++ "b/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206.md" @@ -0,0 +1,19 @@ +# 工具管理 + +- 可查看**全部工具**(包含平台提供工具、团队自定义工具)。 + +- 可**查看**、**编辑**工具。 + +- 可变更工具**权限状态**。 + +![工具管理](../../../images/manage_tool_01.png) + +::: tip +工具的权限状态仅能由**平台管理员**进行变更调整,需谨慎调整 + +- **团队内可用**:即工具配置了可用团队白名单的团队可以使用该工具,默认创建工具的团队已在白名单内 + +- **全平台可用**:即不同团队都可见可用该工具 + +- **支持自定义规则,全平台可用**:即该工具不同团队都可见可用,且支持用户添加团队所需的自定义规则,该自定义规则存在团队隔离,仅团队内可以,其他团队不可使用 +::: \ No newline at end of file diff --git "a/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\347\224\250\346\210\267\347\256\241\347\220\206.md" "b/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\347\224\250\346\210\267\347\256\241\347\220\206.md" new file mode 100644 index 000000000..b51f8f02b --- /dev/null +++ "b/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\347\224\250\346\210\267\347\256\241\347\220\206.md" @@ -0,0 +1,9 @@ +# 用户管理 + +- 可**查看**、**编辑**、**创建**平台用户。 + +- 可配置用户的**登录密码**、**用户级别**、**超级管理员**等。 + +![用户列表](../../../images/manage_user_01.png) + +![用户编辑](../../../images/manage_user_02.png) \ No newline at end of file diff --git "a/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" "b/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" new file mode 100644 index 000000000..3a23e9ddc --- /dev/null +++ "b/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" @@ -0,0 +1,14 @@ +# 节点管理 + +- 可查看**常驻节点状态**,包含**公共节点**和**团队节点**。 + +- 可**查看**、**编辑**、**删除**常驻节点。 + +- 可配置节点**工具进程**。 + +- 可配置**节点标签** + +![节点管理](../../../images/manage_node_01.png) +![节点管理](../../../images/manage_node_02.png) +![节点管理](../../../images/manage_node_03.png) +![节点管理](../../../images/manage_node_04.png) \ No newline at end of file diff --git "a/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\351\241\271\347\233\256\347\256\241\347\220\206.md" "b/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\351\241\271\347\233\256\347\256\241\347\220\206.md" new file mode 100644 index 000000000..3f6e4fbf2 --- /dev/null +++ "b/web/packages/tca-document/zh/guide/\345\220\216\345\217\260\347\256\241\347\220\206/\351\241\271\347\233\256\347\256\241\347\220\206.md" @@ -0,0 +1,7 @@ +# 项目管理 + +- 可查看平台创建的项目列表,并提供了提供相应筛选 + +- 可**禁用**、**恢复**项目 + +![项目列表](../../../images/manage_team_01.png) \ No newline at end of file diff --git "a/web/packages/tca-document/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" "b/web/packages/tca-document/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" index 234c9e649..274a04e62 100644 --- "a/web/packages/tca-document/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" +++ "b/web/packages/tca-document/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\345\233\242\351\230\237\347\256\241\347\220\206.md" @@ -1,4 +1,4 @@ -# 团队管理 +# 团队说明 ![成员权限](../../../images/team_member.png) diff --git "a/web/packages/tca-document/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\346\210\220\345\221\230\346\235\203\351\231\220.md" "b/web/packages/tca-document/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\346\210\220\345\221\230\346\235\203\351\231\220.md" index 5967036a1..d4dfc5d66 100644 --- "a/web/packages/tca-document/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\346\210\220\345\221\230\346\235\203\351\231\220.md" +++ "b/web/packages/tca-document/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\346\210\220\345\221\230\346\235\203\351\231\220.md" @@ -1,6 +1,6 @@ # 成员权限 -## 团队成员管理 +## 团队成员 ![成员权限](../../../images/team_member.png) @@ -10,7 +10,7 @@ **团队普通成员**:可以创建项目,可以访问自己有权限的项目。创建项目的人会自动成为这个项目的项目管理员。 -## 项目成员管理 +## 项目成员 项目成员分为**项目管理员**和**项目普通成员**。 diff --git "a/web/packages/tca-document/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" "b/web/packages/tca-document/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" new file mode 100644 index 000000000..e9684cd13 --- /dev/null +++ "b/web/packages/tca-document/zh/guide/\345\233\242\351\230\237\347\256\241\347\220\206/\350\212\202\347\202\271\347\256\241\347\220\206.md" @@ -0,0 +1,105 @@ +# 节点与标签 + +除了使用**公共节点**执行代码分析外,团队还可以利用**团队标签**注册并使用**团队节点**。 + +## 名词释义与特点 + +- 团队节点是**团队注册并管理**的**私有**节点。 + +- 团队节点**仅会运行**当前团队所属的分析任务。 + +- 团队标签是用于关联节点机器与分析项目。 + ::: tip + 当一个分析项目在方案中配置运行环境为团队标签后,该项目创建的任务就会下发到团队标签关联的节点机器上运行 + ::: + +## 适用场景 + +1. 业务项目**不想**在公共机器上**排队**等待 + +2. 业务项目**代码比较敏感**,不能在公共机器上运行 + +3. 业务项目需要**依赖特定**的**机器环境**(比如CPU架构、操作系统等) + +4. ... + +以上场景,均可考虑使用团队节点,业务团队提供机器资源接入作为团队节点,仅分析自己业务的代码库,**保证执行效率**,**保护源码不泄漏**,**支持项目特殊依赖**等 + +## 团队节点注册 + +- 根据环境下载客户端二进制文件或拉取源码,参考[客户端](../客户端/配置说明.md)。 + +- 通过终端启动客户端: + + - 客户端二进制启动 + + ```bash + ./codepuppy start -t TOKEN --org-sid ORG_SID + ``` + + - 客户端源码启动 + + ```bash + python3 codepuppy.py start -t TOKEN --org-sid ORG_SID + ``` + + ::: tip + 1. TOKEN 可以从平台**个人中心-个人令牌**页面获取 + 2. ORG_SID 可以从**页面链接**中获取 + ::: + +## 团队节点管理 + +完成团队节点注册后,可以在当前团队下看到对应的节点信息,同时**需要进行配置** + +::: warning +- 团队节点**首次注册**时,需要手动在平台上配置**所属标签**、**节点可用性**、**工具进程**等。 +- 将节点的**节点可用性**调整为**活跃**后,运行客户端节点的终端会输出**心跳上报成功**的日志 +::: + +- 首次注册团队节点,节点状态为不可用 + + ![注册团队节点](../../../images/org_node_manager_1.png) + +- 调整后的节点 + + ![注册团队节点](../../../images/org_node_manager_2.png) + +- 配置节点关联的工具进程: + + ![配置工具进程](../../../images/org_node_process.png) + +::: tip +1. 团队节点使用的**所属标签**均为当前团队内创建的标签,可参见[团队标签管理](#团队标签管理) +2. 团队标签可以参考`CodeDog`标签为不同的系统类型(Linux、MacOS、Windows)建立标签,比如`专属标签-Linux`、`专属标签-Mac`等 +::: + +### 团队节点执行任务范围 + +::: warning 使用团队节点运行分析任务的前提 +对应分析项目使用的分析方案中,需要配置分析方案中的**运行环境**为该团队节点配置的所属标签。 +::: + +::: warning 团队节点执行的任务范围取决于该节点的负责人 +- 如果节点负责人为团队管理员,该节点可以执行当前团队所有项目的分析任务 +- 如果节点负责人为项目管理员,该节点只能运行指定项目下的分析任务 +- 如果节点负责人为部分代码库的管理员,该节点只能运行对应代码库的分析任务 +::: + + +## 团队标签管理 + +您可以创建一个团队标签,并配置到您的团队节点和您的分析方案中 + +- 创建团队标签。 + + ![创建团队标签](../../../images/org_tag_manager.png) + +- 配置团队节点所属标签。 + + ![节点配置团队标签](../../../images/org_tag_node.png) + +- 配置分析方案运行环境。 + + ![方案配置团队标签](../../../images/org_tag_scheme.png) + diff --git "a/web/packages/tca-document/zh/guide/\345\256\242\346\210\267\347\253\257/\351\205\215\347\275\256\350\257\246\346\203\205.md" "b/web/packages/tca-document/zh/guide/\345\256\242\346\210\267\347\253\257/\351\205\215\347\275\256\350\257\246\346\203\205.md" new file mode 100644 index 000000000..8cbe12b5a --- /dev/null +++ "b/web/packages/tca-document/zh/guide/\345\256\242\346\210\267\347\253\257/\351\205\215\347\275\256\350\257\246\346\203\205.md" @@ -0,0 +1,26 @@ +## 在线分析 + +在线分析即是通过Server端将分析任务注册到执行队列中,并将任务分配到平台配置的常驻节点上,在常驻节点执行分析,分析完毕后将分析结果上报入库。 + +::: tip +平台需要存在常驻节点,请查阅 [常驻节点分析](./常驻节点分析.md) + +否则任务因没有机器而无法完成分配,超时后任务会注销。 +::: + +## 客户端分析 + +客户端分析即是本地分析,可直接配置本地的客户端配置文件,或在平台上配置好对应信息后,下载配置文件,替换客户端配置问题,并启动客户端分析。分析完毕后会将数据上报入库。 + +- 下载配置文件 + + ![下载配置文件](../../../images/start_scan_03.png) + +- 替换客户端配置文件,并启动客户端分析。 + +::: tip +本地需要下载客户端,请查阅 + +- [部署与配置客户端](../../quickStarted/deployClient.md) +- [客户端使用说明文档](./本地分析.md) +::: \ No newline at end of file diff --git "a/web/packages/tca-document/zh/guide/\345\256\242\346\210\267\347\253\257/\351\205\215\347\275\256\350\257\264\346\230\216.md" "b/web/packages/tca-document/zh/guide/\345\256\242\346\210\267\347\253\257/\351\205\215\347\275\256\350\257\264\346\230\216.md" index 887b8fa1f..34bd7bc6e 100644 --- "a/web/packages/tca-document/zh/guide/\345\256\242\346\210\267\347\253\257/\351\205\215\347\275\256\350\257\264\346\230\216.md" +++ "b/web/packages/tca-document/zh/guide/\345\256\242\346\210\267\347\253\257/\351\205\215\347\275\256\350\257\264\346\230\216.md" @@ -3,23 +3,27 @@ ## 一、基础配置 ### 1. 机器配置推荐 + | 操作系统 | 推荐配置 | | --------: | :------------------------------------------- | -| Linux | 8核16G内存,硬盘空间256G(可用空间不低于100G) | +| Linux | 8核16G内存,硬盘空间256G(可用空间不低于100G) | | Mac | 8核16G内存,硬盘空间256G(可用空间不低于100G) | | Windows | 8核16G内存,硬盘空间256G(可用空间不低于100G) | 以上为推荐配置,实际情况需要考虑扫描对象代码库的大小,按实际情况增加磁盘空间。 ### 2. 配置client/config.ini文件 + -(1)将``替换成实际的serve ip(可包含端口号)。 -(2)国内使用github拉取网络较慢,推荐使用腾讯工蜂拉取,需要修改以下配置: -- 修改 TOOL_CONFIG_URL=https://git.code.tencent.com/TCA/tca-tools/puppy-tools-config.git + +- 修改 TOOL_CONFIG_URL= - 前往[腾讯工蜂网站](https://git.code.tencent.com)注册账号,作为拉取工具使用(已经注册过的可以直接使用) - 将腾讯工蜂的账号密码填写到`TOOL_LOAD_ACCOUNT`中(由于腾讯工蜂的开源仓库也要求使用账号才能拉取,所以此处必须填写账号密码) ### 3. 配置client/codedog.ini文件(分布式节点模式无需配置) + 填写以下必填项:`token`,`org_sid`,`team_name`,`source_dir` | 字段名 | 填写说明 | @@ -31,28 +35,40 @@ 其他可选项按需填写。 - ## 二、使用docker环境快速体验 -> 适用于快速上手体验。使用docker运行,可以免去客户端环境依赖的安装,避免环境兼容性问题。 -> 但是由于环境受限于docker,会无法复用本地的编译环境,部分需要编译的工具无法使用。 + +:::tip +适用于快速上手体验。使用docker运行,可以免去客户端环境依赖的安装,避免环境兼容性问题。 + +但是由于环境受限于docker,会无法复用本地的编译环境,部分需要编译的工具无法使用。 +::: + ### 1. 下载和安装Docker + 参考Docker官方文档:[Docker下载和安装](https://docs.docker.com/get-started/) ### 2. 构建docker镜像 + 在`client`目录下,执行以下命令:`docker build -t tca-client .` ### 3. 执行docker容器,扫描代码,可选以下两种方式 + #### (1)直接使用docker运行 + - 在client目录下,执行以下命令: - (注意:按照实际情况填写`SOURCE_DIR`环境变量值) + ```bash export SOURCE_DIR=需要扫描的代码目录绝对路径 docker run -it --rm -v $PWD:/workspace/client -v $SOURCE_DIR:/workspace/src --name tca-client tca-client ``` + #### (2)使用docker内bash终端运行 + - 通过以下方式,进入容器内的bash终端后,通过命令行启动client代码: - 在client目录下,执行以下命令: - (注意:按照实际情况填写`SOURCE_DIR`环境变量值) + ```bash export SOURCE_DIR=需要扫描的代码目录绝对路径 docker run -it --rm -v $PWD:/workspace/client -v $SOURCE_DIR:/workspace/src --name tca-client tca-client bash @@ -60,53 +76,66 @@ docker run -it --rm -v $PWD:/workspace/client -v $SOURCE_DIR:/workspace/src -- python3 codepuppy.py localscan ``` - ## 三、使用本地机器环境运行 -> 适用于深度体验,可以复用本地编译环境,使用编译型代码分析工具。 -> 可能会有系统环境兼容问题。 + +:::tip +适用于深度体验,可以复用本地编译环境,使用编译型代码分析工具。 + +可能会有系统环境兼容问题。 +::: ### 1. 安装Python环境和第三方库 -- (1) 预装Python3.7、pip,支持 `python3` 和 `pip3` 命令 + +- (1) 预装Python3.7、pip,支持 `python3` 和 `pip3` 命令 - (2) 安装依赖:`pip3 install -r client/requirements/app_reqs.pip` ### 2. 安装第三方工具 + - (1) 进入到`client/requirements`目录 - (2) 在命令行中执行安装脚本`install.sh`(linux/mac环境)或`install.bat`(windows环境) - ### 3. 启动代码分析 + - (1) 进入到`client`目录下 - (2) 执行命令:`python3 codepuppy.py localscan` - ## 四、使用分布式节点模式执行客户端 -> TCA客户端除了通过`localscan`命令启动单次的代码分析,也可以作为一个分布式分析节点启动,作为常驻进程,多个节点可以分布式并行执行服务端下发的任务,提高扫描效率。 -> 和本地执行任务一样,需要先安装环境和必要的工具,并配置好服务端地址。 - + +:::tip + +- CA客户端除了通过`localscan`命令启动单次的代码分析,也可以作为一个分布式分析节点启动,作为常驻进程,多个节点可以分布式并行执行服务端下发的任务,提高扫描效率。 +- 和本地执行任务一样,需要先安装环境和必要的工具,并配置好服务端地址。 +::: + ### 1. 安装Python环境和第三方库 -- (1) 预装Python3.7、pip,支持 `python3` 和 `pip3` 命令 + +- (1) 预装Python3.7、pip,支持 `python3` 和 `pip3` 命令 - (2) 安装依赖:`pip3 install -r client/requirements/app_reqs.pip` ### 2. 安装第三方工具 + - 进入到`client/requirements`目录 - 在命令行中执行安装脚本`install.sh`(linux/mac环境)或`install.bat`(windows环境) ### 3. 启动代码分析节点 + - (1)从tca页面`个人中心`-`个人令牌`-复制Token - (2)进入到`client`目录下,执行命令:`python3 codepuppy.py -l codepuppy.log start -t ` - (3)启动后,可以在命令行输出或`codepuppy.log`中查看运行日志,如果未报异常,且输出`task loop is started.`,表示节点已经正常启动。 ### 4. 配置节点 + - 从tca页面`管理入口`-`节点管理`,可以看到当前在线的节点,可以修改节点名称、标签、负责人等信息。 - 可以进入工具进程配置页面,对节点支持的工具进程进行管理(默认会全部勾选),未勾选的工具进程,将不会在该节点上执行。 - 节点所属标签会与分析方案中的运行环境标签进行匹配,只有相同标签的任务才会下发到该机器节点上。 - ## 五、其他配置与用法 ### 1. 配置使用本地工具 -> 如果由于网络原因,执行时无法从github自动拉取工具,或拉取比较慢,可以参考基础配置腾讯工蜂工具地址,或使用以下方式预先下载好工具,配置使用本地工具目录。 +:::warning +如果由于网络原因,执行时无法从github自动拉取工具,或拉取比较慢,可以参考基础配置腾讯工蜂工具地址,或使用以下方式预先下载好工具,配置使用本地工具目录。 +::: - (1)下载工具配置库 `https://github.com/TCATools/puppy-tools-config.git` ,存放到 `client/data/tools`目录下(如果未生成,可先创建该目录)。 - (2)根据当前机器操作系统,查看`puppy-tools-config`目录下的`linux_tools.ini`或`mac_tools.ini`或`windows_tools.ini`文件,将`[tool_url]`中声明的所有工具下载到 `client/data/tools`目录下。 @@ -114,7 +143,9 @@ python3 codepuppy.py localscan ### 2. 使用自建git server存放工具 -> 如果自己搭建了一套git server,可以将工具配置库 `https://github.com/TCATools/puppy-tools-config.git` 以及里面声明的工具仓库,存放到自建git serevr上。 +:::warning +如果自己搭建了一套git server,可以将工具配置库 `https://github.com/TCATools/puppy-tools-config.git` 以及里面声明的工具仓库,存放到自建git serevr上。 +::: - (1)将工具配置库 `https://github.com/TCATools/puppy-tools-config.git` 上传到自建git仓库。 - (2)按所需的操作系统,将`puppy-tools-config`仓库下的`linux_tools.ini`或`mac_tools.ini`或`windows_tools.ini`文件中`[tool_url]`声明的所有工具库,上传到自建git仓库。 diff --git "a/web/packages/tca-document/zh/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206\350\257\264\346\230\216.md" "b/web/packages/tca-document/zh/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206\350\257\264\346\230\216.md" index 329e9608e..45082ec7f 100644 --- "a/web/packages/tca-document/zh/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206\350\257\264\346\230\216.md" +++ "b/web/packages/tca-document/zh/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\347\256\241\347\220\206\350\257\264\346\230\216.md" @@ -20,6 +20,10 @@ 需平台管理员在**后台管理**-**工具管理**中找到对应工具,并将其权限状态调整为**支持自定义规则**。 ::: +## 自定义工具 +### 工具白名单 +默认自定义工具只能当前团队内使用,添加 `工具白名单` 后可以让其他团队使用。 + ## 使用场景说明 ::: tip diff --git "a/web/packages/tca-document/zh/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\345\267\245\345\205\267.md" "b/web/packages/tca-document/zh/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\345\267\245\345\205\267.md" index 199aff88c..249135666 100644 --- "a/web/packages/tca-document/zh/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\345\267\245\345\205\267.md" +++ "b/web/packages/tca-document/zh/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\345\267\245\345\205\267.md" @@ -1,6 +1,6 @@ # 自定义工具 -腾讯云代码分析平台支持用户自助添加管理工具。 +腾讯云代码分析平台支持用户自助添加代码分析工具。 适用场景:自定义规则无法满足团队业务复杂需求,需要更多的代码逻辑来匹配目标代码的情况。通常需要团队业务方自行实现对应代码分析工具。 @@ -10,62 +10,63 @@ 2. 提交工具到 git 代码库 3. 在页面创建新工具 4. 为工具添加规则 -5. 在项目分析方案中添加规则 +5. 将工具配置到执行节点 +6. 在项目分析方案中添加规则 ## 自定义工具步骤说明 ### 第一步,编写代码,实现分析工具逻辑 根据需要匹配的目标代码场景,编写对应的工具逻辑。 -可以参考 Python 写的 [Demo 项目](https://github.com/TCATools/demo_tool) +可以参考 Python 实现的 [Demo 项目](https://github.com/TCATools/demo_tool)。 **必要:** -- **运行方式**:支持命令行执行,比如 python run.py 或 run.exe,执行命令的工作目录为工具代码的根目录 +- **运行方式**:支持命令行执行,比如 python run.py 或 run.exe,执行命令的工作目录为工具代码的根目录。 -- **运行环境说明**: - - 平台已内置**Python 环境**和**Java 环境**。 - - ::: tip - 已内置 Python 环境(可以指定 Python 版本),如果依赖第三方 pip 包,不能保证已内置 - - 已内置 Java 环境(内置 JDK_8_HOME 环境变量,并在 PATH 环境变量中添加了 JDK_8_HOME 的 bin 目录,可以按需获取使用) - ::: - - **以上内置环境无法支持?** - - - 建议把依赖包内置在工具 git 库中,自行加载所需环境(比如执行时,自动将内置依赖目录,添加到 PATH 环境变量) - - - 也可以将工具打包成可执行程序(比如用 pyinstaller 打包 python 代码) +- **运行环境说明**: + - 建议将工具打包编译成可执行程序,拉取下来直接可以执行。 + - 如果工具需要在特定的环境中运行,比如python、java环境,平台提供了丰富的工具依赖包,可以在`工具管理`-`工具依赖`中查看,创建工具时可供选择,执行时会自动配置好依赖环境。 + - 如果现有的工具依赖包未支持所需依赖,也可以创建新的工具依赖使用。 - **平台已提供的环境变量** - 使用方式请参考 [Demo 项目](https://github.com/TCATools/demo_tool) - - ```python - # TCA 已提供的环境变量 + - 获取及使用方式请参考 [Demo 项目](https://github.com/TCATools/demo_tool)。 + ``` SOURCE_DIR:要扫描的代码目录路径 - DIFF_FILES: 增量文件列表 - TASK_REQUEST: 任务参数json文件路径 - - # 获取环境变量demo(Python 示例代码): - import os - source_dir = os.environ.get("SOURCE_DIR", None) + DIFF_FILES: 值为一个json文件路径,文件内容为增量扫描的文件列表(增量扫描时可用) + SCAN_FILES: 值为一个json文件路径,文件内容为需要扫描的文件列表(增量或全量扫描均可用) + TASK_REQUEST: 值为一个json文件路径,文件内容为当前扫描任务参数 ``` - + +- **工具命令声明** + + 在工具仓库根目录下,添加一个`tool.json`文件,声明工具的检查和扫描命令,比如: + ```json + { + "check_cmd": "python src/main.py check", + "run_cmd": "python src/main.py scan" + } + ``` + 参数说明: + - `check_cmd`: + - 功能:判断当前执行环境是否满足工具要求(如果不需要检查,也可以没有这个命令)。 + 比如某些工具只能在linux下执行,需要判断当前是否为linux环境。 + - 输出:将判断结果输出到`check_result.json`文件中,文件内容为`{"usable": true}`或`{"usable": false}`。 + - `run_cmd`: + - 功能:扫描代码,执行自定义检查器逻辑(该命令必须存在)。 + - 输出:按照指定格式,输出结果到`result.json`文件中。 + - **工具输出格式要求** - 将结果输出到当前工作目录下的`result.json`文件中(Python 示例代码) - + - 将扫描结果输出到当前工作目录下的`result.json`文件中(Python 示例代码) ```python import json with open("result.json", "w") as fp: json.dump(result, fp, indent=2) ``` - `result.json` 文件格式如下: - + - `result.json` 文件格式如下: ```json [ { @@ -90,25 +91,29 @@ **`refs`** 字段说明: - 非必需项,可无。该字段记录问题回溯路径信息。比如当前文件的回溯路径其他的 3 行代码,可以将这三行的路径及提示信息,按顺序添加到 refs 数组中。 + 非必需项,可无。该字段记录问题回溯路径信息。比如当前行的代码问题,是经过上下文的三行代码执行路径而导致的,可以将这三行的位置及提示信息,按顺序添加到 refs 数组中。 ### 第二步,提交工具到 git 代码库 -- 创建代码库,将工具源代码或编译打包后的可执行文件提交到代码库中 +- 创建代码库,将工具源代码或编译打包后的可执行文件,提交到代码仓库中(建议提交到master分支,TCA默认拉取的是master分支)。 - 建议代码库中加入 README.md 文件,说明工具功能和维护人 +- 建议代码库中加入 README.md 文件,说明工具功能和维护人。 -- 后续需要修改规则实现逻辑,可以直接更新代码库,TCA 平台在执行该工具时,会自动拉取最新工具版本 +- 后续需要修改工具实现逻辑,可以直接更新代码库,TCA 平台在执行该工具时,会自动拉取最新工具代码版本。 ### 第三步,在工具管理页面中创建工具 - 进入工具管理页面,点击创建工具 + ![enter image description here](../../../images/customtool_01.png) + - 填写工具信息 + ![enter image description here](../../../images/customtool_02.png) + **部分参数说明:** - - **工具仓库地址**,即前述步骤中提交的工具 git 代码库地址 + - **工具仓库地址**,即前述步骤中提交的工具 git 代码库地址,默认拉取的是master分支,如果是其他分支,需要在仓库地址后加上`#分支名`,比如:`https://github.com/xxx/xxx.git#main` - **工具认证**,授权拉取工具仓库的权限 @@ -116,23 +121,30 @@ - **环境变量**,工具执行所需的环境变量 - ::: tip - 我们已提供以下公共环境变量 + - **License**,如果是开源工具,填写工具遵循的开源协议,或者填写自研共建 - ```python - python_version = 可选,如果工具是python执行,可以指定python版本,可选值:2,3,3.8(3指的是3.7) - ``` + - **是否为编译型工具**,表示在使用该工具对用户代码进行分析时,是否要求代码需要编译或可执行编译 - ::: - - **License**,如果是开源工具,填写工具遵循的开源协议,或者填写自研共建 +- 添加工具依赖 + + ![enter image description here](../../../images/customtool_03.png) + + 添加完成后,会展示已添加的依赖方案: + + ![enter image description here](../../../images/customtool_04.png) + +**工具依赖说明:** +- 比如当前的demo工具,只需要依赖python3运行,而且支持在linux x86_64、linux arm64、mac和windows下执行,那么只需要配置一个依赖方案(如上图),并配置为默认方案。在不同的操作系统中,会自动加载对应操作系统的python环境。 +- 如果需要根据扫描项目设置的环境变量,加载不同的依赖配置,则可以配置不同的判断条件,使用多个依赖方案。 - - **是否为编译型工具**,表示在使用该工具对用户代码进行分析时,是否要求代码需要编译或可执行编译 ### 第四步,为工具添加规则 - 完成工具创建后,进入规则列表,为工具添加规则 + ![enter image description here](../../../images/customtool_05.png) + - 填写规则信息 **部分参数说明:** @@ -148,22 +160,24 @@ ### 第五步,将工具配置到执行节点 ::: tip -需要联系平台管理员协助操作,在节点管理-工具进程配置中找到对应工具,将其配置到对应机器上。 +需要联系平台管理员协助操作,在`管理入口`-`节点管理`中进入需要配置的机器节点的`工具进程配置`中,找到对应工具,勾选工具进程。 -完成节点配置工具进程后,才能在项目中采用该工具进行分析 +完成节点配置工具进程后,才能在项目中采用该工具进行分析。 ::: +![enter image description here](../../../images/customtool_06.png) + ### 第六步,完成上述操作,在项目中使用工具规则 -- 进入到项目中,在分析方案-代码检查进行规则配置 +- 进入到项目中,在`分析方案`-`代码检查`进行规则配置。 -- 点击添加规则,找到对应工具规则进行添加 +- 点击添加规则,找到对应工具规则进行添加。 -- 添加完成后,启动分析,建议启动一次全量分析 +- 添加完成后,启动分析,为了将规则应用到所有代码文件,建议启动一次全量分析(增量分析只会分析自上次扫描后变更的文件)。 ## 自定义工具权限说明 -- **默认自定义工具仅团队管理员可操作,团队内所有成员可使用,** +- **默认自定义工具仅团队管理员可操作,团队内所有成员可使用。** - 团队管理员才能创建工具,添加工具规则等,具备该工具全部权限 diff --git "a/web/packages/tca-document/zh/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\350\247\204\345\210\231.md" "b/web/packages/tca-document/zh/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\350\247\204\345\210\231.md" index ac81f02f2..6912b5b43 100644 --- "a/web/packages/tca-document/zh/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\350\247\204\345\210\231.md" +++ "b/web/packages/tca-document/zh/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\350\207\252\345\256\232\344\271\211\350\247\204\345\210\231.md" @@ -27,8 +27,7 @@ 1. **根据团队业务需求设计正则表达式** ::: tip - > - > 建议先测试好正则表达式是否正确,正则表达式测试网站推荐:[http://tool.oschina.net/regex](http://tool.oschina.net/regex) + 建议先测试好正则表达式是否正确,正则表达式测试网站推荐:[http://tool.oschina.net/regex](http://tool.oschina.net/regex) 规则示例: diff --git "a/web/packages/tca-document/zh/guide/\345\277\253\351\200\237\345\205\245\351\227\250/\345\277\253\351\200\237\345\220\257\345\212\250\344\270\200\346\254\241\344\273\243\347\240\201\345\210\206\346\236\220.md" "b/web/packages/tca-document/zh/guide/\345\277\253\351\200\237\345\205\245\351\227\250/\345\277\253\351\200\237\345\220\257\345\212\250\344\270\200\346\254\241\344\273\243\347\240\201\345\210\206\346\236\220.md" index 97bd61bf6..70bdc82cd 100644 --- "a/web/packages/tca-document/zh/guide/\345\277\253\351\200\237\345\205\245\351\227\250/\345\277\253\351\200\237\345\220\257\345\212\250\344\270\200\346\254\241\344\273\243\347\240\201\345\210\206\346\236\220.md" +++ "b/web/packages/tca-document/zh/guide/\345\277\253\351\200\237\345\205\245\351\227\250/\345\277\253\351\200\237\345\220\257\345\212\250\344\270\200\346\254\241\344\273\243\347\240\201\345\210\206\346\236\220.md" @@ -6,7 +6,9 @@ ![创建团队](../../../images/create_team.png) - > 还没有团队?点击了解[团队管理](../团队管理/团队管理.md) + :::tip + 还没有团队?点击了解[团队管理](../团队管理/团队管理.md) + ::: - **为团队创建一个项目,或选择一个已有项目,并进入项目内** diff --git "a/web/packages/tca-document/zh/guide/\346\234\215\345\212\241\345\231\250/deploy_with_minio.md" "b/web/packages/tca-document/zh/guide/\346\234\215\345\212\241\347\253\257/deploy_with_minio.md" similarity index 100% rename from "web/packages/tca-document/zh/guide/\346\234\215\345\212\241\345\231\250/deploy_with_minio.md" rename to "web/packages/tca-document/zh/guide/\346\234\215\345\212\241\347\253\257/deploy_with_minio.md" diff --git "a/web/packages/tca-document/zh/guide/\346\234\215\345\212\241\347\253\257/deploy_without_migrate.md" "b/web/packages/tca-document/zh/guide/\346\234\215\345\212\241\347\253\257/deploy_without_migrate.md" new file mode 100644 index 000000000..1eef61455 --- /dev/null +++ "b/web/packages/tca-document/zh/guide/\346\234\215\345\212\241\347\253\257/deploy_without_migrate.md" @@ -0,0 +1,15 @@ +在实际的生产环境的部署过程中,团队的MySQL的管理员可能不会给到应用账号create等比较敏感的权限,这种情况下,我们可以通过手动迁移数据的方式起到和等同Django migrate的效果。 + +操作步骤: + +1. 进入Server服务工作目录后(假设工作目录为 ``/data/CodeAnalysis/server/``,以下路径均为工作目录内的相对路径) +2. 在开发环境一个有全部权限的MySQL地址,初始化数据(MySQL版本运行版本:5.7) + - 执行``vi ./scripts/config.sh``:填写一个有全部权限的MySQL数据库地址和Redis信息以及根据需要调整配置信息,主要的工程配置已提供默认值,字段说明可以查看[文档](../server/README.md) + - 执行``bash ./scripts/deploy.sh init``:初始化DB、安装依赖和运行初始化脚本 + - 使用MySQLDump工具导出表结构与数据:``mysqldump -u user -p –databases codedog_main codedog_analysis codedog_file codedog_login > codedog_all.sql`` +3. 在生产环境建数据库,详情见:``server/sql/init.sql`` +4. 连接MySQL,导入数据: + - 临时关闭外键检查: ``SET SESSION FOREIGN_KEY_CHECKS=0``,否则会因为数据中有外键关联导致导入失败 + - 导入表结构与数据: ``source /youdir/codedog_all.sql;`` + - 开启外键检查: ``SET SESSION FOREIGN_KEY_CHECKS=1`` +5. 启动服务: 直接执行 ``bash ./scripts/deploy.sh start``,无需执行 ``init``方法,否则会导致数据重复写入 diff --git "a/web/packages/tca-document/zh/guide/\346\234\215\345\212\241\345\231\250/server.md" "b/web/packages/tca-document/zh/guide/\346\234\215\345\212\241\347\253\257/server.md" similarity index 97% rename from "web/packages/tca-document/zh/guide/\346\234\215\345\212\241\345\231\250/server.md" rename to "web/packages/tca-document/zh/guide/\346\234\215\345\212\241\347\253\257/server.md" index 6ffc73417..8d1b25a01 100644 --- "a/web/packages/tca-document/zh/guide/\346\234\215\345\212\241\345\231\250/server.md" +++ "b/web/packages/tca-document/zh/guide/\346\234\215\345\212\241\347\253\257/server.md" @@ -130,3 +130,4 @@ File存储引擎配置 - SCMPROXY_HOST:ScmProxy服务的HOST,默认为``0.0.0.0`` - SCMPROXY_PORT:ScmProxy服务监听端口,默认为``8009`` - SCMPROXY_SENTRY_URL:ScmProxy服务异常日志上报至sentry配置 +- SCMPROXY: 通过本环境变量去指定其他服务调用ScmProxy服务的地址,默认值为``127.0.0.1:8009`` diff --git a/web/packages/tca-document/zh/quickStarted/FAQ.md b/web/packages/tca-document/zh/quickStarted/FAQ.md index 6ce3c01ad..481d771b8 100644 --- a/web/packages/tca-document/zh/quickStarted/FAQ.md +++ b/web/packages/tca-document/zh/quickStarted/FAQ.md @@ -1,7 +1,10 @@ -# TCA部署与使用常见问题 +# 常见问题 -> 该Q&A文档会持续更新,非常欢迎您的建议与共建!~ -> 如果您遇到任何未在此处列出的部署或使用问题,请在 GitHub issue 系统中进行搜索。如果仍未找到该错误消息,您可以通过[社区](../community/joingroup.md)提出问题,获得帮助。 +:::tip +该Q&A文档会持续更新,非常欢迎您的建议与共建! + +如果您遇到任何未在此处列出的部署或使用问题,请在 GitHub issue 系统中进行搜索。如果仍未找到该错误消息,您可以通过[社区](../community/joingroup.md)提出问题,获得帮助。 +::: [[toc]] @@ -140,13 +143,16 @@ RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak && \ - [Ubuntu安装Python3.7文档](https://github.com/Tencent/CodeAnalysis/blob/main/doc/references/install_python37_on_ubuntu.md) #### 1.6 执行``compose_init.sh``脚本的``pip install``提示``sha256``不匹配错误 + 在构建镜像的``pip install``步骤提示以下报错时: + ``` ERROR: THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE. If you have updated the package versions, please update the hashes. Otherwise, examine the package contents carefully; someone may have tampered with them. setuptools from https://mirrors.cloud.tencent.com/pypi/packages/fb/58/9efbfe68482dab9557c49d433a60fff9efd7ed8835f829eba8297c2c124a/setuptools-62.1.0-py3-none-any.whl#sha256=26ead7d1f93efc0f8c804d9fafafbe4a44b179580a7105754b245155f9af05a8: Expected sha256 26ead7d1f93efc0f8c804d9fafafbe4a44b179580a7105754b245155f9af05a8 Got ddaacc49de5c08c09d744573240a9a49f24f65c5c72380e972433784caa68d98 ``` + 可以执行``export ORIGIN=normal``,然后再执行``./compose_init.sh`` >注:执行``export``命令的作用是调整为``pypi``默认官方下载源进行``pip install`` @@ -187,7 +193,6 @@ ln -s /usr/local/python3/bin/gunicorn /usr/local/bin/gunicorn ln -s /usr/local/python3/bin/celery /usr/local/bin/celery ``` - ### 2. 服务启动与初始化 #### 2.1 ``compose_init.sh``脚本需要填写的密码是什么? diff --git a/web/packages/tca-document/zh/quickStarted/codeDeploy.md b/web/packages/tca-document/zh/quickStarted/codeDeploy.md new file mode 100644 index 000000000..95cd0ae1c --- /dev/null +++ b/web/packages/tca-document/zh/quickStarted/codeDeploy.md @@ -0,0 +1,90 @@ +# 源码部署 +兼容旧版的部署方式 +#### 依赖环境 + +- 系统环境 + - Linux + - 最低配置:2核4G内存、100G硬盘存储空间 + +- 环境准备 +> 目前TCA脚本已封装好Python、Mariadb、Redis与Nginx安装步骤,可以按“操作说明”内容进行操作 + + - **Python 3.7**,[安装指引](./references/install_python37_on_centos.md) + + - **MySQL服务(MySQL5.7.8以上版本或Mariadb 10.5以上版本)**,[安装指引](./references/install_mysql_on_centos.md) + + - **Redis服务(4.0版本以上)**,[安装指引](./references/install_redis_on_centos.md) + + - **Nginx服务** + + :::warning + 仅适用于本地部署体验,生产环境建议使用专业的 MySQL、Redis 等服务 + ::: + +- 权限准备 + + - 环境权限:安装 Server 依赖软件(python、nginx、yum 等软件包)需要使用 ROOT 权限 + - 启动 Server服务时可以使用非 ROOT 用户运行 + - 数据库权限:Server 服务执行数据库初始化需要依赖 ``CREATE、ALTER、INDEX、DELETE、LOCK TABLES、SELECT、INSERT、REFERENCES、UPDATE`` 权限 +- 端口使用:需要开放80端口的访问权限(80为TCA平台默认访问端口),或调整 Web 服务默认的访问端口地址 + +#### 操作说明 + +##### 首次启动操作 + +1. 进入CodeAnalysis工作目录(例如``~/CodeAnalysis``),以下路径均为目录内的相对路径 +2. 安装基础软件与部署TCA(可根据脚本选项确定是否要安装相关基础软件),执行 + ```bash + $ bash ./quick_install.sh local deploy + ``` + 执行该命令会做以下事情: + - 检测本地Python3.7、Mariadb/MySQL、Redis与Nginx,如果不存在会提示安装(install) + - 部署TCA Server、Web与Client,并进行初始化(install) + - 启动TCA Server、Web与Client(start) + - 检测TCA的运行状态(check) + + >注:在运行过程中,脚本会检测本地是否安装了相关基础软件(Python3.7、MySQL/Mariadb、Redis、Nignx),如果未安装会输出以下类似提示语: + >``` + >Do you want to install [Redis] by this script? + >Please enter:[Y/N] + >``` + >如果确定通过脚本安装可以输入`Y`。 +3. 执行完成,无其他报错,即可登录: + - TCA 平台初始登录账号是``CodeDog``,密码是``admin``, + +##### 更新操作 +1. 更新代码 +2. 执行以下命令: + - `bash ./quick_install.sh local install tca`:更新相关配置 + - `bash ./quick_install.sh local start`:启动服务(会自动关闭之前的服务) + - `bash ./quick_install.sh local check`:检查服务是否启动失败 + +注: +1. `local install`命令行参数说明: + - `base`:安装Python、Mariadb/MySQL、Redis与Nginx + - `tca`:初始化或更新TCA Server、Web、Client相关配置和数据 + - `server`:初始化或更新TCA Server相关配置和数据 + - `web`:初始化或更新TCA Web相关配置和数据 + - `client`:初始化或更新TCA Client相关配置和数据 + - 不填参数,默认会执行`base`、`tca`相关操作 + +##### 启动和停止服务 + +- 启动所有服务:`bash ./quick_install.sh local start` +- 启动Main相关服务:`bash ./quick_install.sh local start main` + - `local start`支持启动指定服务,如上述的启动Main服务,还支持`mysql/redis/analysis/file/login/scmproxy/nginx/client/all` +- 停止所有服务:`.bash /quick_install.sh local stop` +- 停止Main相关服务:`bash ./quick_install.sh local stop main` + - `local stop`支持停止指定服务,如上述的停止Main服务,还支持`analysis/file/login/scmproxy/nginx/client/all` + +注: +1. 启动时会自动关闭之前已经运行的服务 +2. `local start`支持启动指定服务,如上述的启动Main服务,还支持`mysql/redis/main/analysis/file/login/scmproxy/nginx/all` + - `mysql`和`redis`默认会使用`systemctl`进行启动,如果`systemctl`无法使用,则会直接使用`nohup`方式运行相关服务 + +##### 检查服务运行状态 +检查服务运行状态:`bash ./quick_install.sh local check` + - 目前支持检查server与web,暂不支持client + +##### 获取服务输出日志 +打印TCA Server各个服务的日志路径: `bash ./quick_install.sh local log` \ No newline at end of file diff --git a/web/packages/tca-document/zh/quickStarted/deployClient.md b/web/packages/tca-document/zh/quickStarted/deployClient.md index 9dde5a9f2..723dd738e 100644 --- a/web/packages/tca-document/zh/quickStarted/deployClient.md +++ b/web/packages/tca-document/zh/quickStarted/deployClient.md @@ -4,86 +4,115 @@ ### 依赖环境 -- 系统要求 +- 系统环境 - - Linux,Windows或macOS - - [Python 3.7](https://docs.python.org/zh-cn/3.7/using/unix.html) + - Linux,Windows或macOS +- 环境准备 -### 部署步骤 + - **Python 3.7**,[安装指引](./references/install_python37_on_centos.md) -#### 部署客户端 +### 使用步骤 -1. 安装Python环境和第三方库 +#### 安装第三方库 - - 安装[Python3.7](https://docs.python.org/zh-cn/3.7/using/unix.html)、[pip3](https://pip.pypa.io/en/stable/installation/) - > 通过``python3 --version``和``pip3 --version``检查是否正确配置环境。 - - 在本地源码目录下安装依赖 - ```bash - pip3 install -r client/requirements/app_reqs.pip - ``` +```bash +# 源码根目录下执行 +pip3 install -r client/requirements/app_reqs.pip +``` -2. 安装第三方工具 +#### 安装第三方工具 - - 进入到`client/requirements`目录 - - 在命令行中执行安装脚本 - ```bash - #Linux/macOS环境 - ./install.sh - #Windows环境 - ./install.bat - ``` +```bash +# 源码根目录 +cd client/requirements + +# 执行安装脚本 +# Linux/macOS环境 +./install.sh +# Windows环境 +./install.bat +``` #### 配置客户端 -- 配置client/config.ini文件 -将``替换成实际的serve ip(可包含端口号)。 - +- 配置 `client/config.ini` 文件 + + 将 `` 替换成实际的serve ip(可包含端口号)。 + + ![客户端执行环境配置](https://tencent.github.io/CodeAnalysis/media/clientConfigIni.png) + +- 配置 `client/codedog.ini` 文件 -- 配置client/codedog.ini文件 - - 填写以下必填项:`token`,`org_sid`,`team_name`,`source_dir` - - `token`: 从tca页面获取,前往[个人中心]-[个人令牌]-复制Token - ![personalToken](../../images/personalToken.png) - - `org_sid`(团队编号),`team_name`(项目名称): 从tca项目概览页面URL中获取,项目概览URL格式:http://{域名}/t/{org_sid}/p/{team_name}/profile - ![orgsid](../../images/orgsid.png) - - `source_dir`: 本地代码目录路径 -- 按需填写其他可选项,也可以不填,按默认配置执行 + 必填项:`token`、`org_sid`、`team_name`、`source_dir` + - **个人令牌** - `token`:从 TCA 页面获取,前往[个人中心]-[个人令牌]-复制Token + + ![personalToken](../../images/personalToken.png) + + - **团队编号** - `org_sid`:进入 TCA 项目概览页,从 URL 中获取 + + - **项目名称** - `team_name`::进入 TCA 项目概览页,从 URL 中获取 + + :::tip + 项目概览URL格式:`http://{域名}/t/{org_sid}/p/{team_name}/profile` + ::: + + - **分析路径** - `source_dir`: 本地代码目录路径 + + :::tip + - 如果项目代码为编译型语言(比如:C/C++,C#,Go,Java,Kotlin,Objective-C等),且使用的分析方案中配置了编译型工具(如图,使用了OC推荐规则包),需要填写`build_cmd`编译命令。 + + - 其他可选项按需填写,不填写时按默认配置执行 + ::: + #### 启动客户端 -进入到`client`目录下,执行客户端脚本 ```bash +# 源码根目录 +cd client + +# 执行客户端脚本 python3 codepuppy.py localscan ``` -> 使用`localscan`命令启动本地单次的代码分析,如需启动分布式并行分析任务,请参考[常驻节点分析](../guide/%E5%AE%A2%E6%88%B7%E7%AB%AF/%E5%B8%B8%E9%A9%BB%E8%8A%82%E7%82%B9%E5%88%86%E6%9E%90.md)进行配置。 + +:::warning +Client 的实现及启动脚本均依赖 Python3 版本为 3.7,可执行 ``python3 --version`` 查看版本。若版本有误,可安装版本为3.7的python并软链接到python3命令。 +::: + +:::tip + +- `codedog.ini` 各项参数可由命令行传入,获取详细参数说明可运行 `python3 codepuppy.py localscan -h` + +- 使用`localscan`命令启动本地单次的代码分析,如需启动分布式并行分析任务,请参考[使用分布式节点模式](../client/README.md#五使用分布式节点模式执行客户端)进行配置。 +::: ## 通过Docker-Compose -> 适用于快速上手体验。使用docker运行,可以免去客户端环境依赖的安装,避免环境兼容性问题。 -> 但是由于环境受限于docker,会无法复用本地的编译环境,部分需要编译的工具无法使用。 +:::tip +适用于快速上手体验。使用docker运行,可以免去客户端环境依赖的安装,避免环境兼容性问题。 +但是由于环境受限于docker,会无法复用本地的编译环境,部分需要编译的工具无法使用。 +::: -### 部署步骤 +### 使用步骤 #### 配置客户端 -- 配置client/config.ini文件 -将``替换成实际的serve ip(可包含端口号)。 - +- 配置 `client/config.ini` 文件 + +- 配置 `client/codedog.ini` 文件 -- 配置client/codedog.ini文件 - - 填写以下必填项:`token`,`org_sid`,`team_name`,`source_dir` - - `token`: 从tca页面获取,前往[个人中心]-[个人令牌]-复制Token - ![personalToken](../../images/personalToken.png) - - `org_sid`(团队编号),`team_name`(项目名称): 从tca项目概览页面URL中获取,项目概览URL格式:http://{域名}/t/{org_sid}/p/{team_name}/profile - ![orgsid](../../images/orgsid.png) - - `source_dir`: 本地代码目录路径 -- 按需填写其他可选项,也可以不填,按默认配置执行 +:::tip +同通过源代码使用-[配置客户端](./deployClient.md#配置客户端) +::: -#### 构建客户端镜像 +#### 构建镜像 1. 安装Docker,安装教程:[官方文档](https://docs.docker.com/engine/install/) + 2. 安装Docker-Compose,安装教程:[官方文档](https://docs.docker.com/compose/install/) + 3. 进入`client`目录,构建docker镜像 ```bash @@ -94,7 +123,8 @@ docker build -t tca-client . ##### 方案一:直接使用docker运行 -1. 进入`client`目录,执行以下命令 +进入`client`目录,执行以下命令 + ```bash # 按照实际情况填写`SOURCE_DIR`环境变量值 export SOURCE_DIR=需要扫描的代码目录绝对路径 @@ -104,26 +134,28 @@ docker run -it --rm -v $PWD:/workspace/client -v $SOURCE_DIR:/workspace/src -- ##### 方案二:使用docker内bash终端运行 1. 进入docker容器内的bash终端 -```bash -# 按照实际情况填写`SOURCE_DIR`环境变量值 -export SOURCE_DIR=需要扫描的代码目录绝对路径 -docker run -it --rm -v $PWD:/workspace/client -v $SOURCE_DIR:/workspace/src --name tca-client tca-client bash -``` + + ```bash + # 按照实际情况填写`SOURCE_DIR`环境变量值 + export SOURCE_DIR=需要扫描的代码目录绝对路径 + docker run -it --rm -v $PWD:/workspace/client -v $SOURCE_DIR:/workspace/src --name tca-client tca-client bash + ``` + 2. 通过命令行启动client代码 -```bash -python3 codepuppy.py localscan -``` + + ```bash + python3 codepuppy.py localscan + ``` ## 通过可执行文件 ### 依赖环境 -- 系统要求 - - - Linux,Windows或macOS +- 系统环境 + - Linux,Windows或macOS -### 部署步骤 +### 使用步骤 #### 下载客户端 @@ -133,23 +165,18 @@ python3 codepuppy.py localscan #### 配置客户端 -- 配置client/config.ini文件 -将``替换成实际的serve ip(可包含端口号)。 - +- 配置 `client/config.ini` 文件 -- 配置client/codedog.ini文件 - - 填写以下必填项:`token`,`org_sid`,`team_name`,`source_dir` - - `token`: 从tca页面获取,前往[个人中心]-[个人令牌]-复制Token - ![personalToken](../../images/personalToken.png) - - `org_sid`(团队编号),`team_name`(项目名称): 从tca项目概览页面URL中获取,项目概览URL格式:http://{域名}/t/{org_sid}/p/{team_name}/profile - ![orgsid](../../images/orgsid.png) - - `source_dir`: 本地代码目录路径 -- 按需填写其他可选项,也可以不填,按默认配置执行 +- 配置 `client/codedog.ini` 文件 + +:::tip +同通过源代码使用-[配置客户端](./deployClient.md#配置客户端) +::: #### 启动客户端 进入到`client`目录下,执行客户端 + ```bash ./codepuppy localscan ``` -> 使用`localscan`命令启动本地单次的代码分析,如需启动分布式并行分析任务,请参考[常驻节点分析](../guide/%E5%AE%A2%E6%88%B7%E7%AB%AF/%E5%B8%B8%E9%A9%BB%E8%8A%82%E7%82%B9%E5%88%86%E6%9E%90.md)进行配置。 \ No newline at end of file diff --git a/web/packages/tca-document/zh/quickStarted/deploySever.md b/web/packages/tca-document/zh/quickStarted/deploySever.md index d1e8f2ec9..7ee1a6e54 100644 --- a/web/packages/tca-document/zh/quickStarted/deploySever.md +++ b/web/packages/tca-document/zh/quickStarted/deploySever.md @@ -1,137 +1,130 @@ -# 部署Server和Web +# 部署 TCA +TCA提供部署脚本,支持一键式快速部署Server、Web、Client。 +脚本共提供三种部署方式:Docker部署(推荐)、[Docker-Compose部署](./dockercomposeDeploy.md)、[源码部署](./codeDeploy.md),可根据您的具体使用场景任意选择其一进行部署。 -## 通过源代码 +## Docker快速部署 -### 依赖环境 - -- 系统要求 - - - Linux - - [Pythn 3.7](https://docs.python.org/zh-cn/3.7/using/unix.html) - - [MySQL 5.7.8](https://dev.mysql.com/doc/mysql-installation-excerpt/5.7/en/) 或更高版本 - - [Redis 4.0](https://redis.io/docs/getting-started/installation/install-redis-on-linux/) 或更高版本 - - [Nginx 1.20.2](https://nginx.org/en/docs/install.html) 或更高版本 - -- 硬件要求 - - - 2核4G内存 - - 100G可用硬盘存储空间 - -- 权限要求 +:::warning +仅适用于Docker部署体验,生产环境建议使用专业的 MySQL、Redis 等服务 +::: - - 安装Server依赖软件(python、nginx、yum软件包)需要使用ROOT权限(启动Server服务时可以使用非ROOT用户运行) - - 需要开放80端口的访问权限(80为TCA平台访问端口) - - Server服务执行数据库初始化需要依赖``CREATE、ALTER、INDEX、DELETE、LOCK TABLES、SELECT、INSERT、REFERENCES、UPDATE``权限 +### 依赖环境 +- 系统环境 + - Linux、macOS、Windows + - 最低配置:2核4G内存、100G硬盘存储空间 +- 环境准备 + - Docker +- 权限准备 + - 需要开放80、8000端口的访问权限(80为TCA平台访问端口,8000为TCA Server访问端口) -### 安装步骤 +### 部署对象 +Server、Web 与 Client -#### 部署Server +### 操作说明 +#### 首次启动操作 -1. 进入Server服务工作目录(例如 ``~/CodeAnalysis/server/``),以下路径均为目录内的相对路径 -2. 配置MySQL和Redis服务,初始化数据(MySQL版本运行版本:5.7) - - 填写数据库和Redis信息以及根据需要调整配置信息,主要的工程配置已提供默认值,字段说明详见[TCA Server](../references/parameters/server.md)。 +1. 进入CodeAnalysis工作目录(例如``~/CodeAnalysis``),以下路径均为目录内的相对路径 +2. 执行命令: ```bash - vi ./scripts/config.sh - ``` - - 初始化DB、安装依赖和运行初始化脚本 - ```bash - bash ./scripts/deploy.sh init - ``` - - 将安装好的``celery``与``gunicorn``可执行文件建立软链接到``/usr/local/bin``路径下 - ```bash - # /path/to/需要替换为celery可执行命令实际的路径,一般在python安装路径的bin目录下,如/usr/local/python3/bin/ - ln -s /path/to/celery /usr/local/bin/celery - # /path/to/需要替换为gunicorn可执行命令实际的路径,一般在python安装路径的bin目录下,如/usr/local/python3/bin/ - ln -s /path/to/gunicorn /usr/local/bin/gunicorn - ``` - - 使环境变量生效,避免出现unknown command错误 - ```bash - export PATH=/usr/local/bin:$PATH - ``` -3. 启动/停止服务 - ```bash - # 启动服务 - bash ./scripts/deploy.sh start - # 停止服务 - bash ./scripts/deploy.sh stop + bash ./quick_install.sh docker deploy ``` +::: tip +通过Docker部署默认会在当前根目录下的挂载三个路径: +- `.docker_temp/logs`:容器内的`/var/log/tca/`,存放TCA平台的日输出文件; +- `.docker_temp/data`:容器内的`/var/opt/tca/`, 存放TCA平台的服务数据,主要是Mariadb、Redis; +- `.docker_temp/configs`:容器内的``/etc/tca``,存放TCA平台的配置文件,主要是`config.sh` +::: -#### 部署Web +#### 更新操作 +1. 更新代码 +2. 执行以下命令: + - `TCA_IMAGE_BUILD=true ./quick_install.sh docker deploy`:重新构建并启动tca容器 +::: tip +`TCA_IMAGE_BUILD=true`表示从本地构建TCA镜像运行 +::: +#### 运行容器 +如果已经在机器上执行过``docker deploy``,并保留容器数据的,可以执行以下命令启动容器,继续运行TCA -1. 完成部署Server并启动服务,后端服务默认登陆账号/密码为:`CodeDog/admin`。 +```bash +bash ./quick_install.sh docker start +``` -2. 进入web服务部署目录(例如 ``~/CodeAnalysis/web/tca-deploy-source``),以下路径均为目录内的相对路径 +#### 停止容器 +如果容器正在运行,希望停止容器,可以运行 -3. 部署/更新前端服务 +```bash +bash ./quick_install.sh docker stop +``` - ```bash - # 部署、更新都使用此命令 - bash ./scripts/deploy.sh init -d - ``` +# 使用TCA +成功部署TCA后,请开始您的代码分析。 +## 进入平台页面 - 具体请查阅部署脚本内容,可根据业务调整配置。 +在浏览器输入`http://部署机器IP/`,点击立即体验,完成登录后即可跳转到团队列表页 -3. **额外说明** +:::tip +默认平台登录账号/密码:CodeDog/admin - `tca-deploy-source/scripts/config.sh` 已配置默认环境变量,用户可根据需要调整环境变量再部署前端服务,具体可查阅脚本内容。 +如部署过程中,已调整默认账号密码,请按照调整后的账号密码进行登录 +::: +## 创建团队及项目 -## 通过Docker-Compose +- 完成团队创建 -### 依赖环境 +- 完成项目创建 -- 系统要求 +## 登记代码库 - - Linux,Windows或macOS - - [Docker](https://docs.docker.com/engine/install/) - > Compose file format需要为3.0及以上,Docker版本要求可以参考[官方文档](https://docs.docker.com/compose/compose-file/compose-file-v3/#compose-and-docker-compatibility-matrix) - - [Docker-Compose 1.26](https://docs.docker.com/compose/install/) 或更高版本 +登记代码库,输入代码库地址以及凭证信息等,完成代码库登记。 -- 硬件要求 +![registerCodeRepo](../../images/registerCodeRepo.png) - - 2核4G内存 - - 100G可用硬盘存储空间 +## 创建分析项目 -- 权限要求 - - - 需要开放80、8000端口的访问权限(80为TCA平台访问端口,8000为TCA Server访问端口) +![开始分析](../../images/start_scan_02.png) -### 部署步骤 +::: tip +1. 用户可选择使用分析方案模板,或创建分析方案的方式,利用方案的分析配置进行代码分析。 +2. 点击确认时,平台会首先创建该代码库的分析方案,然后根据代码库分支、当前分析方案创建分支项目。 +::: -#### 方案一:一键部署 +### 分析方案说明 -拉取代码进入源码根目录,执行``./quick_start.sh``命令,即可自动安装Docker、Docker-Compose和启动Server与Web服务 +- 分析方案是用于对代码库进行分析的一套配置集合。 ->- ``quick_start.sh``脚本中会自动下载[Docker安装脚本](https://get.docker.com)、启动Docker服务、下载``docker-compose``可执行文件以及执行``compose_init.sh``脚本启动Server、Web服务 ->- 如果提示脚本没有执行权限,可以在源码执行命令:``chmod +x compose_init.sh quick_start.sh`` +- 更多分析方案配置可查阅[帮助文档-分析方案](../guide/分析方案/基础属性配置.md) +![creataAnalysePlan](../../images/creataAnalysePlan.png) -#### 方案二:手动部署 +::: tip +本次部署会默认启动运行环境为「Codedog_Linux」的客户端,若需扩展更多运行环境,详见客户端[常驻节点分析](../guide/客户端/常驻节点分析.md) +::: -1. 安装Docker,安装教程:[官方文档](https://docs.docker.com/engine/install/) -2. 安装Docker-Compose,安装教程:[官方文档](https://docs.docker.com/compose/install/) -3. 拉取代码并进入源码根目录后,执行 ``./compose_init.sh`` 命令,即可启动Server与Web服务 +![planPage](../../images/planPage.png) +## 执行代码分析 ->- 如果提示脚本没有执行权限,可以在源码执行命令:``chmod +x compose_init.sh`` ->- 首次启动会构建相关镜像,耗时会比较久 +初始化创建项目后,可通过 `在线分析` 或 `客户端分析` 来启动代码分析。 -``compose_init.sh``脚本会包含各个服务的初始化操作 +![代码分析](../../images/start_scan_06.png) -#### 启动/停止服务 +::: tip +- TCA推荐使用`在线分析`,您可根据具体使用场景选择其一。 +- `在线分析`表示配置代码库链接后,TCA客户端拉取代码后进行分析;`客户端分析`在配置本地待扫描代码路径后,无需代码拉取直接分析本地代码。 +- `在线分析`与`客户端分析`具体详情及配置参考[TCA客户端配置详情](../guide/客户端/配置详情.md) +::: -进入源码目录后,执行``docker-compose up -d``命令,即可启动Server与Web服务。执行``docker-compose stop``命令,即可停止Server与Web服务。 +## 查看分析历史 -### 常见问题 +分析结束后,数据会上报到服务端。可进入分析历史页面查看分析记录以及分析结果。 -- Q:如何查看服务启动的日志? +![分析历史](../../images/start_scan_05.png) - A:可以先找服务名称,执行``docker-compose logs -f xxx``,xxx即服务的名称,比如``main-server``、``main-worker``等 +## 查看分析概览 -- Q:TCA初始登录账号密码是什么? - - A:初始登录账号是``CodeDog``,密码是``admin``,如果想要自定义,在初始化前,可以在``server/dockerconfs/.env.local``对``TCA_DEFAULT_ADMIN``和``TCA_DEFAULT_PASSWORD``变量值进行调整。如果初始化完成后需要调整,则需要登录到平台的``用户管理``页面进行调整。 +分析结束后,进入分支概览可以查看该分支指定分析方案的概览数据以及 [问题列表](../guide/代码检查/分析结果查看.md) 等。 -**详细Q&A文档可以查阅[TCA使用常见问题](FAQ.md)** +![分支概览](../../images/start_scan_04.png) \ No newline at end of file diff --git a/web/packages/tca-document/zh/quickStarted/dockercomposeDeploy.md b/web/packages/tca-document/zh/quickStarted/dockercomposeDeploy.md new file mode 100644 index 000000000..e9062bb65 --- /dev/null +++ b/web/packages/tca-document/zh/quickStarted/dockercomposeDeploy.md @@ -0,0 +1,49 @@ +# Docker-Compose快速部署 +#### 部署对象 +Server、Web 与 Client + +:::warning +仅适用于Docker-Compose部署体验,生产环境建议使用专业的 MySQL、Redis 等服务 +兼容之前的部署方式 +::: + +#### 操作说明 +##### 首次启动操作 + +1. 进入CodeAnalysis工作目录(例如``~/CodeAnalysis``),以下路径均为目录内的相对路径 +2. 执行命令: + - `bash ./quick_install.sh docker-compose deploy`:启动tca_server容器 + +注意:通过Docker-Compose部署默认会在当前根目录下的挂载三个路径: + +- `.docker_data/logs`:存放TCA平台的各个服务日志输出目录; +- `.docker_data/mysql`:存放TCA平台的MySQL数据 +- `.docker_data/redis`:存放TCA平台的Redis数据 +- `.docker_data/filedata`:存放TCA平台文件服务器的文件 + +##### 更新操作 +1. 更新代码 +2. 执行以下命令: + - `bash ./quick_install.sh docker-compose build`:重新构建TCA相关镜像 + - `bash ./quick_install.sh docker-compose deploy`: 重新部署TCA相关容器与初始化(或刷新数据) + +##### 运行操作 +如果已经在机器上执行过``docker-compose deploy``,并保留容器数据的,可以执行以下命令启动容器,继续运行TCA + +```bash +bash ./quick_install.sh docker-compose start +``` + +##### 停止操作 +如果容器正在运行,希望停止容器,可以执行以下命令 + +```bash +bash ./quick_install.sh docker-compose stop +``` + +##### 构建镜像操作 +如果希望构建镜像,可以执行以下命令 + +``` +bash ./quick_install.sh docker-compose build +``` \ No newline at end of file diff --git a/web/packages/tca-document/zh/quickStarted/initRepo.md b/web/packages/tca-document/zh/quickStarted/initRepo.md new file mode 100644 index 000000000..f96467f0f --- /dev/null +++ b/web/packages/tca-document/zh/quickStarted/initRepo.md @@ -0,0 +1,45 @@ +# 创建代码分析项目 + +成功部署并启动 Server 与 Web 服务后,通过以下步骤创建您的第一个代码分析项目。 + +## 进入平台页面 + +在浏览器输入`http://部署机器IP/`,点击立即体验,完成登录后即可跳转到团队列表页 + +:::tip +默认平台登录账号/密码:CodeDog/admin + +如部署过程中,已调整默认账号密码,请按照调整后的账号密码进行登录 +::: + +## 创建团队及项目 + +- 完成团队创建 + +- 完成项目创建 + +## 登记代码库 + +登记代码库,输入代码库地址以及凭证信息等,完成代码库登记。 + +![registerCodeRepo](../../images/registerCodeRepo.png) + +## 创建分析项目 + +![开始分析](../../images/start_scan_02.png) + +::: tip + +1. 用户可选择使用分析方案模板,或创建分析方案的方式,利用方案的分析配置进行代码分析。 +2. 点击确认时,平台会首先创建该代码库的分析方案,然后根据代码库分支、当前分析方案创建分支项目。 +::: + +### 分析方案说明 + +- 分析方案是用于对代码库进行分析的一套配置集合。 + +- 更多分析方案配置可查阅[帮助文档-分析方案](../guide/分析方案/基础属性配置.md) + +![creataAnalysePlan](../../images/creataAnalysePlan.png) + +![planPage](../../images/planPage.png) diff --git a/web/packages/tca-document/zh/quickStarted/intro.md b/web/packages/tca-document/zh/quickStarted/intro.md index 13a951a3d..055ce7737 100644 --- a/web/packages/tca-document/zh/quickStarted/intro.md +++ b/web/packages/tca-document/zh/quickStarted/intro.md @@ -1,34 +1,36 @@ -# 腾讯云代码分析 +# 平台概述 -**腾讯云代码分析**(**Code Analysis, TCA**)支持Linux、Windows和macOS等多种平台。通过以下步骤,您可以将腾讯云代码分析部署到本地,快速启动并运行您的代码分析项目。 +**腾讯云代码分析**(Tencent Cloud Code Analysis,简称TCA,内部曾用研发代号 **CodeDog** )是集众多分析工具的云原生、分布式、高性能的代码综合分析跟踪平台,包含服务端、Web端和客户端三个组件,已集成一批自研工具,同时也支持动态集成业界各编程语言的分析工具。 -> 如果您在部署或使用腾讯云代码分析的过程中遇到了问题,可以参考[常见问题](FAQ.md)。 +### 使用TCA Action快速体验 +使用TCA Action,只需要在代码仓库中添加`.github/workflows/tca.yml`文件,就可以直接在GitHub工作流中快速体验代码分析。请参考:[TCA-action指引](https://github.com/TCATools/TCA-action/blob/main/README.md) -## 部署Server和Web +### 部署TCA -拉取[代码库](https://github.com/Tencent/CodeAnalysis)后,您可以通过以下两种方式部署腾讯云代码分析的Server和Web服务: +拉取 [代码库](https://github.com/Tencent/CodeAnalysis) 后,您可以通过以下三种方式部署腾讯云代码分析平台: -- [通过源代码](deploySever.html#通过源代码) +- [通过 Docker 部署](./deploySever.md#通过docker) -- [通过Docker-Compose](deploySever.md#通过docker-compose) +- [通过源代码](./codeDeploy.md#通过源代码) -## 创建首个代码分析项目 +- [通过 Docker-Compose 部署](./dockercomposeDeploy.md#通过docker-compose) -成功部署并启动Server与Web服务后,您可以使用管理员凭据登陆到腾讯云代码分析平台,按照[指引](setup.md)创建您的首个代码分析项目。 +### 创建首个代码分析项目 -## 部署与配置客户端 +成功部署并启动TCA后,您可以按照 [指引](./deploySever.md) 创建您的首个代码分析项目。 -在启动您的首个代码分析项目前,您需要在本地部署腾讯云代码分析的客户端。完成客户端的项目配置后,即可启动您的首个代码分析项目,并在腾讯云代码分析平台上查看您的分析结果。 +:::tip +默认平台登录账号/密码:CodeDog/admin +::: -您可以通过以下三种方式部署并使用腾讯云代码分析的客户端: +### 快速扩展客户端 -- [通过源代码](deployClient.md#通过源代码) +TCA客户端支持通过可执行文件进行快速扩展部署,详见[通过可执行文件](./deployClient.md#通过可执行文件) -- [通过Docker-Compose](deployClient.md#通过docker-compose) +:::tip +客户端可在本地执行代码分析,也可以作为[在线常驻节点](../advanced/任务分布式执行.md)进行在线分析。 +::: -- [通过可执行文件](deployClient.md#通过可执行文件) +### 了解更多 - -## 了解更多 - -更多关于腾讯云代码分析平台的使用指南和配置说明,参见[帮助文档](../guide/README.md)。 \ No newline at end of file +更多关于腾讯云代码分析平台的使用指南和配置说明,参见[帮助文档](../guide/README.md)。 diff --git a/web/packages/tca-document/zh/quickStarted/references/install_mysql_on_centos.md b/web/packages/tca-document/zh/quickStarted/references/install_mysql_on_centos.md new file mode 100644 index 000000000..0337b5cfc --- /dev/null +++ b/web/packages/tca-document/zh/quickStarted/references/install_mysql_on_centos.md @@ -0,0 +1,102 @@ +# 在 CentOS 安装 MySQL + +## 注意 + +本文档仅供参考,不适用于正式环境部署,正式环境建议使用专业的MySQL服务(比如[腾讯云的MySQL产品](https://cloud.tencent.com/product/cdb)) + +## 环境 + +CentOS 7.3 版本 + +## 安装 mysql yum源 + +```bash +wget https://repo.mysql.com//mysql57-community-release-el7-11.noarch.rpm +yum localinstall mysql57-community-release-el7-11.noarch.rpm +``` + +安装成功后,查看MySQL版本: + +```bash +yum repolist all | grep mysql +``` + +输出结果: + +```bash +mysql-cluster-7.5-community/x86_64 MySQL Cluster 7.5 Community 禁用 +mysql-cluster-7.5-community-source MySQL Cluster 7.5 Community - 禁用 +mysql-cluster-7.6-community/x86_64 MySQL Cluster 7.6 Community 禁用 +mysql-cluster-7.6-community-source MySQL Cluster 7.6 Community - 禁用 +!mysql-connectors-community/x86_64 MySQL Connectors Community 启用: 221 +mysql-connectors-community-source MySQL Connectors Community - S 禁用 +!mysql-tools-community/x86_64 MySQL Tools Community 启用: 135 +mysql-tools-community-source MySQL Tools Community - Source 禁用 +mysql-tools-preview/x86_64 MySQL Tools Preview 禁用 +mysql-tools-preview-source MySQL Tools Preview - Source 禁用 +mysql55-community/x86_64 MySQL 5.5 Community Server 禁用 +mysql55-community-source MySQL 5.5 Community Server - S 禁用 +mysql56-community/x86_64 MySQL 5.6 Community Server 禁用 +mysql56-community-source MySQL 5.6 Community Server - S 禁用 +!mysql57-community/x86_64 MySQL 5.7 Community Server 启用: 544 +mysql57-community-source MySQL 5.7 Community Server - S 禁用 +mysql80-community/x86_64 MySQL 8.0 Community Server 禁用 +mysql80-community-source MySQL 8.0 Community Server - S 禁用 +``` + +## 安装MySQL + +```bash +yum install mysql-community-server +``` + +>1.如遇以下报错,可尝试运行`yum install mysql-community-server --nogpgcheck`安装 +> Public key for mysql-community-libs-compat-5.7.37-1.el7.x86_64.rpm is not installed +> Failing package is: mysql-community-libs-compat-5.7.37-1.el7.x86_64 +> GPG Keys are configured as: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql +>2.如遇以下报错,可执行`yum module disable mysql`后重试安装 +>All matches were filtered out by modular filtering for argument: mysql-community-serve +>Error: Unable to find a match: mysql-community-server + +## 配置MySQL服务 + +安装好的MySQL配置文件路径是``/etc/my.cnf``,这里可以根据需要调整,比如可以调整: + +- datadir:MySQL存放数据的路径,如果有额外挂载磁盘,可以考虑指向相关路径 + +## 启动MySQL服务 + +```bash +systemctl start mysqld +``` + +确认MySQL正常启动 + +```bash +systemctl status mysqld +``` + +查看生成 MySQL root用户临时密码: + +```bash +grep 'temporary password' /var/log/mysqld.log +``` + +### 修改root用户密码 + +连接MySQL服务 + +```bash +$ mysql -uroot -p +# 输出上述查询到的临时密码 +``` + +修改root用户的密码(下面是改成 ``Password@2021``,这里根据自行需要进行调整): + +```SQL +ALTER USER 'root'@'localhost' IDENTIFIED BY 'Password@2021'; +``` + +## 参考文档 + +- [Installing MySQL on Linux Using the MySQL Yum Repository](https://dev.mysql.com/doc/refman/5.7/en/linux-installation-yum-repo.html) diff --git a/web/packages/tca-document/zh/quickStarted/references/install_nginx_from_source.md b/web/packages/tca-document/zh/quickStarted/references/install_nginx_from_source.md new file mode 100644 index 000000000..ca924bf4a --- /dev/null +++ b/web/packages/tca-document/zh/quickStarted/references/install_nginx_from_source.md @@ -0,0 +1,136 @@ +# 源码安装 Nginx + +## 运行环境 + +1. 基于x86_64的CentOS +2. 基于aarch64(鲲鹏920)的UOS V20 +3. 基于aarch64(飞腾2000)的TencentOS Server + +## 环境准备 + +安装编译打包需要的工具 + +```bash +yum -y install gcc zlib-devel pcre-devel bzip2-devel openssl-devel readline-devel +``` +> Ubuntu: ``apt install gcc libssl-dev zlib1g-dev libpcre3-dev libbz2-dev libreadline-dev`` + +## 下载源码 + +```bash +wget http://nginx.org/download/nginx-1.20.2.tar.gz +``` + +## 解压安装 + +```bash +# 解压 +$ tar zvxf nginx-1.20.2.tar.gz -C /usr/local/src + +# 进入源码目录 +$ cd /usr/local/src/nginx-1.20.2 + +# 配置 +$ ./configure \ +--sbin-path=/usr/local/nginx/nginx \ +--conf-path=/etc/nginx/nginx.conf \ +--pid-path=/run/nginx.pid \ +--with-stream \ +--with-http_ssl_module --with-http_v2_module --with-http_auth_request_module + +# 构建nginx +$ make -j4 +$ make install +$ make clean + +# 建立软链 +$ ln -s /usr/local/nginx/nginx /usr/local/bin/nginx +``` + +## 添加nginx配置文件 + +```bash +mkdir /etc/nginx/conf.d/ +vi /etc/nginx/nginx.conf +``` + +检查``nginx.conf``配置文件: + +1. 检查``pid /run/nginx.pid``,如果缺失或被注释则加上,加上位置如下所示: +2. 检查是否缺失这一行``include conf.d/*.conf;``,如果缺失则加上,加上位置如下所示: + +```bash +# ...省略内容 +#pid logs/nginx.pid; # 默认有的 +pid /run/nginx.pid; + +events { + # ...省略内容 +} + +# ...省略内容 + +http { + # ...省略内容 + # + include conf.d/*.conf; + server { + # ...省略内容 + } +} + +``` + +后续可以将nginx配置文件放置到``/etc/nginx/conf.d/``目录下 + + + +## 配置开机自动启动 + +```bash +vim /usr/lib/systemd/system/nginx.service +``` + +输入以下内容: + +```bash +[Unit] +Description=The nginx HTTP and reverse proxy server +After=network-online.target remote-fs.target nss-lookup.target +Wants=network-online.target + +[Service] +Type=forking +PIDFile=/run/nginx.pid +# Nginx will fail to start if /run/nginx.pid already exists but has the wrong +# SELinux context. This might happen when running `nginx -t` from the cmdline. +# https://bugzilla.redhat.com/show_bug.cgi?id=1268621 +ExecStartPre=/bin/rm -f /run/nginx.pid +ExecStartPre=/usr/local/bin/nginx -t +ExecStart=/usr/local/bin/nginx +ExecReload=/usr/local/bin/nginx -s reload +ExecStop=/usr/local/bin/nginx -s stop +KillSignal=SIGQUIT +TimeoutStopSec=5 +KillMode=process +PrivateTmp=true + +[Install] +WantedBy=multi-user.target +``` + +启动nginx: + +```bash +systemctl start nginx +``` + +开机自动启动nginx: + +```bash +systemctl enable nginx +``` + +## 参考文档 + +- [Nginx官方文档](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/#downloading-the-sources) diff --git a/web/packages/tca-document/zh/quickStarted/references/install_python37_on_centos.md b/web/packages/tca-document/zh/quickStarted/references/install_python37_on_centos.md new file mode 100644 index 000000000..ac782a828 --- /dev/null +++ b/web/packages/tca-document/zh/quickStarted/references/install_python37_on_centos.md @@ -0,0 +1,109 @@ +# 在 CentOS 安装 Python3.7 + +## 下载Python源码包 + +```bash +wget https://www.python.org/ftp/python/3.7.12/Python-3.7.12.tgz +``` + +## 安装前准备 + +安装依赖组件 + +```bash +yum -y install wget zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make libffi-devel xz-devel +``` + +## 解压安装 + +```bash +# 解压到/usr/local/src目录 +$ tar zvxf Python-3.7.12.tgz -C /usr/local/src +$ cd /usr/local/src/Python-3.7.12 +# 编译前配置 +$ ./configure prefix=/usr/local/python3 --enable-shared +# 编译构建 +$ make -j8 +# 安装Python +$ make install +# 清理编译产出的中间文件 +$ make clean +# 链接构建产出的Python可执行文件到/usr/local/bin目录 +$ ln -s /usr/local/python3/bin/python3 /usr/local/bin/python +# 链接构建产出的pip3可执行文件到/usr/local/bin目录 +$ ln -s /usr/local/python3/bin/pip3 /usr/local/bin/pip +# 链接构建产出的Python动态库 +$ ln -s /usr/local/python3/lib/libpython3.7m.so.1.0 /usr/lib/libpython3.7m.so.1.0 +# 配置动态库 +$ ldconfig +``` + +## 检查 + +检查Python版本是否安装成功 + +```bash +$ python --version +Python 3.7.12 # 正常输出,表示安装成功 +``` + +注: + +1. 链接到/usr/local/bin/目录不会影响系统软件(比如yum)的使用,因为 yum 工具指定的Python路径是``/usr/bin/python`` +2. 一般情况下,PATH配置是先``/usr/local/bin``再``/usr/bin`` +3. 检查``python -v``输出结果是否为``Python 3.7.12``版本,如果不是该版本,可能影响后续依赖安装和服务运行 + +## pypi下载源配置 + +pip默认是到``pypi``官方源下载第三方依赖包,下载速度可能会比较慢,可以考虑调整为腾讯云的``pypi``下载源,调整方式: + +```bash +mkdir ~/.pip/ +echo "extra-index-url = https://mirrors.cloud.tencent.com/pypi/simple" >> ~/.pip/pip.conf +``` + +## 一键安装脚本 +以下脚本内容是上面的步骤集合,省去了复制粘贴的重复动作。 +1. 创建文件 `install_py37.sh`,写入以下 shell 脚本 +2. 赋予执行权限,`chmox +x install_py37.sh` +3. 执行脚本,`./install_py37.sh` + +```bash +#!/bin/env bash + +## 下载 Python 源码,如果已下载源码在脚本当前目录下,可注释跳过下载步骤 +wget https://www.python.org/ftp/python/3.7.12/Python-3.7.12.tgz + +## 安装编译依赖组件 +yum -y install wget zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make libffi-devel xz-devel + +## 解压安装 +# 解压到/usr/local/src目录 +tar zvxf Python-3.7.12.tgz -C /usr/local/src +cd /usr/local/src/Python-3.7.12 +# 编译前配置 +./configure prefix=/usr/local/python3 --enable-shared +# 编译构建 +make -j8 +# 安装Python +make install +# 清理编译产出的中间文件 +make clean +# 链接构建产出的Python可执行文件到/usr/local/bin目录 +ln -s /usr/local/python3/bin/python3 /usr/local/bin/python +# 链接构建产出的pip3可执行文件到/usr/local/bin目录 +ln -s /usr/local/python3/bin/pip3 /usr/local/bin/pip +# 链接构建产出的Python动态库 +ln -s /usr/local/python3/lib/libpython3.7m.so.1.0 /usr/lib/libpython3.7m.so.1.0 +# 配置动态库 +ldconfig + +## 检查Python版本是否安装成功 +echo -e "\033[1;42;37m[$(date "+%Y/%m/%d %H:%M:%S")] [Check]: 检查Python版本\033[0m" +python --version +echo -e "\033[1;42;37m[$(date "+%Y/%m/%d %H:%M:%S")] [Check]: 检查Python版本\033[0m" + +## pypi下载源配置 +mkdir ~/.pip/ +echo "extra-index-url = https://mirrors.cloud.tencent.com/pypi/simple" >> ~/.pip/pip.conf +``` diff --git a/web/packages/tca-document/zh/quickStarted/references/install_python37_on_ubuntu.md b/web/packages/tca-document/zh/quickStarted/references/install_python37_on_ubuntu.md new file mode 100644 index 000000000..14459aaf1 --- /dev/null +++ b/web/packages/tca-document/zh/quickStarted/references/install_python37_on_ubuntu.md @@ -0,0 +1,66 @@ +# 在 Ubuntu 安装 Python3.7 + +> 注:当前Ubuntu版本为18.04 + +## 下载Python源码包 + +```bash +wget https://www.python.org/ftp/python/3.7.12/Python-3.7.12.tgz +``` + +## 安装前准备 + +安装依赖组件 + +```bash +apt-get update +apt-get install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libsqlite3-dev libreadline-dev libffi-dev wget libbz2-dev tk-dev gcc make +``` + +## 解压安装 + +```bash +# 解压到/usr/local/src目录 +$ tar zvxf Python-3.7.12.tgz -C /usr/local/src +$ cd /usr/local/src/Python-3.7.12 +# 编译前配置 +$ ./configure prefix=/usr/local/python3 --enable-shared +# 编译构建 +$ make -j8 +# 安装Python +$ make install +# 清理编译产出的中间文件 +$ make clean +# 链接构建产出的Python可执行文件到/usr/local/bin目录 +$ ln -s /usr/local/python3/bin/python3 /usr/local/bin/python +# 链接构建产出的pip3可执行文件到/usr/local/bin目录 +$ ln -s /usr/local/python3/bin/pip3 /usr/local/bin/pip +# 链接构建产出的Python动态库 +$ ln -s /usr/local/python3/lib/libpython3.7m.so.1.0 /usr/lib/libpython3.7m.so.1.0 +# 配置动态库 +$ ldconfig +``` + +## 检查 + +检查Python版本是否安装成功 + +```bash +$ python --version +Python 3.7.12 # 正常输出,表示安装成功 +``` + +注: + +1. 链接到/usr/local/bin/目录不会影响系统软件 +2. 一般情况下,PATH配置是先``/usr/local/bin``再``/usr/bin`` +3. 检查``python -v``输出结果是否为``Python 3.7.12``版本,如果不是该版本,可能影响后续依赖安装和服务运行 + +## pypi下载源配置 + +pip默认是到``pypi``官方源下载第三方依赖包,下载速度可能会比较慢,可以考虑调整为腾讯云的``pypi``下载源,调整方式: + +```bash +mkdir ~/.pip/ +echo "[global]\nindex-url = https://mirrors.cloud.tencent.com/pypi/simple" >> ~/.pip/pip.conf +``` diff --git a/web/packages/tca-document/zh/quickStarted/references/install_redis_from_source.md b/web/packages/tca-document/zh/quickStarted/references/install_redis_from_source.md new file mode 100644 index 000000000..3ad90db98 --- /dev/null +++ b/web/packages/tca-document/zh/quickStarted/references/install_redis_from_source.md @@ -0,0 +1,107 @@ +# 源码安装 Redis + +## 运行环境 + +1. 基于x86_64的CentOS +2. 基于鲲鹏920(aarch64)的UOS V20 +3. 基于飞腾2000(aarch64)的TencentOS Server + +## 环境准备 + +安装编译打包需要的工具 + +```bash +yum install -y gcc make tcl wget +``` + +## 下载源码 + +```bash +wget http://download.redis.io/releases/redis-5.0.4.tar.gz +``` + +## 编译安装 + +```bash +# 解压 +$ tar zvxf redis-5.0.4.tar.gz -C /usr/local/src + +# 进入源码目录 +$ cd /usr/local/src/redis-5.0.4 + +# 构建redis依赖库 +$ cd deps; make -j4 hiredis jemalloc linenoise lua +$ cd .. + +# 构建redis +$ make -j4 +$ make install +$ make clean +``` + +安装后,可以在``/usr/local/src/redis-5.0.4/src``目录和``/usr/local/bin/``目录下找到``redis-server``与``redis-cli``两个文件 + +## 调整配置 + +```bash +cp /usr/local/src/redis/redis.conf /etc/redis.conf +vim /usr/local/src/redis/redis.conf +``` + +```bash +# 设置Redis密码 +requirepass 123456 + +# 将 daemonize no 调整为 daemonize yes,将redis-server调整为默认后台启动 +daemonize yes + +# 配置日志 +logfile '/var/log/redis/redis-server.log' +``` + +## 启动服务 + +```bash +redis-server /etc/redis.conf +``` + +## 配置开机自动启动 + +```bash +vim /etc/systemd/system/redis.service +``` + +输入以下内容: + +```service +[Unit] +Description=redis-server +After=network.target + +[Service] +Type=forking +ExecStart=/usr/local/bin/redis-server /etc/redis.conf +ExecStop=/usr/local/bin/redis-cli shutdown +Restart=always + +PrivateTmp=true + +[Install] +WantedBy=multi-user.target +``` + +启动redis-server: + +```bash +systemctl start redis +``` + +开机自动启动redis: + +```bash +systemctl enable redis +``` + +## 参考文档 + +- [Redis官方文档](https://redis.io/topics/quickstart) diff --git a/web/packages/tca-document/zh/quickStarted/references/install_redis_on_centos.md b/web/packages/tca-document/zh/quickStarted/references/install_redis_on_centos.md new file mode 100644 index 000000000..1a10cf9be --- /dev/null +++ b/web/packages/tca-document/zh/quickStarted/references/install_redis_on_centos.md @@ -0,0 +1,48 @@ +# 在 CentOS 安装 Redis + +## 注意 + +本文档仅供参考,不适用于正式环境部署,正式环境建议使用专业的Redis服务(比如[腾讯云的Redis产品](https://cloud.tencent.com/product/crs)) + +## 环境 + +CentOS 7.3 版本 + +## yum 安装 redis + +```bash +yum install redis +``` + +注:安装redis可能会出现"no package redis available"的错误提示,请执行``yum install epel-release``后重试redis安装命令。 + +## 修改redis密码 + +```bash +$ vi /etc/redis.conf + +# 找到 requirepass foobared +# 复制一行并根据自己需要调整密码,比如 +requirepass tca123 +``` + +## 启动redis + +```bash +systemctl start redis +``` + +查看redis运行状态 + +```bash +systemctl status redis +``` + +## 访问redis + +```bash +$ redis-cli + +127.0.0.1:6379> auth tca123 +OK # 鉴权通过 +``` diff --git a/web/packages/tca-document/zh/quickStarted/runProject.md b/web/packages/tca-document/zh/quickStarted/runProject.md new file mode 100644 index 000000000..3fa362da5 --- /dev/null +++ b/web/packages/tca-document/zh/quickStarted/runProject.md @@ -0,0 +1,26 @@ +# 启动代码分析 + +在完成 Server、Web 和 Client 相关部署和配置后,可通过平台执行代码分析。 + +## 执行代码分析 + +初始化创建项目后,可通过 `在线分析` 或 `客户端分析` 来启动代码分析。 + +![代码分析](../../images/start_scan_06.png) + +注: +- TCA推荐使用`在线分析`,您可根据具体使用场景选择其一。 +- `在线分析`表示配置代码库链接后,TCA客户端拉取代码后进行分析;`客户端分析`在配置本地待扫描代码路径后,无需代码拉取直接分析本地代码。 +- `在线分析`与`客户端分析`具体详情及配置参考[TCA客户端配置详情](../guide/客户端/客户端配置详情.md) + +## 查看分析历史 + +分析结束后,数据会上报到服务端。可进入分析历史页面查看分析记录以及分析结果。 + +![分析历史](../../images/start_scan_05.png) + +## 查看分析概览 + +分析结束后,进入分支概览可以查看该分支指定分析方案的概览数据以及 [问题列表](../guide/代码检查/分析结果查看.md) 等。 + +![分支概览](../../images/start_scan_04.png) diff --git a/web/packages/tca-document/zh/quickStarted/server.md b/web/packages/tca-document/zh/quickStarted/server.md deleted file mode 100644 index 867f39148..000000000 --- a/web/packages/tca-document/zh/quickStarted/server.md +++ /dev/null @@ -1,76 +0,0 @@ -# 源代码安装服务端 - -## 依赖环境 - -- 系统要求 - - - Linux - - [Pythn 3.7](https://docs.python.org/zh-cn/3.7/using/unix.html) - - [MySQL 5.7.8](https://dev.mysql.com/doc/mysql-installation-excerpt/5.7/en/) 或更高版本 - - [Redis 4.0](https://redis.io/docs/getting-started/installation/install-redis-on-linux/) 或更高版本 - - [Nginx 1.20.2](https://nginx.org/en/docs/install.html) 或更高版本 - -- 硬件要求 - - - 2核4G内存 - - 100G可用硬盘存储空间 - -- 权限要求 - - - 安装Server依赖软件(python、nginx、yum软件包)需要使用ROOT权限(启动Server服务时可以使用非ROOT用户运行) - - 需要开放80端口的访问权限(80为TCA平台访问端口) - - Server服务执行数据库初始化需要依赖``CREATE、ALTER、INDEX、DELETE、LOCK TABLES、SELECT、INSERT、REFERENCES、UPDATE``权限 - - -## 安装步骤 - -### 部署Server - -1. 进入Server服务工作目录(例如 ``~/CodeAnalysis/server/``),以下路径均为目录内的相对路径 -2. 配置MySQL和Redis服务,初始化数据(MySQL版本运行版本:5.7) - - 填写数据库和Redis信息以及根据需要调整配置信息,主要的工程配置已提供默认值,字段说明详见[TCA Server](../references/parameters/server.md)。 - ```bash - vi ./scripts/config.sh - ``` - - 初始化DB、安装依赖和运行初始化脚本 - ```bash - bash ./scripts/deploy.sh init - ``` - - 将安装好的``celery``与``gunicorn``可执行文件建立软链接到``/usr/local/bin``路径下 - ```bash - # /path/to/需要替换为celery可执行命令实际的路径,一般在python安装路径的bin目录下,如/usr/local/python3/bin/ - ln -s /path/to/celery /usr/local/bin/celery - # /path/to/需要替换为gunicorn可执行命令实际的路径,一般在python安装路径的bin目录下,如/usr/local/python3/bin/ - ln -s /path/to/gunicorn /usr/local/bin/gunicorn - ``` - - 使环境变量生效,避免出现unknown command错误 - ```bash - export PATH=/usr/local/bin:$PATH - ``` -3. 启动/停止服务 - ```bash - # 启动服务 - bash ./scripts/deploy.sh start - # 停止服务 - bash ./scripts/deploy.sh stop - ``` - -### 部署Web - - -1. 完成部署Server并启动服务,后端服务默认登陆账号/密码为:`CodeDog/admin`。 - -2. 进入web服务部署目录(例如 ``~/CodeAnalysis/web/tca-deploy-source``),以下路径均为目录内的相对路径 - -3. 部署/更新前端服务 - - ```bash - # 部署、更新都使用此命令 - bash ./scripts/deploy.sh init -d - ``` - - 具体请查阅部署脚本内容,可根据业务调整配置。 - -3. **额外说明** - - `tca-deploy-source/scripts/config.sh` 已配置默认环境变量,用户可根据需要调整环境变量再部署前端服务,具体可查阅脚本内容。 diff --git a/web/packages/tca-document/zh/quickStarted/setup.md b/web/packages/tca-document/zh/quickStarted/setup.md deleted file mode 100644 index 0f5b90db9..000000000 --- a/web/packages/tca-document/zh/quickStarted/setup.md +++ /dev/null @@ -1,24 +0,0 @@ -# 创建代码分析项目 - -部署并启动服务端后,通过以下步骤创建您的第一个代码分析项目。 - -**1. 在浏览器输入http://<部署机IP>/,进入TCA主页,开始项目配置。** -![homepage](../../images/homepage.png) - - -**2. 创建团队及项目。** - - - -**3. 登记代码库。输入代码库地址及配置凭证信息。** - - - -> 注:在本地扫描模式下,待扫描的代码库需在客户端配置文件中配置本地路径,步骤3中登记的代码库并非最终实际扫描的代码库,详见客户端使用文档。 - -**4. 创建分析方案,配置代码检查规则包。首次扫描可勾选「普通创建」,根据待分析的语言及问题类型勾选「分析语言」及「功能」。** - - - - -更多配置及使用说明详见[帮助文档](/zh/guide/README.md)。 \ No newline at end of file diff --git "a/web/packages/tca-document/zh/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\345\210\227\350\241\250.md" b/web/packages/tca-document/zh/quickStarted/tools.md similarity index 74% rename from "web/packages/tca-document/zh/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\345\210\227\350\241\250.md" rename to web/packages/tca-document/zh/quickStarted/tools.md index 4b3f042d7..a4c47ba0f 100644 --- "a/web/packages/tca-document/zh/guide/\345\267\245\345\205\267\347\256\241\347\220\206/\345\267\245\345\205\267\345\210\227\350\241\250.md" +++ b/web/packages/tca-document/zh/quickStarted/tools.md @@ -1,9 +1,10 @@ -# TCA 工具列表 -目前TCA支持以下工具: +# 工具列表 + +目前 TCA 支持以下工具: | 官方工具 | 第三方工具 | | :--------: | :-------: | -|[0daychecker](../tools/codedog_0Day_checker)| androidlint | +|[0daychecker](https://github.com/Tencent/CodeAnalysis/tree/main/tools/codedog_0Day_checker)| androidlint | |clangwarning| checkstyle | |codecount| clang | |customfilescan| cobra | @@ -12,10 +13,10 @@ |javawarning| cpplint | |regexfilescan| dart_code_metrics | |regexscan| dartanalyzer | -|[tca_ql_php_beta](../tools/Hades_Beta/README.md)| detekt | +|[tca_ql_php_beta](https://github.com/Tencent/CodeAnalysis/tree/main/tools/Hades_Beta)| detekt | |unusedresource| eslint | -|[collie](../tools/collie/README.md)| eslint_typescript | -|[compass](../tools/compass/README.md)| eslint_vue | +|[collie](https://github.com/Tencent/CodeAnalysis/tree/main/tools/collie/)| eslint_typescript | +|[compass](https://github.com/Tencent/CodeAnalysis/tree/main/tools/compass)| eslint_vue | || findbugs | || flake8 | || [flawfinder](https://github.com/TCATools/flawfinder) | diff --git a/web/packages/tca-layout/package.json b/web/packages/tca-layout/package.json index f3aca2931..0502072db 100644 --- a/web/packages/tca-layout/package.json +++ b/web/packages/tca-layout/package.json @@ -10,7 +10,9 @@ "scripts": { "dev": "NODE_ENV=development webpack server --config ./scripts/webpack.dev.js --progress --color", "build": "NODE_ENV=production webpack --config ./scripts/webpack.prod.js --progress --color", - "analyzer": "webpack-bundle-analyzer --port 8888 ./dist/stats.json" + "analyzer": "webpack-bundle-analyzer --port 8888 ./dist/stats.json", + "lint": "eslint --ext .js,.jsx,.ts,.tsx src/", + "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx src/ --fix" }, "dependencies": { "@types/lodash": "^4.14.175", @@ -70,4 +72,4 @@ "webpack-merge": "^5.8.0", "webpackbar": "^5.0.0-3" } -} +} \ No newline at end of file diff --git a/web/packages/tca-layout/src/components/copy/index.tsx b/web/packages/tca-layout/src/components/copy/index.tsx new file mode 100644 index 000000000..58d3aaaee --- /dev/null +++ b/web/packages/tca-layout/src/components/copy/index.tsx @@ -0,0 +1,46 @@ +// Copyright (c) 2021-2022 THL A29 Limited +// +// This source code file is made available under MIT License +// See LICENSE for details +// ============================================================================== + +/** + * 复制到剪切板 + */ + +import React from 'react'; +import cn from 'classnames'; +import CopyToClipboard from 'react-copy-to-clipboard'; +import { Tooltip, message } from 'coding-oa-uikit'; +import CopyIcon from 'coding-oa-uikit/lib/icon/Copy'; + +import copyStyle from './style.scss'; + +interface CopyProps { + text: string; // tooltip 提示文字 + copyText?: string; // copy 文字,不传默认为 text + msg?: string; // 复制成功提示,不传默认为 “复制成功” + className?: any; + style?: any; + getPopupContainer?: any +} + +const Copy = (props: CopyProps) => { + const { text, copyText, msg, className, style, getPopupContainer } = props; + + return ( + + message.success(msg || '复制成功')} + > + {/* tooltip 组件会默认替换第一个子节点类名 */} + + + + + + ); +}; + +export default Copy; diff --git a/web/packages/tca-layout/src/components/copy/style.scss b/web/packages/tca-layout/src/components/copy/style.scss new file mode 100644 index 000000000..735a49d36 --- /dev/null +++ b/web/packages/tca-layout/src/components/copy/style.scss @@ -0,0 +1,13 @@ +@import '@src/common/style/color.scss'; + +.copy-icon { + color: $grey-6; + margin-left: 6px; + transition: color 0.3s; + cursor: pointer; + + &:hover { + color: $grey-8; + } + +} \ No newline at end of file diff --git a/web/packages/tca-layout/src/components/ellipsis/index.tsx b/web/packages/tca-layout/src/components/ellipsis/index.tsx new file mode 100644 index 000000000..43042ace1 --- /dev/null +++ b/web/packages/tca-layout/src/components/ellipsis/index.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import classnames from 'classnames'; + +interface IProps { + maxWidth?: number; + className?: string; + children?: React.ReactNode; +} + +const EllipsisTemplate = ({ maxWidth, className, children }: IProps) => ( +
              + {children} +
              +); + +export default EllipsisTemplate; diff --git a/web/packages/tca-layout/src/context/store.tsx b/web/packages/tca-layout/src/context/store.tsx index e27534f76..1d612903d 100644 --- a/web/packages/tca-layout/src/context/store.tsx +++ b/web/packages/tca-layout/src/context/store.tsx @@ -40,7 +40,6 @@ const reducer = (state: StateProps, action: ActionProps) => { }; const StoreProvider = ({ children }: { children: any }) => { - // @ts-ignore const [state, dispatch] = useReducer(reducer, initialState); return ( diff --git a/web/packages/tca-layout/src/index.tsx b/web/packages/tca-layout/src/index.tsx index 8b27246f8..5b5584e75 100644 --- a/web/packages/tca-layout/src/index.tsx +++ b/web/packages/tca-layout/src/index.tsx @@ -41,7 +41,7 @@ const create = (rootDom: HTMLElement) => { } }; -function bootstrap() { } +function bootstrap() { console.log(''); } const mount = (props: any) => { const { rootDom, injectAsyncReducer, store } = props; @@ -53,7 +53,6 @@ const mount = (props: any) => { (node ? node.parentNode : document.body)} > diff --git a/web/packages/tca-layout/src/modules/layout/manage/siderbar-manage/menus.tsx b/web/packages/tca-layout/src/modules/layout/manage/siderbar-manage/menus.tsx index dcc57d2d4..5e5cf218a 100644 --- a/web/packages/tca-layout/src/modules/layout/manage/siderbar-manage/menus.tsx +++ b/web/packages/tca-layout/src/modules/layout/manage/siderbar-manage/menus.tsx @@ -58,8 +58,8 @@ const MENUS: MenuItem[] = [ { icon: , title: t('OAuth管理'), - link: '/manage/oauth', - key: 'oauth', + link: '/manage/oauths', + key: 'oauths', }, ]; diff --git a/web/packages/tca-layout/src/modules/layout/team/index.tsx b/web/packages/tca-layout/src/modules/layout/team/index.tsx index 48c75bcd3..435a9ce48 100644 --- a/web/packages/tca-layout/src/modules/layout/team/index.tsx +++ b/web/packages/tca-layout/src/modules/layout/team/index.tsx @@ -20,11 +20,11 @@ import Analysis from '@src/modules/layout/project/index'; import Tools from '@src/modules/tools'; import ToolDetail from '@src/modules/tools/detail'; import Toollibs from '@src/modules/tool-libs'; - +import Nodes from '@src/modules/nodes'; +import NodeProcess from '@src/modules/nodes/process' // 项目内 import { getTeamInfo } from '@src/services/team'; - import Header from '@src/modules/layout/header'; import Constant from '@src/reducer/constant'; @@ -97,6 +97,8 @@ const TeamLayout = () => { getComponent(ToolDetail)} />, getComponent(Tools)} />, getComponent(Toollibs)} />, + getComponent(NodeProcess)} /> + getComponent(Nodes)} /> } /> diff --git a/web/packages/tca-layout/src/modules/layout/team/sidebar.tsx b/web/packages/tca-layout/src/modules/layout/team/sidebar.tsx index da671f7a7..36f44c40e 100644 --- a/web/packages/tca-layout/src/modules/layout/team/sidebar.tsx +++ b/web/packages/tca-layout/src/modules/layout/team/sidebar.tsx @@ -14,6 +14,7 @@ import Project from 'coding-oa-uikit/lib/icon/Project'; import Api from 'coding-oa-uikit/lib/icon/Api'; import TeamOverview from 'coding-oa-uikit/lib/icon/TeamOverview'; import Tiles from 'coding-oa-uikit/lib/icon/Tiles'; +import Sitemap from 'coding-oa-uikit/lib/icon/Sitemap'; import MenuLayout from '@src/components/menu-layout'; import { API_DOC_PATH } from '@src/utils/getRoutePath'; @@ -80,6 +81,12 @@ const SideBar = () => { }, ], }, + { + icon: , + title: t('节点管理'), + link: `/t/${orgSid}/nodes/`, + key: 'nodes', + }, { icon: , title: t('开放平台'), diff --git a/web/packages/tca-layout/src/modules/layout/user/auth/index.tsx b/web/packages/tca-layout/src/modules/layout/user/auth/index.tsx index 3cbf9418b..bc9b676e1 100644 --- a/web/packages/tca-layout/src/modules/layout/user/auth/index.tsx +++ b/web/packages/tca-layout/src/modules/layout/user/auth/index.tsx @@ -146,7 +146,7 @@ const Auth = () => { * @param oauthInfo 选中OAuth平台信息 */ const onOAuthStart = (oauthInfo: any) => { - var winRef = window.open('',"oauthWindow",getWindowSize()); + const winRef = window.open('',"oauthWindow",getWindowSize()); getOAuthStatus({scm_platform_name: oauthInfo?.scm_platform_name}).then((res)=>{ winRef.location = res?.git_auth_url; }).catch(()=>{ @@ -160,7 +160,7 @@ const Auth = () => { * @param oauthInfo 选中OAuth平台信息 */ const onOAuthUpdate = (oauthInfo: any) => { - var winRef = window.open('',"oauthWindow",getWindowSize()); + const winRef = window.open('',"oauthWindow",getWindowSize()); getOAuthStatus({scm_platform_name: oauthInfo?.scm_platform_name}).then((res)=>{ winRef.location = res?.git_auth_url; }).catch(()=>{ diff --git a/web/packages/tca-layout/src/modules/nodes/constants.ts b/web/packages/tca-layout/src/modules/nodes/constants.ts new file mode 100644 index 000000000..5a0ac7de5 --- /dev/null +++ b/web/packages/tca-layout/src/modules/nodes/constants.ts @@ -0,0 +1,78 @@ +import { t } from '@src/i18n/i18next'; + +export const STATUS_ENUM = { + DISACTIVE: 0, + ACTIVE: 1, + OFFLINE: 2, +}; + +export const STATUS_CHOICES = { + [STATUS_ENUM.DISACTIVE]: t('不可用'), + [STATUS_ENUM.ACTIVE]: t('活跃'), + [STATUS_ENUM.OFFLINE]: t('离线'), +}; + +export const STATE_ENUM = { + FREE: 0, + BUSY: 1 +} + +export const STATE_CHOICES = { + [STATE_ENUM.BUSY]: t('忙碌'), + [STATE_ENUM.FREE]: t('空闲') +} + +export const STATUS_OPTIONS = [{ + text: STATUS_CHOICES[STATUS_ENUM.ACTIVE], + value: STATUS_ENUM.ACTIVE, +}, { + text: STATUS_CHOICES[STATUS_ENUM.DISACTIVE], + value: STATUS_ENUM.DISACTIVE, +}, { + text: STATUS_CHOICES[STATUS_ENUM.OFFLINE], + value: STATUS_ENUM.OFFLINE, +}]; + +export const TAG_TYPE_ENUM = { + PUBLIC: 1, + PRIVATE: 2, + DISABLED: 99 +}; + +export const TAG_TYPE_CHOICES = { + [TAG_TYPE_ENUM.PUBLIC]: t('公共'), + [TAG_TYPE_ENUM.PRIVATE]: t('团队'), + [TAG_TYPE_ENUM.DISABLED]: t('停用'), +}; + +export const TAG_TYPE_OPTIONS = [{ + text: TAG_TYPE_CHOICES[TAG_TYPE_ENUM.PRIVATE], + value: TAG_TYPE_ENUM.PRIVATE, +}, { + text: TAG_TYPE_CHOICES[TAG_TYPE_ENUM.DISABLED], + value: TAG_TYPE_ENUM.DISABLED, +}]; + +export const TAG_TYPE_COLOR = { + [TAG_TYPE_ENUM.PUBLIC]: 'geekblue', + [TAG_TYPE_ENUM.PRIVATE]: 'blue', + [TAG_TYPE_ENUM.DISABLED]: 'default', +}; + +export const TASK_STATE_ENUM = { + WAITING: 0, + RUNNING: 1, + CLOSED: 2, + CLOSING: 3, + INITING: 4, + INITED: 5, +}; + +export const TASK_STATE_CHOICES = { + [TASK_STATE_ENUM.WAITING]: t('等待中'), + [TASK_STATE_ENUM.RUNNING]: t('执行中'), + [TASK_STATE_ENUM.CLOSED]: t('已结束'), + [TASK_STATE_ENUM.CLOSING]: t('入库中'), + [TASK_STATE_ENUM.INITING]: t('初始化'), + [TASK_STATE_ENUM.INITED]: t('已初始化'), +}; diff --git a/web/packages/tca-manage/src/modules/nodes/index.tsx b/web/packages/tca-layout/src/modules/nodes/index.tsx similarity index 100% rename from web/packages/tca-manage/src/modules/nodes/index.tsx rename to web/packages/tca-layout/src/modules/nodes/index.tsx diff --git a/web/packages/tca-manage/src/modules/nodes/node-modal.tsx b/web/packages/tca-layout/src/modules/nodes/node-modal.tsx similarity index 63% rename from web/packages/tca-manage/src/modules/nodes/node-modal.tsx rename to web/packages/tca-layout/src/modules/nodes/node-modal.tsx index 43f857320..b2051eef2 100644 --- a/web/packages/tca-manage/src/modules/nodes/node-modal.tsx +++ b/web/packages/tca-layout/src/modules/nodes/node-modal.tsx @@ -1,4 +1,5 @@ import React, { useEffect } from 'react'; +import { useParams } from 'react-router-dom'; import { Modal, Form, Input, message, Select } from 'coding-oa-uikit'; // 项目内 @@ -16,10 +17,12 @@ interface IProps { onCancel: () => void; nodeinfo: any; tagOptions: Array; + members: Array; } -const NodeModal = ({ nodeinfo, visible, onOk, onCancel, tagOptions }: IProps) => { +const NodeModal = ({ nodeinfo, visible, onOk, onCancel, tagOptions, members }: IProps) => { const [form] = Form.useForm(); + const { orgSid }: any = useParams(); useEffect(() => { if (visible) { @@ -28,12 +31,12 @@ const NodeModal = ({ nodeinfo, visible, onOk, onCancel, tagOptions }: IProps) => }, [visible]); /** - * 表单保存操作 - * @param formData 参数 - */ + * 表单保存操作 + * @param formData 参数 + */ const onSubmitHandle = () => { form.validateFields().then((formData) => { - putNode(nodeinfo.id, formData).then(() => { + putNode(orgSid, nodeinfo.id, formData).then(() => { message.success(t('已更新节点')); onOk(); }); @@ -62,11 +65,12 @@ const NodeModal = ({ nodeinfo, visible, onOk, onCancel, tagOptions }: IProps) =>
              {STATUS_OPTIONS.map((item: any) => ( @@ -86,12 +90,30 @@ const NodeModal = ({ nodeinfo, visible, onOk, onCancel, tagOptions }: IProps) => ))} + + + - + diff --git a/web/packages/tca-manage/src/modules/nodes/node-search.tsx b/web/packages/tca-layout/src/modules/nodes/node-search.tsx similarity index 88% rename from web/packages/tca-manage/src/modules/nodes/node-search.tsx rename to web/packages/tca-layout/src/modules/nodes/node-search.tsx index 4d7170cf4..9eeef9788 100644 --- a/web/packages/tca-manage/src/modules/nodes/node-search.tsx +++ b/web/packages/tca-layout/src/modules/nodes/node-search.tsx @@ -5,7 +5,7 @@ import { Form, Button, Input, Select } from 'coding-oa-uikit'; // 项目内 import Filter from '@src/components/filter'; -// 模块内 +const { Option } = Select; const numberParams: Array = ['state', 'result']; const arrayParams: Array = []; @@ -67,7 +67,7 @@ const Search = ({ searchParams, loading, callback, tagOptions }: SearchProps) => onChange('manager', value)} /> @@ -76,8 +76,14 @@ const Search = ({ searchParams, loading, callback, tagOptions }: SearchProps) => style={{ width: 200 }} allowClear placeholder='全部' size='middle' - options={tagOptions} - onChange={value => onChange('exec_tags', value)} /> + onChange={value => onChange('exec_tags', value)} + > + {tagOptions.map((item: any) => ( + + ))} + {Object.keys(searchParams).some((key: string) => ( isArray(searchParams[key]) ? !isEmpty(searchParams[key]) : searchParams[key])) diff --git a/web/packages/tca-manage/src/modules/nodes/node-table.tsx b/web/packages/tca-layout/src/modules/nodes/node-table.tsx similarity index 59% rename from web/packages/tca-manage/src/modules/nodes/node-table.tsx rename to web/packages/tca-layout/src/modules/nodes/node-table.tsx index 1ca4a6e05..b3f2a2b42 100644 --- a/web/packages/tca-manage/src/modules/nodes/node-table.tsx +++ b/web/packages/tca-layout/src/modules/nodes/node-table.tsx @@ -1,23 +1,26 @@ import React, { useState, useEffect } from 'react'; -import { useHistory } from 'react-router-dom'; -import { Table, Tag, Button } from 'coding-oa-uikit'; +import { useHistory, useParams } from 'react-router-dom'; +import { Table, Tag, Button, Space } from 'coding-oa-uikit'; +import { isEmpty, union } from 'lodash'; import Loading from 'coding-oa-uikit/lib/icon/Loading' import Stop from 'coding-oa-uikit/lib/icon/Stop' import ExclamationCircle from 'coding-oa-uikit/lib/icon/ExclamationCircle' import DotCircle from 'coding-oa-uikit/lib/icon/DotCircle' - // 项目内 import { t } from '@src/i18n/i18next'; +import EllipsisTemplate from '@src/components/ellipsis'; import { formatDateTime, getPaginationParams, getFilterURLPath } from '@src/utils'; import { DEFAULT_PAGER } from '@src/common/constants'; import { useDeepEffect, useURLParams } from '@src/utils/hooks'; import { getNodes, getTags } from '@src/services/nodes'; +import { getTeamMember } from '@src/services/team'; // 模块内 -import { STATUS_ENUM, STATE_ENUM } from './constants'; +import { STATUS_ENUM, STATE_ENUM, TAG_TYPE_COLOR, TAG_TYPE_ENUM } from './constants'; import NodeModal from './node-modal'; +import NodeTaskModal from './node-tasks-modal'; import s from './style.scss'; import Search from './node-search'; @@ -39,17 +42,20 @@ const NodeTable = () => { const [count, setCount] = useState(DEFAULT_PAGER.count); const [loading, setLoading] = useState(false); const [tagOptions, setTagOptions] = useState>([]); + const [nodeTaskVisible, setNodeTaskVisible] = useState(false); + const [members, setMembers] = useState>([]); const [visible, setVisible] = useState(false); const [selectNode, setSelectNode] = useState(null); const { filter, currentPage, searchParams } = useURLParams(FILTER_FIELDS); const history = useHistory(); + const { orgSid }: any = useParams(); /** - * 根据路由参数获取团队列表 - */ + * 根据路由参数获取团队列表 + */ const getListData = () => { setLoading(true); - getNodes(filter).then((response) => { + getNodes(orgSid, filter).then((response) => { setCount(response.count); setListData(response.results || []); }).finally(() => { @@ -58,12 +64,16 @@ const NodeTable = () => { }; useEffect(() => { - getTags().then((response) => { + getTags(orgSid).then((response) => { setTagOptions(response.results.map((item: any) => ({ - text: item.name, + ...item, + text: item.display_name || item.name, value: item.name, }))); }); + getTeamMember(orgSid).then((response) => { + setMembers(union(response?.admins, response?.users)); + }); }, []); const onCreateOrUpdateHandle = (node: any = null) => { @@ -85,6 +95,11 @@ const NodeTable = () => { })); }; + const onShowTasks = (node: any) => { + setSelectNode(node); + setNodeTaskVisible(true); + }; + // 翻页 const onChangePageSize = (page: number, pageSize: number) => { const params = getPaginationParams(page, pageSize); @@ -102,32 +117,55 @@ const NodeTable = () => { setVisible(false); }} tagOptions={tagOptions} + members={members} + /> + setNodeTaskVisible(false)} + nodeId={selectNode?.id} /> -
              +
`${range[0]} - ${range[1]} 条数据,共 ${total} 条`, onChange: onChangePageSize, - }} rowKey={(item: any) => item.id} dataSource={listData}> - - + }} rowKey={(item: any) => item.id} dataSource={listData} scroll={{ x: true }} > + {name}} + /> + + isEmpty(related_managers) ? '无' : related_managers.join(', ')} + /> formatDateTime(last_beat_time) || '- -'} /> exec_tags.map((tag: string) => {tag}) - } + dataIndex="exec_tag_details" + key="exec_tag_details" + render={(exec_tag_details: any) => exec_tag_details.map((item: any) => + + {item.display_name || item.name} + + )} /> { key="enabled" render={(enabled: any, node: any) => { if (enabled === STATUS_ENUM.ACTIVE && node.state === STATE_ENUM.BUSY) { - return } color='processing'>运行中 + return } color='processing'>{t('运行中')} } else if (enabled === STATUS_ENUM.ACTIVE) { - return } color='success'>在线 + return } color='success'>{t('在线')} } else if (enabled === STATUS_ENUM.DISACTIVE) { - return }>失效 + return }>{t('失效')} } else { - return } color='warning'>离线 + return } color='warning'>{t('离线')} } }} /> @@ -149,20 +187,25 @@ const NodeTable = () => { title={t('其他操作')} dataIndex="op" render={(_: any, node: any) => ( -
+ + -
+ )} />
diff --git a/web/packages/tca-layout/src/modules/nodes/node-tasks-modal.tsx b/web/packages/tca-layout/src/modules/nodes/node-tasks-modal.tsx new file mode 100644 index 000000000..8342d4ab5 --- /dev/null +++ b/web/packages/tca-layout/src/modules/nodes/node-tasks-modal.tsx @@ -0,0 +1,136 @@ +import React, { useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import { Table, Modal, Tag } from 'coding-oa-uikit'; + +// 项目内 +import { t } from '@src/i18n/i18next'; +import EllipsisTemplate from '@src/components/ellipsis'; +import { getNodeTask } from '@src/services/nodes' +import { TASK_STATE_CHOICES } from './constants'; + +const { Column } = Table; + +export const DEFAULT_PAGER = { + count: 0, + pageSize: 10, + currentPage: 1, +}; + +interface NodeTaskModalProps { + visible: boolean; + nodeId: string; + onCancel: () => void; +} + +const NodeTaskModal = ({ visible, nodeId, onCancel }: NodeTaskModalProps) => { + const { orgSid }: any = useParams(); + const [listData, setListData] = useState>([]); + const [loading, setLoading] = useState(false); + const [pager, setPager] = useState(DEFAULT_PAGER); + const { count, currentPage } = pager; + + + useEffect(() => { + if (visible && nodeId) { + getTaskList(DEFAULT_PAGER.currentPage, DEFAULT_PAGER.pageSize); + } + }, [nodeId]) + + const getTaskList = (page: number, pageSize: number) => { + setLoading(true); + getNodeTask(orgSid, nodeId, { + limit: pageSize, + offset: (page - 1) * pageSize, + }).then((res: any) => { + setListData(res.results); + setPager({ + count: res.count, + pageSize, + currentPage: page, + }); + setLoading(false); + }); + }; + + const onChangePage = (current: number, pageSize: number) => { + setLoading(true); + getTaskList(current, pageSize); + }; + + return ( + + item.id} + dataSource={listData} + tableLayout='auto' + pagination={{ + current: currentPage, + total: count, + onChange: onChangePage, + simple: true, + }} + loading={loading} + > + ( + <> + + {scm_url} + +
+ 分支:{branch} +
+ + )} + /> + + ( +
{TASK_STATE_CHOICES[state]}
+ )} + width={100} + /> + ( +
+ {result_code !== null && ( + + {result_code_msg} + + )} + {result_msg && ( +
{result_msg}
+ )} +
+ )} + /> +
+
+ ); +}; + +export default NodeTaskModal; diff --git a/web/packages/tca-manage/src/modules/nodes/process/index.tsx b/web/packages/tca-layout/src/modules/nodes/process/index.tsx similarity index 91% rename from web/packages/tca-manage/src/modules/nodes/process/index.tsx rename to web/packages/tca-layout/src/modules/nodes/process/index.tsx index 68bcf1da9..8f009f933 100644 --- a/web/packages/tca-manage/src/modules/nodes/process/index.tsx +++ b/web/packages/tca-layout/src/modules/nodes/process/index.tsx @@ -29,7 +29,7 @@ import { STATUS_ENUM, STATE_ENUM } from '../constants'; const { Column } = Table; const Process = () => { - const { nodeId }: any = useParams(); + const { orgSid, nodeId }: any = useParams(); const [nodeInfo, setNodeInfo] = useState(null); const [processTableData, setProcessTableData] = useState>([]); const [loading, setLoading] = useState(false); @@ -40,7 +40,7 @@ const Process = () => { const getNodePorcessRequest = async (id: number) => { setLoading(true); - const process = await getNodeProcess(id); + const process = await getNodeProcess(orgSid, id); const data = Object.keys(process).map((key: string) => { const item = process[key]; const supported = Object.keys(item).every((k: string) => item[k].supported); @@ -61,7 +61,7 @@ const Process = () => { useEffect(() => { (async () => { - const node = await getNode(nodeId); + const node = await getNode(orgSid, nodeId); setNodeInfo(node); await getNodePorcessRequest(nodeId); })(); @@ -115,31 +115,32 @@ const Process = () => { const { name } = item.checktool; process[name] = omit(item, ['checktool']); }); - putNodeProcess(nodeId, process).then(() => { + putNodeProcess(orgSid, nodeId, process).then(() => { message.success(t('已更新该节点进程配置')); }); }; if (nodeInfo) { - const { name, manager, addr, enabled, state } = nodeInfo; - let nodeStatusRender = } color='warning'>离线 + const { name, manager, related_managers, addr, enabled, state } = nodeInfo; + let nodeStatusRender = } color='warning'>离线 if (enabled === STATUS_ENUM.ACTIVE && state === STATE_ENUM.BUSY) { - nodeStatusRender = } color='processing'>运行中 + nodeStatusRender = } color='processing'>运行中 } else if (enabled === STATUS_ENUM.ACTIVE) { - nodeStatusRender = } color='success'>在线 + nodeStatusRender = } color='success'>在线 } else if (enabled === STATUS_ENUM.DISACTIVE) { - nodeStatusRender = }>失效 + nodeStatusRender = }>失效 } return (
{name} {manager} + {related_managers.join(', ')} {addr} {nodeStatusRender} - + {formatDateTime(nodeInfo.last_beat_time) || '- -'} diff --git a/web/packages/tca-layout/src/modules/nodes/style.scss b/web/packages/tca-layout/src/modules/nodes/style.scss new file mode 100644 index 000000000..229249fdc --- /dev/null +++ b/web/packages/tca-layout/src/modules/nodes/style.scss @@ -0,0 +1,19 @@ +@import "@src/common/style/color.scss"; + +.header { + padding: 0 24px; + border-bottom: 1px solid $grey-3; + :global(.ant-tabs-nav::before) { + border: none; + } +} + +.filter { + display: flex; + align-items: center; + justify-content: space-between; + height: 48px; + padding: 0 24px; + background-color: hsla(0, 0%, 98.8%, 0.98); + box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.1), 0 0.5px 0 0 rgba(0, 0, 0, 0.08); +} diff --git a/web/packages/tca-manage/src/modules/nodes/tag-modal.tsx b/web/packages/tca-layout/src/modules/nodes/tag-modal.tsx similarity index 58% rename from web/packages/tca-manage/src/modules/nodes/tag-modal.tsx rename to web/packages/tca-layout/src/modules/nodes/tag-modal.tsx index c9c925051..8ba1d2105 100644 --- a/web/packages/tca-manage/src/modules/nodes/tag-modal.tsx +++ b/web/packages/tca-layout/src/modules/nodes/tag-modal.tsx @@ -1,11 +1,15 @@ import React, { useEffect } from 'react'; -import { Modal, Form, Input, message } from 'coding-oa-uikit'; +import { useParams } from 'react-router-dom'; +import { Modal, Form, Input, message, Select } from 'coding-oa-uikit'; // 项目内 import { t } from '@src/i18n/i18next'; import { postTags, putTag } from '@src/services/nodes'; // 模块内 +import { TAG_TYPE_OPTIONS } from './constants'; + +const { Option } = Select; interface IProps { visible: boolean; @@ -16,6 +20,7 @@ interface IProps { const TagModal = ({ taginfo, visible, onOk, onCancel }: IProps) => { const [form] = Form.useForm(); + const { orgSid }: any = useParams(); useEffect(() => { if (visible) { @@ -25,25 +30,23 @@ const TagModal = ({ taginfo, visible, onOk, onCancel }: IProps) => { const onSaveRequest = (formData: any) => { if (taginfo) { - return putTag(taginfo.id, formData).then((response) => { + return putTag(orgSid, taginfo.id, formData).then((response) => { message.success(t('已更新标签信息')); return response; }); } - return postTags(formData).then((response) => { + return postTags(orgSid, formData).then((response) => { message.success(t('已创建标签')); return response; }); }; /** - * 表单保存操作 - * @param formData 参数 - */ + * 表单保存操作 + * @param formData 参数 + */ const onSubmitHandle = () => { form.validateFields().then((formData) => { - // 暂时标签仅能设置为公有 - formData.public = true onSaveRequest(formData).then(() => { onOk(); }); @@ -60,19 +63,35 @@ const TagModal = ({ taginfo, visible, onOk, onCancel }: IProps) => { afterClose={form.resetFields} >
- + + } + + {taginfo && + + } - {/* - - */}
); diff --git a/web/packages/tca-layout/src/modules/nodes/tag-table.tsx b/web/packages/tca-layout/src/modules/nodes/tag-table.tsx new file mode 100644 index 000000000..a5c53fcc9 --- /dev/null +++ b/web/packages/tca-layout/src/modules/nodes/tag-table.tsx @@ -0,0 +1,112 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import { Table, Button, Tag } from 'coding-oa-uikit'; + +// 项目内 +import { t } from '@src/i18n/i18next'; +import { getTags } from '@src/services/nodes'; + +// 模块内 +import { TAG_TYPE_ENUM, TAG_TYPE_CHOICES, TAG_TYPE_COLOR } from './constants'; +import TagModal from './tag-modal'; +import { filter } from 'lodash'; + +const { Column } = Table; + +const formatTypeTag = (tagType: number) => { + switch (tagType) { + case TAG_TYPE_ENUM.PUBLIC: + return {TAG_TYPE_CHOICES[tagType]}; + case TAG_TYPE_ENUM.PRIVATE: + return {TAG_TYPE_CHOICES[tagType]}; + case TAG_TYPE_ENUM.DISABLED: + return {TAG_TYPE_CHOICES[tagType]}; + default: + return {TAG_TYPE_CHOICES[TAG_TYPE_ENUM.PUBLIC]}; + } +} + +const TagTable = () => { + const [listData, setListData] = useState>([]); + const [visible, setVisible] = useState(false); + const [selectTag, setSelectTag] = useState(null); + const { orgSid }: any = useParams(); + const orgTagList = useMemo(() => filter(listData, (tag: any) => (tag.org_sid === orgSid)), [listData]); + + /** + * 获取标签列表 + */ + const getListData = () => { + getTags(orgSid).then((response) => { + setListData(response.results || []); + }); + }; + + useEffect(() => { + getListData(); + }, []); + + const onCreateOrUpdateHandle = (tag: any = null) => { + setVisible(true); + setSelectTag(tag); + }; + + return ( + <> +
+ +
+ setVisible(false)} + onOk={() => { + getListData(); + setVisible(false); + }} + taginfo={selectTag} + /> + item.id} dataSource={orgTagList}> + ( + <> + {record?.display_name || record?.name} +
+ {name} +
+ + )} + /> + desc || '- -'} + /> + formatTypeTag(type)} + /> + ( + + )} + /> +
+ + ); +}; + +export default TagTable; diff --git a/web/packages/tca-layout/src/modules/team/components/profile/index.tsx b/web/packages/tca-layout/src/modules/team/components/profile/index.tsx index 5c271173e..b7e017655 100644 --- a/web/packages/tca-layout/src/modules/team/components/profile/index.tsx +++ b/web/packages/tca-layout/src/modules/team/components/profile/index.tsx @@ -12,6 +12,7 @@ import Group from 'coding-oa-uikit/lib/icon/Group'; import Project from 'coding-oa-uikit/lib/icon/Project'; import Package from 'coding-oa-uikit/lib/icon/Package'; +import Copy from '@src/components/copy' import { useStateStore } from '@src/context/store'; import { t } from '@src/i18n/i18next'; import { getTeamInfo, updateTeamInfo, disableTeam } from '@src/services/team'; @@ -130,7 +131,12 @@ const Profile = () => { onFinish={onFinish} > +
{data.org_sid}
+
+ { offset = pageStart, limit = DEFAULT_PAGER.pageSize, searchParams = {}, - callback?: Function, + callback?: (...args: any[]) => any, ) => { const res = (await getTeams({ offset, limit, ...searchParams })) || {}; setScrollLoading(false); @@ -92,7 +92,7 @@ const Team = () => { // 滚动加载更多团队 const loadMoreTeam = () => { const teamWrapper = document.getElementById('team-wrapper'); - if (teamWrapper.scrollTop + teamWrapper.clientHeight > teamWrapper.scrollHeight*0.8 && !allLoaded && !scrollLoading) { + if (teamWrapper.scrollTop + teamWrapper.clientHeight > teamWrapper.scrollHeight * 0.8 && !allLoaded && !scrollLoading) { setScrollLoading(true); getTeamList(true); } @@ -160,21 +160,21 @@ const Team = () => { } > -
- {list.map((item: any) => ( -
onClickTeam(item)} - > - -
- ))} -
+ {list.map((item: any) => ( +
onClickTeam(item)} + > + +
+ ))} +
{!allLoaded && 滚动加载更多团队}
diff --git a/web/packages/tca-layout/src/services/nodes.ts b/web/packages/tca-layout/src/services/nodes.ts new file mode 100644 index 000000000..5d8cfeb22 --- /dev/null +++ b/web/packages/tca-layout/src/services/nodes.ts @@ -0,0 +1,83 @@ +import { get, post, put, del } from './index'; +import { MAIN_SERVER_API } from './common'; + +const getNodePrefix = (orgId: string) => `${MAIN_SERVER_API}/orgs/${orgId}/nodes/`; + +/** + * 获取节点列表 + * @param orgId + * @param params 筛选参数 + */ +export const getNodes = (orgId: string, params: any = null) => get(getNodePrefix(orgId), params); + +/** + * 获取节点详情 + * @param orgId + * @param nodeId + */ +export const getNode = (orgId: string, nodeId: number | string) => get(`${getNodePrefix(orgId)}${nodeId}/`); + +/** + * 更新节点详情 + * @param orgId + * @param nodeId + * @param data 更新数据 + */ +export const putNode = (orgId: string, nodeId: number | string, data: any) => put(`${getNodePrefix(orgId)}${nodeId}/`, data); + +/** + * 删除节点 + * @param orgId + * @param nodeId + */ +export const delNode = (orgId: string, nodeId: number | string) => del(`${getNodePrefix(orgId)}${nodeId}/`); + +/** + * 获取节点进程 + * @param orgId + * @param nodeId + */ +export const getNodeProcess = (orgId: string, nodeId: number | string) => get(`${getNodePrefix(orgId)}${nodeId}/processes/`); + +/** + * 更新节点进程 + * @param orgId + * @param nodeId + * @param data 节点信息 + */ +export const putNodeProcess = (orgId: string, nodeId: number | string, data: any) => put(`${getNodePrefix(orgId)}${nodeId}/processes/`, data); + +/** + * 查看节点执行任务列表 + * @param orgId + * @param nodeId + * @param params 筛选项 + */ + export const getNodeTask = (orgId: string, nodeId: number | string, params: any) => get(`${getNodePrefix(orgId)}${nodeId}/tasks/`, params); + +/** + * 获取标签列表 + * @param orgId + * @param params 筛选参数 + */ +export const getTags = (orgId: string, params: any = null) => get(`${getNodePrefix(orgId)}tags/`, params); + +/** + * 创建标签 + * @param orgId + * @param data 标签信息 + */ +export const postTags = (orgId: string, params: any = null) => post(`${getNodePrefix(orgId)}tags/`, params); + +/** + * 更新标签 + * @param orgId + * @param data 标签信息 + */ +export const putTag = (orgId: string, tagId: number | string, params: any = null) => put(`${getNodePrefix(orgId)}tags/${tagId}/`, params); + +/** + * 删除标签 + * @param orgId + */ +export const delTag = (orgId: string, tagId: number | string) => del(`${getNodePrefix(orgId)}tags/${tagId}/`); diff --git a/web/packages/tca-layout/src/utils/hooks.ts b/web/packages/tca-layout/src/utils/hooks.ts index 2f4b819b6..0efac05ce 100644 --- a/web/packages/tca-layout/src/utils/hooks.ts +++ b/web/packages/tca-layout/src/utils/hooks.ts @@ -50,7 +50,7 @@ export const useURLParams = (FILTER_FIELDS: Array = []) => { * @param fn * @param deps */ -export const useDeepEffect = (fn: Function, deps: any) => { +export const useDeepEffect = (fn: (...args: any[]) => any, deps: any) => { const isFirst = useRef(true); const prevDeps = useRef(deps); diff --git a/web/packages/tca-layout/src/utils/index.ts b/web/packages/tca-layout/src/utils/index.ts index 7abea8ae9..953177606 100644 --- a/web/packages/tca-layout/src/utils/index.ts +++ b/web/packages/tca-layout/src/utils/index.ts @@ -27,6 +27,22 @@ export const formatDate = (time: any, format = 'YYYY-MM-DD') => Moment(time, for */ export const formatDateTime = (time: any) => (time ? formatDate(time, 'YYYY-MM-DD HH:mm:ss') : null); +/** + * 格式化文件大小 + * @param bytes 文件大小 + */ +const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + +export const bytesToSize = (bytes: number) => { + if (typeof bytes !== 'number') { + return '-' + } + if (bytes === 0) return '0 B'; + const k = 1024; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i]; +} + /** * 根据loading状态设置layout加载class,用于控制container显示/隐藏 * @param loading 加载状态 diff --git a/web/packages/tca-manage/README.md b/web/packages/tca-manage/README.md index 6d7b2ff70..762892c50 100644 --- a/web/packages/tca-manage/README.md +++ b/web/packages/tca-manage/README.md @@ -1,6 +1,8 @@ # `tca-manage` -腾讯云代码分析后台管理微前端。 +腾讯代码分析后台管理微前端。 + +- 后台管理相关列表页,如团队管理、用户管理、仓库管理等 ## 本地启动脚本 diff --git a/web/packages/tca-manage/global.d.ts b/web/packages/tca-manage/global.d.ts index 3186fd6cc..d5cf927a7 100644 --- a/web/packages/tca-manage/global.d.ts +++ b/web/packages/tca-manage/global.d.ts @@ -1,49 +1 @@ declare module '*.scss'; - -type Store = import('redux').Store; -type Reducer = import('redux').Reducer; - -interface LifeCycle { - bootstrap: (config: T) => void; - mount: (config: T) => void; - unmount: (config: T) => void; - update?: (config: T) => void; -} - -type RegisterMicroApp = (id: string, lifecycle: LifeCycle) => void; -type TInjectAsyncReducer = (name: string, reducer: Reducer, override?: boolean) => void; - -interface MicroApplicationProps { - name: string; - description: string; - match: string; - commitId?: string; - changeAt?: string; - css: string[]; - js: string[]; - prefix: string[] | string; -} - -interface MicroApplication { - props: MicroApplicationProps; - readonly loadStyle: () => Promise; - readonly loadScript: () => Promise; - readonly loadResources: () => Promise; - readonly removeStyle: () => Promise; - readonly path: () => RegExp; -} - -interface WindowMicroHook { - registerApp: RegisterMicroApp; - injectAsyncReducer: TInjectAsyncReducer; - store: Store; - meta: MicroApplication[]; -} - -interface Window { - microHook: WindowMicroHook; -} - -declare const PLATFORM_ENV: 'saas' | 'private' | 'oa'; - -declare const ENABLE_MANAGE: 'TRUE'; diff --git a/web/packages/tca-manage/i18next-scanner.config.js b/web/packages/tca-manage/i18next-scanner.config.js new file mode 100644 index 000000000..f0e136f4e --- /dev/null +++ b/web/packages/tca-manage/i18next-scanner.config.js @@ -0,0 +1,2 @@ +const { i18nScannerConfig } = require('@tencent/micro-frontend-shared/i18n/i18next-scanner.config'); +module.exports = i18nScannerConfig; diff --git a/web/packages/tca-manage/package.json b/web/packages/tca-manage/package.json index 06b6c5565..71ad59d74 100644 --- a/web/packages/tca-manage/package.json +++ b/web/packages/tca-manage/package.json @@ -2,17 +2,30 @@ "name": "tca-manage", "version": "1.0.0", "description": "TCA 后台管理微前端", + "private": true, "keywords": [ - "tca-manage", - "micro-frontend" + "\"single-spa", + "micro-frontend", + "tca-manage\"" ], + "author": "nickctang ", + "homepage": "", "license": "MIT", + "main": "src/index.tsx", + "files": [ + "src" + ], "scripts": { - "dev": "NODE_ENV=development webpack server --config ./scripts/webpack.dev.js --progress --color", - "build": "NODE_ENV=production webpack --config ./scripts/webpack.prod.js --progress --color", - "analyzer": "webpack-bundle-analyzer --port 8888 ./dist/stats.json" + "dev": "PLATFORM_ENV=${PLATFORM_ENV:-open} PUBLIC_PATH=${PUBLIC_PATH:-http://127.0.0.1:5058/} PORT=${PORT:-5058} NODE_ENV=development webpack server --config ./webpack.config.ts --progress --color", + "build": "NODE_ENV=production webpack --config ./webpack.config.ts --progress --color", + "analyzer": "webpack-bundle-analyzer --port 8888 ./dist/stats.json", + "lint": "eslint --ext .js,.jsx,.ts,.tsx src/", + "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx src/ --fix", + "scanner": "i18next-scanner" }, "dependencies": { + "@ant-design/charts": "1.2.14", + "@tencent/micro-frontend-shared": "^1.0.0", "@types/lodash": "^4.14.175", "@types/qs": "^6.9.7", "@types/react": "^17.0.24", @@ -21,52 +34,20 @@ "@types/react-redux": "^7.1.18", "@types/react-router-dom": "^5.3.1", "classnames": "^2.3.1", - "coding-oa-uikit": "^4.3.9", "i18next": "^21.2.4", "lodash": "^4.17.21", - "moment": "2.29.4", + "moment": "^2.29.4", "qs": "^6.10.1", "react": "^17.0.2", "react-copy-to-clipboard": "^5.0.4", "react-dom": "^17.0.2", - "react-i18next": "^11.12.0", + "react-i18next": "^11.17.3", "react-redux": "^7.2.5", "react-router-dom": "^5.3.0", + "tdesign-react": "0.36.4", "universal-cookie": "^4.0.4" }, "devDependencies": { - "@babel/core": "^7.15.5", - "@babel/plugin-proposal-class-properties": "^7.14.5", - "@babel/plugin-proposal-object-rest-spread": "^7.15.6", - "@babel/plugin-transform-runtime": "^7.15.0", - "@babel/preset-env": "^7.15.6", - "@babel/preset-react": "^7.14.5", - "@babel/preset-typescript": "^7.15.0", - "@hot-loader/react-dom": "^17.0.1", - "@types/friendly-errors-webpack-plugin": "^0.1.4", - "@types/mini-css-extract-plugin": "^2.3.0", - "@types/sass": "^1.16.1", - "assets-webpack-plugin": "^7.1.1", - "axios": "^0.22.0", - "babel-loader": "^8.2.2", - "clean-webpack-plugin": "^4.0.0", - "copy-webpack-plugin": "^9.0.1", - "css-loader": "^6.3.0", - "eslint-webpack-plugin": "^3.0.1", - "friendly-errors-webpack-plugin": "^1.7.0", - "html-webpack-plugin": "^5.3.2", - "mini-css-extract-plugin": "^2.3.0", - "postcss-loader": "^6.1.1", - "postcss-preset-env": "^6.7.0", - "react-hot-loader": "^4.13.0", - "sass": "^1.42.1", - "sass-loader": "^12.1.0", - "style-loader": "^3.3.0", - "webpack": "^5.54.0", - "webpack-bundle-analyzer": "^4.4.2", - "webpack-cli": "^4.8.0", - "webpack-dev-server": "^4.3.0", - "webpack-merge": "^5.8.0", - "webpackbar": "^5.0.0-3" + "@tencent/micro-frontend-webpack": "^1.0.0" } -} +} \ No newline at end of file diff --git a/web/packages/tca-manage/public/locales/en-US/translation.json b/web/packages/tca-manage/public/locales/en-US/translation.json new file mode 100644 index 000000000..2dd464468 --- /dev/null +++ b/web/packages/tca-manage/public/locales/en-US/translation.json @@ -0,0 +1,182 @@ +{ + "执行成功": "执行成功", + "执行异常": "执行异常", + "等待中": "等待中", + "执行中": "执行中", + "已结束": "已结束", + "入库中": "入库中", + "初始化": "初始化", + "已初始化": "已初始化", + "分析记录列表": "分析记录列表", + "分析任务": "分析任务", + "所属团队&项目": "所属团队&项目", + "执行进度": "执行进度", + "执行状态": "执行状态", + "总耗时": "总耗时", + "启动时间": "启动时间", + "活跃": "活跃", + "节点列表": "节点列表", + "标签列表": "标签列表", + "已更新节点": "已更新节点", + "更新节点": "更新节点", + "节点名称": "节点名称", + "节点名称为必填项": "节点名称为必填项", + "节点标签为必选项": "节点标签为必选项", + "负责人": "负责人", + "IP 地址": "IP 地址", + "最近上报心跳": "最近上报心跳", + "所属标签": "所属标签", + "节点状态": "节点状态", + "编辑": "编辑", + "已更新该节点进程配置": "已更新该节点进程配置", + "节点信息": "节点信息", + "节点工具进程配置": "节点工具进程配置", + "保存节点工具进程配置": "保存节点工具进程配置", + "工具": "工具", + "已更新标签信息": "已更新标签信息", + "更新标签": "更新标签", + "添加标签": "添加标签", + "标签名称": "标签名称", + "标签名称为必填项": "标签名称为必填项", + "描述": "描述", + "已激活": "已激活", + "团队列表": "团队列表", + "成员数": "成员数", + "项目数": "项目数", + "代码库": "代码库", + "团队名称": "团队名称", + "团队概览": "团队概览", + "状态": "状态", + "级别": "级别", + "代码库地址": "代码库地址", + "项目列表": "项目列表", + "项目名称": "项目名称", + "所属团队": "所属团队", + "管理员": "管理员", + "创建时间": "创建时间", + "正常运营": "正常运营", + "暂停使用": "暂停使用", + "已下架": "已下架", + "体验运营": "体验运营", + "工具列表": "工具列表", + "已调整工具权限": "已调整工具权限", + "调整工具权限": "调整工具权限", + "工具名称&简介": "工具名称&简介", + "提供方": "提供方", + "权限状态": "权限状态", + "操作": "操作", + "权限调整": "权限调整", + "普通用户": "普通用户", + "VIP用户": "VIP用户", + "超级VIP用户": "超级VIP用户", + "待激活": "待激活", + "已过期": "已过期", + "禁止": "禁止", + "用户管理列表": "用户管理列表", + "创建用户": "创建用户", + "已更新用户信息": "已更新用户信息", + "已创建用户": "已创建用户", + "更新用户": "更新用户", + "账户": "账户", + "账户为必填项": "账户为必填项", + "更新密码": "更新密码", + "昵称": "昵称", + "用户昵称为必填项": "用户昵称为必填项", + "仅支持英文、数字、邮箱等": "仅支持英文、数字、邮箱等", + "超级管理员": "超级管理员", + "节点配置信息": "节点配置信息", + "公共": "公共", + "团队": "团队", + "停用": "停用", + "规则名称": "规则名称", + "所属工具": "所属工具", + "类别": "类别", + "语言": "语言", + "归档分析记录列表": "归档分析记录列表", + "取消任务成功": "取消任务成功", + "取消任务": "取消任务", + "取消原因": "取消原因", + "取消原因为必选项": "取消原因为必选项", + "详情": "详情", + "结果详情": "结果详情", + "渠道": "渠道", + "启动渠道": "启动渠道", + "URL": "URL", + "启动人": "启动人", + "当月": "当月", + "当日": "当日", + "执行结果": "执行结果", + "已创建配置": "已创建配置", + "已更新配置": "已更新配置", + "已删除配置": "已删除配置", + "OAuth配置列表": "OAuth配置列表", + "删除": "删除", + "OAuth配置": "OAuth配置", + "更新配置": "更新配置", + "创建配置": "创建配置", + "平台类型": "平台类型", + "Client ID": "Client ID", + "Client ID为必填项": "Client ID为必填项", + "Client Secret": "Client Secret", + "Client Secret为必填项": "Client Secret为必填项", + "回调地址": "回调地址", + "请填入当前TCA平台配置的域名或IP地址": "请填入当前TCA平台配置的域名或IP地址", + "(如当前页面非80端口,需要显式指定端口号)": "(如当前页面非80端口,需要显式指定端口号)", + "回调地址为必填项": "回调地址为必填项", + "平台描述": "平台描述", + "平台": "平台", + "配置状态": "配置状态", + "已配置": "已配置", + "未配置": "未配置", + "删除配置": "删除配置", + "analyze": "analyze", + "datahandle": "datahandle", + "compile": "compile", + "工具权限": "工具权限", + "最近修改时间": "最近修改时间", + "最近访问时间": "最近访问时间", + "腾讯工蜂(OA)": "腾讯工蜂(OA)", + "腾讯工蜂": "腾讯工蜂", + "Coding": "Coding", + "GitHub": "GitHub", + "Gitee": "Gitee", + "GitLab": "GitLab", + "团队 ID": "团队 ID", + "项目 ID": "项目 ID", + "批量更新节点信息成功": "批量更新节点信息成功", + "批量更新节点信息失败": "批量更新节点信息失败", + "节点信息无更新": "节点信息无更新", + "批量编辑节点信息": "批量编辑节点信息", + "标签": "标签", + "(可选)": "(可选)", + "关注人": "关注人", + "管理员为必填项": "管理员为必填项", + "未选中任何节点": "未选中任何节点", + "工具进程": "工具进程", + "任务列表": "任务列表", + "批量编辑节点": "批量编辑节点", + "批量配置工具": "批量配置工具", + "子任务": "子任务", + "执行任务列表": "执行任务列表", + "已添加标签": "已添加标签", + "展示名称": "展示名称", + "展示名称为必填项": "展示名称为必填项", + "标签类型": "标签类型", + "标签类型为必选项": "标签类型为必选项", + "所属团队为必选项": "所属团队为必选项", + "标签描述": "标签描述", + "类型": "类型", + "禁用": "禁用", + "已禁用团队": "已禁用团队", + "恢复团队": "恢复团队", + "确定要恢复已禁用的团队吗?": "确定要恢复已禁用的团队吗?", + "已恢复团队": "已恢复团队", + "查看项目": "查看项目", + "禁用团队": "禁用团队", + "已禁用项目": "已禁用项目", + "恢复项目": "恢复项目", + "确定要恢复已禁用的项目吗?": "确定要恢复已禁用的项目吗?", + "已恢复项目": "已恢复项目", + "项目": "项目", + "禁用项目": "禁用项目" +} diff --git a/web/packages/tca-manage/public/locales/zh-CN/translation.json b/web/packages/tca-manage/public/locales/zh-CN/translation.json new file mode 100644 index 000000000..2dd464468 --- /dev/null +++ b/web/packages/tca-manage/public/locales/zh-CN/translation.json @@ -0,0 +1,182 @@ +{ + "执行成功": "执行成功", + "执行异常": "执行异常", + "等待中": "等待中", + "执行中": "执行中", + "已结束": "已结束", + "入库中": "入库中", + "初始化": "初始化", + "已初始化": "已初始化", + "分析记录列表": "分析记录列表", + "分析任务": "分析任务", + "所属团队&项目": "所属团队&项目", + "执行进度": "执行进度", + "执行状态": "执行状态", + "总耗时": "总耗时", + "启动时间": "启动时间", + "活跃": "活跃", + "节点列表": "节点列表", + "标签列表": "标签列表", + "已更新节点": "已更新节点", + "更新节点": "更新节点", + "节点名称": "节点名称", + "节点名称为必填项": "节点名称为必填项", + "节点标签为必选项": "节点标签为必选项", + "负责人": "负责人", + "IP 地址": "IP 地址", + "最近上报心跳": "最近上报心跳", + "所属标签": "所属标签", + "节点状态": "节点状态", + "编辑": "编辑", + "已更新该节点进程配置": "已更新该节点进程配置", + "节点信息": "节点信息", + "节点工具进程配置": "节点工具进程配置", + "保存节点工具进程配置": "保存节点工具进程配置", + "工具": "工具", + "已更新标签信息": "已更新标签信息", + "更新标签": "更新标签", + "添加标签": "添加标签", + "标签名称": "标签名称", + "标签名称为必填项": "标签名称为必填项", + "描述": "描述", + "已激活": "已激活", + "团队列表": "团队列表", + "成员数": "成员数", + "项目数": "项目数", + "代码库": "代码库", + "团队名称": "团队名称", + "团队概览": "团队概览", + "状态": "状态", + "级别": "级别", + "代码库地址": "代码库地址", + "项目列表": "项目列表", + "项目名称": "项目名称", + "所属团队": "所属团队", + "管理员": "管理员", + "创建时间": "创建时间", + "正常运营": "正常运营", + "暂停使用": "暂停使用", + "已下架": "已下架", + "体验运营": "体验运营", + "工具列表": "工具列表", + "已调整工具权限": "已调整工具权限", + "调整工具权限": "调整工具权限", + "工具名称&简介": "工具名称&简介", + "提供方": "提供方", + "权限状态": "权限状态", + "操作": "操作", + "权限调整": "权限调整", + "普通用户": "普通用户", + "VIP用户": "VIP用户", + "超级VIP用户": "超级VIP用户", + "待激活": "待激活", + "已过期": "已过期", + "禁止": "禁止", + "用户管理列表": "用户管理列表", + "创建用户": "创建用户", + "已更新用户信息": "已更新用户信息", + "已创建用户": "已创建用户", + "更新用户": "更新用户", + "账户": "账户", + "账户为必填项": "账户为必填项", + "更新密码": "更新密码", + "昵称": "昵称", + "用户昵称为必填项": "用户昵称为必填项", + "仅支持英文、数字、邮箱等": "仅支持英文、数字、邮箱等", + "超级管理员": "超级管理员", + "节点配置信息": "节点配置信息", + "公共": "公共", + "团队": "团队", + "停用": "停用", + "规则名称": "规则名称", + "所属工具": "所属工具", + "类别": "类别", + "语言": "语言", + "归档分析记录列表": "归档分析记录列表", + "取消任务成功": "取消任务成功", + "取消任务": "取消任务", + "取消原因": "取消原因", + "取消原因为必选项": "取消原因为必选项", + "详情": "详情", + "结果详情": "结果详情", + "渠道": "渠道", + "启动渠道": "启动渠道", + "URL": "URL", + "启动人": "启动人", + "当月": "当月", + "当日": "当日", + "执行结果": "执行结果", + "已创建配置": "已创建配置", + "已更新配置": "已更新配置", + "已删除配置": "已删除配置", + "OAuth配置列表": "OAuth配置列表", + "删除": "删除", + "OAuth配置": "OAuth配置", + "更新配置": "更新配置", + "创建配置": "创建配置", + "平台类型": "平台类型", + "Client ID": "Client ID", + "Client ID为必填项": "Client ID为必填项", + "Client Secret": "Client Secret", + "Client Secret为必填项": "Client Secret为必填项", + "回调地址": "回调地址", + "请填入当前TCA平台配置的域名或IP地址": "请填入当前TCA平台配置的域名或IP地址", + "(如当前页面非80端口,需要显式指定端口号)": "(如当前页面非80端口,需要显式指定端口号)", + "回调地址为必填项": "回调地址为必填项", + "平台描述": "平台描述", + "平台": "平台", + "配置状态": "配置状态", + "已配置": "已配置", + "未配置": "未配置", + "删除配置": "删除配置", + "analyze": "analyze", + "datahandle": "datahandle", + "compile": "compile", + "工具权限": "工具权限", + "最近修改时间": "最近修改时间", + "最近访问时间": "最近访问时间", + "腾讯工蜂(OA)": "腾讯工蜂(OA)", + "腾讯工蜂": "腾讯工蜂", + "Coding": "Coding", + "GitHub": "GitHub", + "Gitee": "Gitee", + "GitLab": "GitLab", + "团队 ID": "团队 ID", + "项目 ID": "项目 ID", + "批量更新节点信息成功": "批量更新节点信息成功", + "批量更新节点信息失败": "批量更新节点信息失败", + "节点信息无更新": "节点信息无更新", + "批量编辑节点信息": "批量编辑节点信息", + "标签": "标签", + "(可选)": "(可选)", + "关注人": "关注人", + "管理员为必填项": "管理员为必填项", + "未选中任何节点": "未选中任何节点", + "工具进程": "工具进程", + "任务列表": "任务列表", + "批量编辑节点": "批量编辑节点", + "批量配置工具": "批量配置工具", + "子任务": "子任务", + "执行任务列表": "执行任务列表", + "已添加标签": "已添加标签", + "展示名称": "展示名称", + "展示名称为必填项": "展示名称为必填项", + "标签类型": "标签类型", + "标签类型为必选项": "标签类型为必选项", + "所属团队为必选项": "所属团队为必选项", + "标签描述": "标签描述", + "类型": "类型", + "禁用": "禁用", + "已禁用团队": "已禁用团队", + "恢复团队": "恢复团队", + "确定要恢复已禁用的团队吗?": "确定要恢复已禁用的团队吗?", + "已恢复团队": "已恢复团队", + "查看项目": "查看项目", + "禁用团队": "禁用团队", + "已禁用项目": "已禁用项目", + "恢复项目": "恢复项目", + "确定要恢复已禁用的项目吗?": "确定要恢复已禁用的项目吗?", + "已恢复项目": "已恢复项目", + "项目": "项目", + "禁用项目": "禁用项目" +} diff --git a/web/packages/tca-manage/scripts/config-webpack-plugin.js b/web/packages/tca-manage/scripts/config-webpack-plugin.js deleted file mode 100644 index 8c631e3e2..000000000 --- a/web/packages/tca-manage/scripts/config-webpack-plugin.js +++ /dev/null @@ -1,142 +0,0 @@ -const AssetsWebpackPlugin = require('assets-webpack-plugin'); -const merge = require('lodash/merge'); -const chalk = require('chalk'); - -const PLUGIN_NAME = 'ConfigWebpackPlugin'; -const PLUGIN_PREFIX = `[${PLUGIN_NAME}] `; - -function isTrue(value) { - return value === true || value === 'true'; -} - -class ConfigWebpackPlugin { - constructor(options) { - if (!options) { - options = {}; - } - const productName = this.getProductName(options); - const description = this.getDescription(options); - this.options = merge( - { - enable: process.env.CONFIG_ENABLED || true, // 默认启用 - productName, - description, - commitId: process.env.GIT_REVISION || '', - match: process.env.PRODUCT_ROUTE_MATCH, - prefix: process.env.PUBLIC_PATH || '/', - }, - options, - ); - this.isDev = (!!options.isDev) || process.env.NODE_ENV !== 'production'; - - // AssetsWebpackPlugin所需 - this.path = options.path; - this.filename = `${productName}.json`; - this.assetKeys = options.assetKeys || []; - - // 未启用 - if (!isTrue(this.options.enable)) { - console.info(chalk.yellow(PLUGIN_PREFIX) - + chalk.green('plugin is disabled, please use process.env.CONFIG_ENABLED enable it')); - return; - } - this.optionCheck(this.options); - } - - // 对启用该插件进行必要参数校验 - optionCheck(options) { - if (!options.productName) { - throw new Error(`${PLUGIN_PREFIX} 需要设置 productName。const productName = options?.productName || options?.pkgInfo?.name || process.env.PRODUCT_NAME;`); - } - - if (!options.path) { - throw new Error(`${PLUGIN_PREFIX} 需要设置 path。用于AssetsWebpackPlugin打包资源到path下`); - } - - if (!options.assetKeys) { - throw new Error(`${PLUGIN_PREFIX} 需要设置 assetKeys。用于资源获取。`); - } - - if (!options.match) { - throw new Error(`${PLUGIN_PREFIX} 需要设置 match。用于资源路由match`); - } - } - - // 获取应用名称 - getProductName(options) { - if (options) { - if (options.productName) { - return options.productName; - } - if (options.pkgInfo?.name) { - return options.pkgInfo.name; - } - } - return process.env.PRODUCT_NAME; - } - - // 获取应用描述 - getDescription(options) { - if (options?.pkgInfo?.description) { - return options.pkgInfo.description; - } - return process.env.PRODUCT_DESC || ''; - } - - apply(compiler) { - const plugins = [ - new AssetsWebpackPlugin({ - path: this.path, - filename: this.filename, - processOutput: this.processOutput.bind(this), - }), - ]; - - plugins.forEach((plugin) => { - plugin.apply(compiler); - }); - - // 在 compilation 完成时执行,dev环境将配置地址打印出来 - compiler.hooks.done.tap(PLUGIN_NAME, (stats) => { - // 用于dev,生成api.json url - if (this.isDev) { - const { https = false, host = 'localhost', port = 8080 } = stats.compilation.options.devServer; - const reg = new RegExp(/https?:\/\//); - const publicPath = this.options.prefix; - const prefix = reg.test(publicPath) ? publicPath : `http${https ? 's' : ''}://${host}:${port}${publicPath}`; - const api = `${prefix}${this.filename}`; - setTimeout(() => { - console.info(`${chalk.yellow('[dev环境]')}: ${chalk.green(`API = ${chalk.yellow(api)} , `)}`); - }, 20); - } - }); - } - - processOutput(assets) { - // 插件未启用 - if (!isTrue(this.options.enable)) { - console.info(chalk.yellow(PLUGIN_PREFIX) + chalk.green('插件未启用,跳过...')); - return ''; - } - - // 获取资源内的js和css - const js = this.assetKeys.map(k => assets[k]?.js).filter(v => !!v); - const css = this.assetKeys.map(k => assets[k]?.css).filter(v => !!v); - - // 生成config - const config = { - name: this.options.productName, - description: this.options.description, - commitId: this.options.commitId, - match: this.options.match, - js, - css, - prefix: [this.options.prefix], - }; - - console.log(chalk.yellow(PLUGIN_PREFIX) + chalk.yellow('[config]') + chalk.green(JSON.stringify(config))); - return this.options.processOutput ? this.options.processOutput(assets, config) : JSON.stringify(config); - } -} - -module.exports = ConfigWebpackPlugin; diff --git a/web/packages/tca-manage/scripts/envs.js b/web/packages/tca-manage/scripts/envs.js deleted file mode 100644 index 631918524..000000000 --- a/web/packages/tca-manage/scripts/envs.js +++ /dev/null @@ -1,21 +0,0 @@ -const envs = { - PUBLIC_PATH: process.env.PUBLIC_PATH || '/', - TITLE: process.env.TITLE, - DESCRIPTION: process.env.DESCRIPTION, - KEYWORDS: process.env.KEYWORDS, - FAVICON: process.env.FAVICON, -}; - -// 用于生成index.runtime.html 可被替换的值 -const runtimeKeys = ['TITLE', 'DESCRIPTION', 'KEYWORDS', 'FAVICON']; - -const runtimeEnvs = {}; - -runtimeKeys.forEach((key) => { - runtimeEnvs[key] = `__${key}__`; -}); - -module.exports = { - envs, - runtimeEnvs, -}; diff --git a/web/packages/tca-manage/scripts/webpack.common.js b/web/packages/tca-manage/scripts/webpack.common.js deleted file mode 100644 index b816b63f9..000000000 --- a/web/packages/tca-manage/scripts/webpack.common.js +++ /dev/null @@ -1,178 +0,0 @@ -const path = require('path'); -const webpack = require('webpack'); -const { CleanWebpackPlugin } = require('clean-webpack-plugin'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const EslintWebpackPlugin = require('eslint-webpack-plugin'); -// 日志优化 -const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin'); -const WebpackBar = require('webpackbar'); -const ConfigWebpackPlugin = require('./config-webpack-plugin'); - -const pkgInfo = require('../package.json'); - -// 根据NODE_ENV分别获取不同的config -const isDev = process.env.NODE_ENV === 'development'; -// 获取项目环境变量 -const { envs, runtimeEnvs } = require('./envs'); -// 打包目录路径 -const BASE_DIR = path.resolve(__dirname, '..'); -const buildPath = process.env.ENABLE_MANAGE === 'TRUE' ? path.resolve(BASE_DIR, 'dist-admin') : path.resolve(BASE_DIR, 'dist'); -// index.html路径 -const indexPath = path.resolve(BASE_DIR, 'public', 'index.html'); - -const htmlMinify = { - html5: true, // 根据HTML5规范解析输入 - collapseWhitespace: true, // 折叠空白区域 - preserveLineBreaks: false, - minifyCSS: true, // 压缩文内css - minifyJS: true, // 压缩文内js - removeComments: true, // 移除注释 -}; - -module.exports = { - entry: { - [pkgInfo.name]: path.resolve(BASE_DIR, 'src/index.tsx'), - }, - output: { - path: buildPath, - publicPath: process.env.PUBLIC_PATH || '/', - filename: `[name]${isDev ? '' : '-[chunkhash:8]'}.js`, - chunkFilename: `[name]${isDev ? '' : '-[chunkhash:8]'}.js`, - }, - devtool: isDev ? 'inline-source-map' : false, - target: 'web', - resolve: { - // 尝试按顺序解析这些后缀名 - extensions: ['.ts', '.tsx', '.js', '.jsx', '.svg'], - alias: { - '@src': path.resolve(BASE_DIR, 'src'), - 'react-dom': '@hot-loader/react-dom', - }, - modules: [BASE_DIR, 'node_modules'], - }, - module: { - rules: [ - { - test: /\.[jt]sx?$/i, - exclude: /node_modules/, - loader: 'babel-loader', - options: { - rootMode: 'upward', - }, - }, - // Images - { - test: /\.(?:ico|gif|png|jpg|jpeg)$/i, - type: 'asset/resource', - }, - // Fonts and SVGs - { - test: /\.(woff(2)?|eot|ttf|otf|svg|)$/i, - type: 'asset/inline', - }, - { - test: /\.css$/i, - use: [ - MiniCssExtractPlugin.loader, - 'css-loader', - ], - }, - { - test: /\.s[ac]ss$/i, - exclude: [path.resolve(BASE_DIR, 'public')], - use: [ - MiniCssExtractPlugin.loader, - // 将 CSS 转化成 CommonJS 模块 - { - loader: 'css-loader', - options: { - modules: { - localIdentName: '[local]-[hash:base64:10]', - exportLocalsConvention: 'camelCase', - }, - importLoaders: 2, - }, - }, - // 将 Sass 编译成 CSS - { - loader: 'sass-loader', - options: { - // Prefer `dart-sass` - implementation: require('sass'), - }, - }, - ], - }, - ], - }, - optimization: { - runtimeChunk: true, - splitChunks: { - chunks: 'all', - name: (module, chunks, cacheGroupKey) => { - const allChunksNames = chunks.map(item => item.name).join('~'); - return `${cacheGroupKey}~${allChunksNames}`; - }, - cacheGroups: { - vendors: { - test: /[\\/]node_modules[\\/]/, - chunks: 'all', - enforce: true, - }, - }, - }, - }, - plugins: [ - // 打包前移除/清理 打包目录 - new FriendlyErrorsWebpackPlugin(), - new CleanWebpackPlugin(), - new webpack.DefinePlugin({ - 'process.env': Object.keys(envs).reduce((e, key) => { - e[key] = JSON.stringify(envs[key]); - return e; - }, {}), - PLATFORM_ENV: JSON.stringify(process.env.PLATFORM_ENV), - ENABLE_MANAGE: JSON.stringify(process.env.ENABLE_MANAGE), - }), - new EslintWebpackPlugin({ - fix: true, - extensions: ['js', 'jsx', 'tsx', 'ts'], - }), - new MiniCssExtractPlugin({ - filename: `[name]${isDev ? '' : '-[contenthash:8]'}.css`, - chunkFilename: `[name]${isDev ? '' : '-[contenthash:8]'}.css`, - ignoreOrder: false, - }), - new HtmlWebpackPlugin({ - inject: true, - template: indexPath, - envs, - minify: htmlMinify, - }), - // 该配置生成一个 index.runtime.html 模板用于提供 index.html runtime 的能力 - new HtmlWebpackPlugin({ - inject: true, - template: indexPath, - filename: 'index.runtime.html', - minify: htmlMinify, - envs: { - ...envs, - ...runtimeEnvs, - }, - }), - new ConfigWebpackPlugin({ - pkgInfo, - isDev, - path: buildPath, - match: process.env.PRODUCT_ROUTE_MATCH || '^/manage', - assetKeys: ['runtime~tca-manage', 'vendors~tca-manage', 'tca-manage'], - }), - // 忽略第三方包指定目录,让这些指定目录不要被打包进去,对moment操作参考:https://blog.csdn.net/qq_17175013/article/details/86845624 - new webpack.IgnorePlugin({ - resourceRegExp: /^\.\/locale$/, - contextRegExp: /moment$/, - }), - new WebpackBar(), - ], -}; diff --git a/web/packages/tca-manage/scripts/webpack.dev.js b/web/packages/tca-manage/scripts/webpack.dev.js deleted file mode 100644 index 80b96ae25..000000000 --- a/web/packages/tca-manage/scripts/webpack.dev.js +++ /dev/null @@ -1,27 +0,0 @@ -const path = require('path'); -const { merge } = require('webpack-merge'); -const common = require('./webpack.common.js'); -const host = process.env.HOST || '127.0.0.1'; -const port = process.env.PORT || 5058; -const webSocketURL = `ws://${host}:${port}/ws`; -module.exports = merge(common, { - devServer: { - static: path.join(__dirname, '../dist'), - hot: true, - liveReload: false, - allowedHosts: 'all', - host, - port, - client: { - webSocketURL, - }, - historyApiFallback: true, - compress: true, - devMiddleware: { - writeToDisk: true, - }, - headers: { - 'Access-Control-Allow-Origin': '*', - }, - }, -}); diff --git a/web/packages/tca-manage/scripts/webpack.prod.js b/web/packages/tca-manage/scripts/webpack.prod.js deleted file mode 100644 index ec08f1736..000000000 --- a/web/packages/tca-manage/scripts/webpack.prod.js +++ /dev/null @@ -1,27 +0,0 @@ -const { merge } = require('webpack-merge'); -// 文件体积监控 -const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); - -const common = require('./webpack.common.js'); - -const externals = process.env.ENABLE_EXTERNALS === 'TRUE' ? { - react: 'React', - 'react-dom': 'ReactDOM', - 'react-redux': 'ReactRedux', - classnames: 'Classnames', - 'coding-oa-uikit': 'CodingOAUikit', - lodash: 'Lodash', -} : {}; - -module.exports = merge(common, { - performance: { - hints: false, - }, - plugins: [ - new BundleAnalyzerPlugin({ - analyzerMode: 'disabled', // 不启动展示打包报告的http服务器 - generateStatsFile: true, // 不打开网站,但是在dist生成stats.json文件 - }), - ], - externals, -}); diff --git a/web/packages/tca-manage/src/common/constants.ts b/web/packages/tca-manage/src/common/constants.ts deleted file mode 100644 index 2eea3625d..000000000 --- a/web/packages/tca-manage/src/common/constants.ts +++ /dev/null @@ -1,6 +0,0 @@ -// 分页默认值 -export const DEFAULT_PAGER = { - count: 0, - pageSize: 10, - pageStart: 0, -}; diff --git a/web/packages/tca-manage/src/common/style/color.scss b/web/packages/tca-manage/src/common/style/color.scss deleted file mode 100644 index 7a8c3ddbe..000000000 --- a/web/packages/tca-manage/src/common/style/color.scss +++ /dev/null @@ -1,75 +0,0 @@ -$grey-1: #f5f7fa; -$grey-2: #e6e9ed; -$grey-3: #dadfe6; -$grey-4: #c5cedb; -$grey-5: #adbacc; -$grey-6: #8592a6; -$grey-7: #606c80; -$grey-8: #202d40; -$grey-9: #171d26; - -$blue-1: #e6f7ff; -$blue-2: #cceaff; -$blue-3: #8cc6ff; -$blue-4: #3d98ff; -$blue-5: #06f; -$blue-6: #0052cc; -$blue-7: #0f4799; -$blue-8: #1d4073; - -$red-1: #fff0f0; -$red-2: #fed2d2; -$red-3: #fc9c9c; -$red-4: #f76469; -$red-5: #eb333f; -$red-6: #ca1628; -$red-7: #9a1325; -$red-8: #791122; - -$orange-1: #fff4e0; -$orange-2: #ffe0b3; -$orange-3: #fec57c; -$orange-4: #fba337; -$orange-5: #f0850a; -$orange-6: #cb6b0b; -$orange-7: #9e540a; -$orange-8: #7b4209; - -$yellow-1: #fffbe5; -$yellow-2: #fff4c2; -$yellow-3: #fde586; -$yellow-4: #fbd341; -$yellow-5: #edb807; -$yellow-6: #ce9c09; -$yellow-7: #a07708; -$yellow-8: #725409; - -$green-1: #f2ffe0; -$green-2: #dbfdb5; -$green-3: #adf269; -$green-4: #7ad93a; -$green-5: #4fbe0e; -$green-6: #3ea00e; -$green-7: #2e7d0c; -$green-8: #205c0a; - -$cyan-1: #e5feff; -$cyan-2: #c4fafd; -$cyan-3: #8ef3fa; -$cyan-4: #3de6f5; -$cyan-5: #0ccadf; -$cyan-6: #0ba4bc; -$cyan-7: #097c90; -$cyan-8: #096072; - -$purple-1: #f4e5ff; -$purple-2: #dbb5fd; -$purple-3: #c28bf9; -$purple-4: #a657f4; -$purple-5: #892aef; -$purple-6: #6812ca; -$purple-7: #4b109e; -$purple-8: #380d77; - -$black: #000000; -$white: #ffffff; diff --git a/web/packages/tca-manage/src/common/style/index.scss b/web/packages/tca-manage/src/common/style/index.scss deleted file mode 100644 index 8b4288d98..000000000 --- a/web/packages/tca-manage/src/common/style/index.scss +++ /dev/null @@ -1,687 +0,0 @@ -@import './color.scss'; -@import './font.scss'; -// nickctang 添加的公共样式 -.text-left { - text-align: left; -} - -.text-right { - text-align: right; -} - -.text-center { - text-align: center; -} - -$space-base: 16px !default; -$space-x-base: $space-base !default; -$space-y-base: $space-base !default; - -$space-none: ( - x: 0, - y: 0, -) !default; -$space-xs: ( - x: ( - $space-x-base * 0.25, - ), - y: ( - $space-y-base * 0.25, - ), -) !default; -$space-sm: ( - x: ( - $space-x-base * 0.5, - ), - y: ( - $space-y-base * 0.5, - ), -) !default; -$space-md: ( - x: $space-x-base, - y: $space-y-base, -) !default; -$space-lg: ( - x: ( - $space-x-base * 1.5, - ), - y: ( - $space-y-base * 1.5, - ), -) !default; -$space-xl: ( - x: ( - $space-x-base * 3, - ), - y: ( - $space-y-base * 3, - ), -) !default; -$space-12: ( - x: 12px, - y: 12px, -) !default; -$space-20: ( - x: 20px, - y: 20px, -) !default; -$space-48: ( - x: 48px, - y: 48px, -) !default; -$space-6: ( - x: 6px, - y: 6px, -) !default; - -// sorry for long line; we need .sass and it doesn't support multi-line list -$spaces: ( - 'none': $space-none, - 'xs': $space-xs, - '6': $space-6, - 'sm': $space-sm, - 'md': $space-md, - 'lg': $space-lg, - 'xl': $space-xl, - '12': $space-12, - '20': $space-20, - '48': $space-48, -) !default; - -@each $space, $value in $spaces { - .pa-#{$space} { - padding: map-get($value, 'y') map-get($value, 'x'); - } - - .pl-#{$space} { - padding-left: map-get($value, 'x'); - } - - .pr-#{$space} { - padding-right: map-get($value, 'x'); - } - - .pt-#{$space} { - padding-top: map-get($value, 'y'); - } - - .pb-#{$space} { - padding-bottom: map-get($value, 'y'); - } - - .px-#{$space} { - padding-left: map-get($value, 'x'); - padding-right: map-get($value, 'x'); - } - - .py-#{$space} { - padding-top: map-get($value, 'y'); - padding-bottom: map-get($value, 'y'); - } - - .ma-#{$space} { - margin: map-get($value, 'y') map-get($value, 'x'); - } - - .ml-#{$space} { - margin-left: map-get($value, 'x'); - } - - .mr-#{$space} { - margin-right: map-get($value, 'x'); - } - - .mt-#{$space} { - margin-top: map-get($value, 'y'); - } - - .mb-#{$space} { - margin-bottom: map-get($value, 'y'); - } - - .mx-#{$space} { - margin-left: map-get($value, 'x'); - margin-right: map-get($value, 'x'); - } - - .my-#{$space} { - margin-top: map-get($value, 'y'); - margin-bottom: map-get($value, 'y'); - } -} - -.ml-auto { - margin-left: auto; -} - -.mr-auto { - margin-right: auto; -} - -.mx-auto { - margin-left: auto; - margin-right: auto; -} - -.block { - display: block !important; -} - -.inline-block { - display: inline-block !important; -} - -.fit { - width: 100% !important; - height: 100% !important; -} - -.full-height { - height: 100% !important; -} - -.full-width { - width: 100% !important; - margin-left: 0 !important; - margin-right: 0 !important; -} - -// visibility -.no-margin { - margin: 0 !important; -} - -.no-padding { - padding: 0 !important; -} - -.no-border { - border: 0 !important; -} - -.no-border-radius { - border-radius: 0 !important; -} - -.no-box-shadow { - box-shadow: none !important; -} - -.no-outline { - outline: 0 !important; -} - -.ellipsis { - text-overflow: ellipsis; - - white-space: nowrap; - - overflow: hidden; - - &-2-lines, - &-3-lines { - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - } - - &-2-lines { - -webkit-line-clamp: 2; - } - - &-3-lines { - -webkit-line-clamp: 3; - } -} - -.hidden { - display: none !important; -} - -.invisible { - visibility: hidden !important; -} - -.transparent { - background: transparent !important; -} - -.overflow-auto { - overflow: auto !important; -} - -.overflow-hidden { - overflow: hidden !important; -} - -.overflow-hidden-y { - overflow-y: hidden !important; -} - -$z-top: 7000 !default; -$z-max: 9998 !default; - -.z-top { - z-index: $z-top !important; -} - -.z-max { - z-index: $z-max !important; -} - -// mouse -.non-selectable { - user-select: none !important; -} - -.scroll { - overflow: auto; -} - -.scroll, -.scroll-x, -.scroll-y { - -webkit-overflow-scrolling: touch; - will-change: scroll-position; -} - -.scroll-x { - overflow-x: auto; -} - -.scroll-y { - overflow-y: auto; -} - -.no-scroll { - overflow: hidden !important; -} - -.no-pointer-events, -.no-pointer-events--children, -.no-pointer-events--children * { - pointer-events: none !important; -} - -.all-pointer-events { - pointer-events: all !important; -} - -.cursor { - &-pointer { - cursor: pointer !important; - } - - &-not-allowed { - cursor: not-allowed !important; - } - - &-inherit { - cursor: inherit !important; - } - - &-none { - cursor: none !important; - } -} - -// position -.float-left { - float: left; -} - -.float-right { - float: right; -} - -.relative-position { - position: relative; -} - -.absolute, -.absolute-full, -.absolute-center, -.absolute-bottom, -.absolute-left, -.absolute-right, -.absolute-top, -.absolute-top-left, -.absolute-top-right, -.absolute-bottom-left, -.absolute-bottom-right { - position: absolute; -} - -.fixed, -.fixed-full, -.fullscreen, -.fixed-center, -.fixed-bottom, -.fixed-left, -.fixed-right, -.fixed-top, -.fixed-top-left, -.fixed-top-right, -.fixed-bottom-left, -.fixed-bottom-right { - position: fixed; -} - -.fixed-center, -.absolute-center { - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.fixed-top, -.absolute-top { - top: 0; - left: 0; - right: 0; -} - -.fixed-right, -.absolute-right { - top: 0; - right: 0; - bottom: 0; -} - -.fixed-bottom, -.absolute-bottom { - right: 0; - bottom: 0; - left: 0; -} - -.fixed-left, -.absolute-left { - top: 0; - bottom: 0; - left: 0; -} - -.fixed-top-left, -.absolute-top-left { - top: 0; - left: 0; -} - -.fixed-top-right, -.absolute-top-right { - top: 0; - right: 0; -} - -.fixed-bottom-left, -.absolute-bottom-left { - bottom: 0; - left: 0; -} - -.fixed-bottom-right, -.absolute-bottom-right { - bottom: 0; - right: 0; -} - -.vertical- { - &top { - vertical-align: top !important; - } - - &middle { - vertical-align: middle !important; - } - - &bottom { - vertical-align: bottom !important; - } -} - -.on-left { - margin-right: 12px; -} - -.on-right { - margin-left: 12px; -} - -.text-black { - color: $black; -} - -.text-white { - color: $white; -} - -.bg-white { - background-color: $white; -} - -// grey -.text-grey-9 { - color: $grey-9; -} -.text-grey-8 { - color: $grey-8; -} -.text-grey-7 { - color: $grey-7; -} -.text-grey-6 { - color: $grey-6; -} -.text-grey-5 { - color: $grey-5; -} -.text-grey-4 { - color: $grey-4; -} -.text-grey-3 { - color: $grey-3; -} -.text-grey-2 { - color: $grey-2; -} -.text-grey-1 { - color: $grey-1; -} - -// blue -.text-blue-8 { - color: $blue-8; -} -.text-blue-7 { - color: $blue-7; -} -.text-blue-6 { - color: $blue-6; -} -.text-blue-5 { - color: $blue-5; -} -.text-blue-4 { - color: $blue-4; -} -.text-blue-3 { - color: $blue-3; -} -.text-blue-2 { - color: $blue-2; -} -.text-blue-1 { - color: $blue-1; -} - -// red -.text-red-8 { - color: $red-8; -} -.text-red-7 { - color: $red-7; -} -.text-red-6 { - color: $red-6; -} -.text-red-5 { - color: $red-5; -} -.text-red-4 { - color: $red-4; -} -.text-red-3 { - color: $red-3; -} -.text-red-2 { - color: $red-2; -} -.text-red-1 { - color: $red-1; -} - -// orange -.text-orange-8 { - color: $orange-8; -} -.text-orange-7 { - color: $orange-7; -} -.text-orange-6 { - color: $orange-6; -} -.text-orange-5 { - color: $orange-5; -} -.text-orange-4 { - color: $orange-4; -} -.text-orange-3 { - color: $orange-3; -} -.text-orange-2 { - color: $orange-2; -} -.text-orange-1 { - color: $orange-1; -} - -// yellow -.text-yellow-8 { - color: $yellow-8; -} -.text-yellow-7 { - color: $yellow-7; -} -.text-yellow-6 { - color: $yellow-6; -} -.text-yellow-5 { - color: $yellow-5; -} -.text-yellow-4 { - color: $yellow-4; -} -.text-yellow-3 { - color: $yellow-3; -} -.text-yellow-2 { - color: $yellow-2; -} -.text-yellow-1 { - color: $yellow-1; -} - -// green -.text-green-8 { - color: $green-8; -} -.text-green-7 { - color: $green-7; -} -.text-green-6 { - color: $green-6; -} -.text-green-5 { - color: $green-5; -} -.text-green-4 { - color: $green-4; -} -.text-green-3 { - color: $green-3; -} -.text-green-2 { - color: $green-2; -} -.text-green-1 { - color: $green-1; -} - -// cyan -.text-cyan-8 { - color: $cyan-8; -} -.text-cyan-7 { - color: $cyan-7; -} -.text-cyan-6 { - color: $cyan-6; -} -.text-cyan-5 { - color: $cyan-5; -} -.text-cyan-4 { - color: $cyan-4; -} -.text-cyan-3 { - color: $cyan-3; -} -.text-cyan-2 { - color: $cyan-2; -} -.text-cyan-1 { - color: $cyan-1; -} - -// purple -.text-purple-8 { - color: $purple-8; -} -.text-purple-7 { - color: $purple-7; -} -.text-purple-6 { - color: $purple-6; -} -.text-purple-5 { - color: $purple-5; -} -.text-purple-4 { - color: $purple-4; -} -.text-purple-3 { - color: $purple-3; -} -.text-purple-2 { - color: $purple-2; -} -.text-purple-1 { - color: $purple-1; -} - -$text-weights: ( - thin: 100, - light: 300, - regular: 400, - medium: 500, - bold: 700, - bolder: 900, -) !default; - -@each $weight, $value in $text-weights { - .text-weight-#{$weight} { - font-weight: $value; - } -} diff --git a/web/packages/tca-manage/src/common/style/uikit.scss b/web/packages/tca-manage/src/common/style/uikit.scss deleted file mode 100644 index bfd357568..000000000 --- a/web/packages/tca-manage/src/common/style/uikit.scss +++ /dev/null @@ -1,52 +0,0 @@ -@import "./color.scss"; - -:global(.ant-tabs-nav) { - margin-bottom: 0 !important; - - &::before { - border-bottom-color: $grey-3 !important; - } -} - -:global(.ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn) { - font-weight: 600; - color: $grey-8; -} - -:global(.ant-tabs-tab) { - padding: 10px; - margin-right: 24px; - font-size: 16px; - transition: all 0.3s; - - &:hover { - color: $grey-8; - font-weight: 600; - } -} - -:global(.ant-tabs-tab-btn) { - &:active, - &:focus { - color: $grey-8; - font-weight: 600; - } -} - -:global(.ant-avatar) { - background: $grey-5; -} - -:global(.ant-table-tbody) { - & > tr { - td > a.link-name { - color: $grey-8; - } - - &:hover { - td > a.link-name { - color: $blue-5 !important; - } - } - } -} diff --git a/web/packages/tca-manage/src/components/delete-modal/index.tsx b/web/packages/tca-manage/src/components/delete-modal/index.tsx deleted file mode 100644 index 8cfb8ea1c..000000000 --- a/web/packages/tca-manage/src/components/delete-modal/index.tsx +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -/** - * 确认删除操作弹框 - */ - - import React, { useEffect, useState } from 'react'; - import { Modal, Form, Input, message, Button } from 'coding-oa-uikit'; - import { t } from '@src/i18n/i18next'; - - import s from './style.scss'; - - interface DeleteModalProps { - actionType: string; - objectType: string; - confirmName: string; - visible: boolean; - addtionInfo?: string; - onCancel: () => void; - onOk: () => void; - } - - const DeleteModal = ({ actionType, objectType, confirmName, addtionInfo='', visible, onCancel, onOk }: DeleteModalProps) => { - const [form] = Form.useForm(); - const [confirmed, setConfirmed] = useState(true); - - useEffect(() => { - visible && form.resetFields(); - visible && setConfirmed(false); - }, [visible]); - - /** - * 表单提交操作 - * @param formData 参数 - */ - const onSubmitHandle = () => { - form.validateFields().then((formData) => { - if (formData?.confirm === confirmName) { - onOk(); - } else { - message.error(t('验证失败,请重新输入')); - } - }); - }; - - const checkConfirm = (changedValues: any) => { - if (changedValues?.confirm === confirmName) { - setConfirmed(true); - } else { - setConfirmed(false); - } - }; - - return ( - - {t('确认')}{actionType} - , - , - ]} - > -

- {t('您正在')}{actionType}{objectType} {confirmName}{' '}
-

- {addtionInfo &&

{addtionInfo}

} -

{t(`为确认${actionType}操作,请输入您要${actionType}的`)}{objectType}

-
- - - -
-
- ); - }; - - export default DeleteModal; - \ No newline at end of file diff --git a/web/packages/tca-manage/src/components/filter/FilterItem.tsx b/web/packages/tca-manage/src/components/filter/FilterItem.tsx deleted file mode 100644 index 12011ad14..000000000 --- a/web/packages/tca-manage/src/components/filter/FilterItem.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; -import cn from 'classnames'; -import { Form } from 'coding-oa-uikit'; - -interface ItemProps { - children: React.ReactNode; - label?: string; - className?: any; -} - -const Item = (props: ItemProps & any) => { - const { children, label, className, ...otherProps } = props; - return ( - - {children} - - ); -}; - -export default Item; diff --git a/web/packages/tca-manage/src/components/filter/index.tsx b/web/packages/tca-manage/src/components/filter/index.tsx deleted file mode 100644 index 304bce65a..000000000 --- a/web/packages/tca-manage/src/components/filter/index.tsx +++ /dev/null @@ -1,28 +0,0 @@ -/** - * 过滤筛选组件 - */ - -import React from 'react'; -import { Form } from 'coding-oa-uikit'; - -import Item from './FilterItem'; - -interface FilterProps { - children: React.ReactNode -} - -const Filter = (props: FilterProps & any) => { - const { children, ...otherProps } = props; - return ( -
- {children} -
- ); -}; - -Filter.Item = Item; - -export default Filter; diff --git a/web/packages/tca-manage/src/components/loading/index.tsx b/web/packages/tca-manage/src/components/loading/index.tsx deleted file mode 100644 index 5f00b2596..000000000 --- a/web/packages/tca-manage/src/components/loading/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -/** - * 加载中组件 - */ - -import React from 'react'; -import LoadingIcon from 'coding-oa-uikit/lib/icon/Loading'; - -import style from './style.scss'; - -const Loading = () => ( -
- - 加载中 -
-); - -export default Loading; diff --git a/web/packages/tca-manage/src/constant/common.ts b/web/packages/tca-manage/src/constant/common.ts new file mode 100644 index 000000000..e69de29bb diff --git a/web/packages/tca-manage/src/constant/index.ts b/web/packages/tca-manage/src/constant/index.ts new file mode 100644 index 000000000..5e1074aa2 --- /dev/null +++ b/web/packages/tca-manage/src/constant/index.ts @@ -0,0 +1,3 @@ +// export * from './common'; +export * from './tool'; +export * from './pkg'; diff --git a/web/packages/tca-manage/src/constant/pkg.ts b/web/packages/tca-manage/src/constant/pkg.ts new file mode 100644 index 000000000..56b3d0607 --- /dev/null +++ b/web/packages/tca-manage/src/constant/pkg.ts @@ -0,0 +1,20 @@ +export enum PkgStatusEnum { + RUNNING = 1, + TESTING, + HIDDEN, + DISABLED = 9, +} + +export const PKG_STATUS_CHOICES = { + [PkgStatusEnum.RUNNING]: '正常运营', + [PkgStatusEnum.TESTING]: '测试中', + [PkgStatusEnum.HIDDEN]: '隐藏中', + [PkgStatusEnum.DISABLED]: '已禁用', +}; + +export const PKG_STATUS_OPTIONS = [ + PkgStatusEnum.RUNNING, + PkgStatusEnum.TESTING, + PkgStatusEnum.HIDDEN, + PkgStatusEnum.DISABLED, +]; diff --git a/web/packages/tca-manage/src/constant/scheme.ts b/web/packages/tca-manage/src/constant/scheme.ts new file mode 100644 index 000000000..28614b00a --- /dev/null +++ b/web/packages/tca-manage/src/constant/scheme.ts @@ -0,0 +1,13 @@ +export enum PkgStatusEnum { + RUNNING = 1, + TESTING, + HIDDEN, + DISABLED = 9, +} + +export const PKG_STATUS_CHOICES = { + [PkgStatusEnum.RUNNING]: '正常运营', + [PkgStatusEnum.TESTING]: '测试中', + [PkgStatusEnum.HIDDEN]: '隐藏中', + [PkgStatusEnum.DISABLED]: '已禁用', +}; diff --git a/web/packages/tca-manage/src/constant/tool.ts b/web/packages/tca-manage/src/constant/tool.ts new file mode 100644 index 000000000..11149f725 --- /dev/null +++ b/web/packages/tca-manage/src/constant/tool.ts @@ -0,0 +1,100 @@ +import invert from 'lodash/invert'; +import { generateOptions } from '@tencent/micro-frontend-shared/util'; + +export enum RuleEditParamEnum { + SEVERITY, + STATUS, +} + +export enum RuleSeverityEnum { + FATAL = 1, + ERROR, + WARNING, + INFO, +} + +export const RuleSeverityInvertEnum = invert(RuleSeverityEnum); + +export const RULE_SEVERITY_CHOICES = { + [RuleSeverityEnum.FATAL]: '致命', + [RuleSeverityEnum.ERROR]: '错误', + [RuleSeverityEnum.WARNING]: '警告', + [RuleSeverityEnum.INFO]: '提示', +}; + +export const RULE_SEVERITY_OPTIONS = generateOptions(RULE_SEVERITY_CHOICES, true); + +export const RULE_STATE_OPTIONS = [ + { + label: '活跃', + value: false, + }, + { + label: '失效', + value: true, + }, +]; + +export enum PkgRuleStateEnum { + ENABLE = 1, + DISABLE, +} + +export const PkgRuleStateInvertEnum = invert(PkgRuleStateEnum); + +export const PKG_RULE_STATE_CHOICES = { + [PkgRuleStateEnum.ENABLE]: '启用', + [PkgRuleStateEnum.DISABLE]: '屏蔽', +}; + +export const PKG_RULE_STATE_OPTIONS = generateOptions(PKG_RULE_STATE_CHOICES, true); + +export enum RuleCategoryEnum { + CORRECTNESS = 1, + SECURITY, + PERFORMANCE, + USABILITY, + ACCESSIBILITY, + I18N, + CONVENTION, + OTHER, +} + +export const RULE_CATEGORY_CHOICES = { + [RuleCategoryEnum.CORRECTNESS]: '功能', + [RuleCategoryEnum.SECURITY]: '安全', + [RuleCategoryEnum.PERFORMANCE]: '性能', + [RuleCategoryEnum.USABILITY]: '可用性', + [RuleCategoryEnum.ACCESSIBILITY]: '无障碍化', + [RuleCategoryEnum.I18N]: '国际化', + [RuleCategoryEnum.CONVENTION]: '风格', + [RuleCategoryEnum.OTHER]: '其他', +}; + +export const RULE_CATEGORY_OPTIONS = generateOptions(RULE_CATEGORY_CHOICES); + +export enum ToolLibTypeEnum { + PRIVATE = 'private', + PUBLIC = 'public' +} + +export const TOOLLIB_TYPE_CHOICES = { + [ToolLibTypeEnum.PRIVATE]: '私有', + [ToolLibTypeEnum.PUBLIC]: '公共', +}; + +export enum ToolStatusEnum { + RUNNING = 0, + SUSPENDING, + DISABLE, + TRIAL, +} + +export const TOOL_STATUS_CHOICES = { + [ToolStatusEnum.RUNNING]: '正常运营', + [ToolStatusEnum.SUSPENDING]: '暂停使用', + [ToolStatusEnum.DISABLE]: '已下架', + [ToolStatusEnum.TRIAL]: '体验运营', +}; + +export const TOOL_STATUS_OPTIONS = generateOptions(TOOL_STATUS_CHOICES); diff --git a/web/packages/tca-manage/src/context/store.tsx b/web/packages/tca-manage/src/context/store.tsx deleted file mode 100644 index 5efc483e0..000000000 --- a/web/packages/tca-manage/src/context/store.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React, { createContext, useReducer, useContext } from 'react'; -import { noop } from 'lodash'; - -interface ActionProps { - type: string; - payload: any; -} - -const initialState = {}; - -const StateContext = createContext<{}>(initialState); -const DispatchContext = createContext(noop); - -const reducer = (state: {}, action: ActionProps) => { - switch (action.type) { - default: { - return state; - } - } -}; - -const StoreProvider = ({ children }: { children: any }) => { - const [state, dispatch] = useReducer(reducer, initialState); - - return ( - - {children} - - ); -}; - -const useStateStore = () => useContext(StateContext); -const useDispatchStore = () => useContext(DispatchContext); - -export { StoreProvider, useStateStore, useDispatchStore }; diff --git a/web/packages/tca-manage/src/i18n/i18next.ts b/web/packages/tca-manage/src/i18n/i18next.ts deleted file mode 100644 index 1388ab50a..000000000 --- a/web/packages/tca-manage/src/i18n/i18next.ts +++ /dev/null @@ -1,7 +0,0 @@ -import i18next from 'i18next'; - -const t = i18next.t.bind(i18next); - -export { t }; - -export default i18next; diff --git a/web/packages/tca-manage/src/i18n/index.ts b/web/packages/tca-manage/src/i18n/index.ts deleted file mode 100644 index 077a2b1fe..000000000 --- a/web/packages/tca-manage/src/i18n/index.ts +++ /dev/null @@ -1,39 +0,0 @@ -// 请确保 i18n 初始化文件最先加载 -import i18next from 'i18next'; -import { initReactI18next } from 'react-i18next'; -import Cookie from 'universal-cookie'; - -const cookie = new Cookie(); -const language = cookie.get('language') || 'zh_CN'; - -let translation = {}; - -try { - // eslint-disable-next-line @typescript-eslint/no-require-imports - translation = require(`./locales/${language}/translation.json`); -} catch (e) { - translation = {}; -} - -const resources = { - zh_CN: { - translation, - }, - en_US: { - translation, - }, -}; - -i18next.use(initReactI18next).init({ - lng: 'zh_CN', - keySeparator: false, - nsSeparator: false, - interpolation: { - escapeValue: false, - }, - resources, -}); - -export const isEnglish = () => language === 'en_US'; - -export default i18next; diff --git a/web/packages/tca-manage/src/i18n/locales/en_US/translation.json b/web/packages/tca-manage/src/i18n/locales/en_US/translation.json deleted file mode 100644 index 9e26dfeeb..000000000 --- a/web/packages/tca-manage/src/i18n/locales/en_US/translation.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/web/packages/tca-manage/src/i18n/locales/zh_CN/translation.json b/web/packages/tca-manage/src/i18n/locales/zh_CN/translation.json deleted file mode 100644 index 9e26dfeeb..000000000 --- a/web/packages/tca-manage/src/i18n/locales/zh_CN/translation.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/web/packages/tca-manage/src/i18n/locales/zh_TW/translation.json b/web/packages/tca-manage/src/i18n/locales/zh_TW/translation.json deleted file mode 100644 index 9e26dfeeb..000000000 --- a/web/packages/tca-manage/src/i18n/locales/zh_TW/translation.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/web/packages/tca-manage/src/index.scss b/web/packages/tca-manage/src/index.scss deleted file mode 100644 index 02953e6b3..000000000 --- a/web/packages/tca-manage/src/index.scss +++ /dev/null @@ -1,26 +0,0 @@ -:global { - @import "@src/common/style/index.scss"; - @import "@src/common/style/uikit.scss"; - @media only screen and (min-width: 1080px) { - html, - body, - #container { - min-width: 1080px; - overflow: auto; - } - button, - p { - padding: 0; - margin: 0; - } - - .a-no-style, - .a-no-style:link, - .a-no-style:visited, - .a-no-style:hover, - .a-no-style:active { - text-decoration: none !important; - color: inherit !important; - } - } -} diff --git a/web/packages/tca-manage/src/index.tsx b/web/packages/tca-manage/src/index.tsx index 430c9fd20..bc4d6634a 100644 --- a/web/packages/tca-manage/src/index.tsx +++ b/web/packages/tca-manage/src/index.tsx @@ -1,78 +1,21 @@ -import './i18n'; -import './index.scss'; -import 'coding-oa-uikit/dist/coding-oa-uikit.css'; -import pkg from '../package.json'; - - import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { Provider } from 'react-redux'; -import { createStore, combineReducers } from 'redux'; -import { ConfigProvider } from 'coding-oa-uikit'; -import Cookie from 'universal-cookie'; -import { StoreProvider } from './context/store'; -import MicroLayout from './micro-layout'; +import MicroInit from '@tencent/micro-frontend-shared/tdesign-component/micro-init'; +import MicroLayout from '@tencent/micro-frontend-shared/tdesign-component/micro-layout'; +import initI18next from '@tencent/micro-frontend-shared/i18n'; +import { initReactI18next } from 'react-i18next'; +// 项目内 import Root from './root'; +import pkg from '../package.json'; -const ID = 'container'; -const cookie = new Cookie(); -const language = cookie.get('language') || 'zh_CN'; - -let locale: any = ''; -try { - locale = require(`coding-oa-uikit/lib/locale/${language}.js`); -} catch (e) { - locale = require('coding-oa-uikit/lib/locale/zh_CN.js'); -} - -const create = (rootDom: HTMLElement) => { - const e = document.getElementById(ID); - if (rootDom && !e) { - const container = document.createElement('div'); - container.id = ID; - rootDom.appendChild(container); - } -}; - -function bootstrap() { } - -const mount = (props: any) => { - const { rootDom, store } = props; - create(rootDom); - render( - - - (node ? node.parentNode : document.body)} - > - - - - - - , - document.getElementById(ID), - ); -}; - -const unmount = () => { - const e = document.getElementById(ID); - if (e) { - unmountComponentAtNode(e); - } -}; - -if (typeof window.microHook === 'object') { - window.microHook.registerApp(pkg.name, { - bootstrap, - mount, - unmount, - }); -} else { - const store = createStore(combineReducers({})); - mount({ store }); -} +// 初始化i18n +initI18next({ modules: [initReactI18next] }); + +MicroInit({ + id: 'container', + name: pkg.name, + container: + + + , +}); diff --git a/web/packages/tca-manage/src/modules/components/node-tag/constant.tsx b/web/packages/tca-manage/src/modules/components/node-tag/constant.tsx new file mode 100644 index 000000000..a01abe04b --- /dev/null +++ b/web/packages/tca-manage/src/modules/components/node-tag/constant.tsx @@ -0,0 +1,13 @@ +import { t } from '@tencent/micro-frontend-shared/i18n'; + +export const TAG_TYPE_ENUM = { + PUBLIC: 1, + PRIVATE: 2, + DISABLED: 99, +}; + +export const TAG_TYPE_CHOICES = { + [TAG_TYPE_ENUM.PUBLIC]: t('公共'), + [TAG_TYPE_ENUM.PRIVATE]: t('团队'), + [TAG_TYPE_ENUM.DISABLED]: t('停用'), +}; diff --git a/web/packages/tca-manage/src/modules/components/node-tag/index.tsx b/web/packages/tca-manage/src/modules/components/node-tag/index.tsx new file mode 100644 index 000000000..1976c5e87 --- /dev/null +++ b/web/packages/tca-manage/src/modules/components/node-tag/index.tsx @@ -0,0 +1,35 @@ +/** + * 节点标签组件 + */ +import React from 'react'; +import { Tag } from 'tdesign-react'; + +import { TAG_TYPE_ENUM } from './constant'; +import s from './style.scss'; + +interface NodeTagProps { + /** 节点标签信息 */ + tag: any +} + +/** 节点标签组件 */ +const NodeTag = ({ tag }: NodeTagProps) => { + switch (tag?.tag_type) { + case TAG_TYPE_ENUM.PUBLIC: + return + {tag.display_name || tag.name} + ; + case TAG_TYPE_ENUM.PRIVATE: + return + {tag.display_name || tag.name} + ; + case TAG_TYPE_ENUM.DISABLED: + return {tag.display_name || tag.name}; + default: + return + {tag.display_name || tag.name} + ; + } +}; + +export default NodeTag; diff --git a/web/packages/tca-manage/src/modules/components/node-tag/style.scss b/web/packages/tca-manage/src/modules/components/node-tag/style.scss new file mode 100644 index 000000000..d3afb6c2e --- /dev/null +++ b/web/packages/tca-manage/src/modules/components/node-tag/style.scss @@ -0,0 +1,11 @@ +@import "@tencent/micro-frontend-shared/style/color.scss"; + +.private-tag { + color: $cyan-6; + background-color: $cyan-1; +} + +.public-tag { + color: $blue-6; + background-color: $blue-1; +} diff --git a/web/packages/tca-manage/src/modules/components/node-tag/tag-type.tsx b/web/packages/tca-manage/src/modules/components/node-tag/tag-type.tsx new file mode 100644 index 000000000..b54c02a86 --- /dev/null +++ b/web/packages/tca-manage/src/modules/components/node-tag/tag-type.tsx @@ -0,0 +1,35 @@ +/** + * 节点标签类别组件 + */ +import React from 'react'; +import { Tag } from 'tdesign-react'; + +import { TAG_TYPE_ENUM, TAG_TYPE_CHOICES } from './constant'; +import s from './style.scss'; + +interface TagTypeProps { + /** 节点标签信息 */ + tag_type: number +} + +/** 节点标签类别 */ +const TagType = ({ tag_type }: TagTypeProps) => { + switch (tag_type) { + case TAG_TYPE_ENUM.PUBLIC: + return + {TAG_TYPE_CHOICES[TAG_TYPE_ENUM.PUBLIC]} + ; + case TAG_TYPE_ENUM.PRIVATE: + return + {TAG_TYPE_CHOICES[TAG_TYPE_ENUM.PRIVATE]} + ; + case TAG_TYPE_ENUM.DISABLED: + return {TAG_TYPE_CHOICES[TAG_TYPE_ENUM.DISABLED]}; + default: + return + {TAG_TYPE_CHOICES[TAG_TYPE_ENUM.PUBLIC]} + ; + } +}; + +export default TagType; diff --git a/web/packages/tca-manage/src/modules/components/org-info.tsx b/web/packages/tca-manage/src/modules/components/org-info.tsx deleted file mode 100644 index dd8028623..000000000 --- a/web/packages/tca-manage/src/modules/components/org-info.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; -import { Tooltip, Row, Col } from 'coding-oa-uikit'; -import InfoCircle from 'coding-oa-uikit/lib/icon/InfoCircle'; - -// 项目内 -import EllipsisTemplate from '@src/components/ellipsis'; -import { formatDateTime } from '@src/utils'; - -const LEVEL_ENUM = { - NORMAL: 1, - VIP: 2, - SUPER_VIP: 3, -}; - -const LEVEL_CHOICES = { - [LEVEL_ENUM.NORMAL]: '普通团队', - [LEVEL_ENUM.VIP]: 'VIP 团队', - [LEVEL_ENUM.SUPER_VIP]: '超级 VIP 团队', -}; - -interface IProps { - org: any; - maxWidth?: number; -} -const OrgInfo = ({ org, maxWidth }: IProps) => ( - - - {org.name} - - - -
团队名称:{org.name}
-
负责人:{org.owner}
-
联系方式:{org.tel_number}
-
团队级别:{LEVEL_CHOICES[org.level]}
-
创建时间:{formatDateTime(org.created_time)}
-
- } - > - - - - -); - -export default OrgInfo; diff --git a/web/packages/tca-manage/src/modules/components/org-team-info.tsx b/web/packages/tca-manage/src/modules/components/org-team-info.tsx index 320d8000b..05fb7e956 100644 --- a/web/packages/tca-manage/src/modules/components/org-team-info.tsx +++ b/web/packages/tca-manage/src/modules/components/org-team-info.tsx @@ -1,15 +1,15 @@ import React from 'react'; +import EllipsisTemplate from '@tencent/micro-frontend-shared/tdesign-component/ellipsis'; // 项目内 -import EllipsisTemplate from '@src/components/ellipsis'; -import OrgInfo from '@src/modules/components/org-info'; +import OrgInfo from '@plat/modules/components/org-info'; -interface IProps { +interface OrgAndTeamInfoProps { org: any; team: any; maxWidth?: number; } -const OrgAndTeamInfo = ({ org, team, maxWidth }: IProps) => { +const OrgAndTeamInfo = ({ org, team, maxWidth }: OrgAndTeamInfoProps) => { if (!org || !team) { return <>- -; } diff --git a/web/packages/tca-manage/src/modules/components/org-team-project-info.tsx b/web/packages/tca-manage/src/modules/components/org-team-project-info.tsx index a336aaa42..712647a4f 100644 --- a/web/packages/tca-manage/src/modules/components/org-team-project-info.tsx +++ b/web/packages/tca-manage/src/modules/components/org-team-project-info.tsx @@ -1,16 +1,16 @@ import React from 'react'; // 项目内 -import EllipsisTemplate from '@src/components/ellipsis'; -import OrgInfo from '@src/modules/components/org-info'; +import EllipsisTemplate from '@tencent/micro-frontend-shared/tdesign-component/ellipsis'; +import OrgInfo from '@plat/modules/components/org-info'; -interface IProps { +interface OrgAndTeamInfoProps { org: any; team: any; project: any; maxWidth?: number; } -const OrgAndTeamInfo = ({ org, team, project, maxWidth }: IProps) => ( +const OrgAndTeamInfo = ({ org, team, project, maxWidth }: OrgAndTeamInfoProps) => ( <> diff --git a/web/packages/tca-manage/src/modules/components/rule-table.tsx b/web/packages/tca-manage/src/modules/components/rule-table.tsx new file mode 100644 index 000000000..98c02128e --- /dev/null +++ b/web/packages/tca-manage/src/modules/components/rule-table.tsx @@ -0,0 +1,169 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import Table from '@tencent/micro-frontend-shared/tdesign-component/table'; +import { Tag } from 'tdesign-react'; + +import { useURLParams } from '@tencent/micro-frontend-shared/hooks'; +import { get, concat } from 'lodash'; + +// 项目内 +import { getToolRouter } from '@plat/util'; +import { RuleSeverityEnum, RuleSeverityInvertEnum, RULE_SEVERITY_CHOICES } from '@src/constant'; + +// const { Column } = Table; + +interface RuleTableProps { + /** 表格数据 */ + tableData: any; + /** 列数据在表格数据中对应的路径集合 */ + indexs: any; + /** 加载状态 */ + loading: boolean; + /** 数据总数 */ + count: number; + /** 是否可编辑规则 */ + editable?: boolean; + /** 是否可选择规则 */ + checkable?: boolean; + /** 选中规则的ID列表 */ + selectedRowKeys?: any; + /** 选则规则回调函数 */ + setSelectedRowKeys?: (selectedKeys: any) => void; + /** 编辑规则回调函数 */ + editRule?: (ruleInfo: any) => void; +} + +const RuleTable = ({ + tableData, + indexs, + count, + loading, + editable = false, + checkable = false, + selectedRowKeys = null, + setSelectedRowKeys = null, + editRule = null }: RuleTableProps) => { + const { t } = useTranslation(); + const { currentPage, pageSize } = useURLParams(); + + const onSelectChange = (selectedKeys: any) => { + setSelectedRowKeys(selectedKeys); + }; + + const checkColumn = { + colKey: 'row-select', + type: 'multiple', + checkProps: ({ row }: any) => ({ + disabled: get(row, 'select_state') === 2, + checked: get(row, 'select_state') === 2 || selectedRowKeys.indexOf(row?.id) !== -1, + }), + width: 50, + }; + + const editColumn = { + colKey: 'ops', + title: t('操作'), + width: 100, + cell: ({ row }: any) => ( + editRule(row)} + > + {t('编辑')} + + ), + }; + + const infoColumns = [ + { + colKey: indexs.rule_display_name, + title: t('规则名称'), + width: 300, + cell: ({ row }: any) => (<> + {get(row, indexs.rule_display_name)} + {get(row, indexs.rule_title) &&

{get(row, indexs.rule_title)}

} + ), + }, + { + colKey: indexs.tool_display_name, + title: t('所属工具'), + width: 200, + cell: ({ row }: any) => ( + + {get(row, indexs.tool_display_name)}{get(row, 'checktool').scope === 1 && (私有)} + ), + }, + { + colKey: indexs.rule_severity, + title: t('级别'), + width: 100, + cell: ({ row }: any) => ( + {RULE_SEVERITY_CHOICES[get(row, indexs.rule_severity) as RuleSeverityEnum]} + ), + }, + { + colKey: indexs.rule_category_name, + title: t('类别'), + width: 100, + }, + { + colKey: indexs.rule_support_language, + title: t('语言'), + width: 100, + cell: ({ row }: any) => (get(row, indexs.rule_support_language).join() || '通用'), + }, + { + colKey: 'status', + title: '状态', + width: 100, + cell: ({ row }: any) => { + switch (get(row, indexs.rule_status)) { + case 1: + return 生效中; + case 2: + return 已屏蔽; + case false: + return 活跃; + case true: + return 失效; + default: + return 未知; + } + }, + }, + { + colKey: indexs.rule_owner, + title: t('负责人'), + width: 120, + }, + ]; + + const getColumns = () => { + let columns = infoColumns; + if (checkable) columns = concat(checkColumn, columns); + if (editable) columns = concat(columns, editColumn); + return columns; + }; + + return ( + + ); +}; + +export default RuleTable; diff --git a/web/packages/tca-manage/src/modules/jobs/archived-jobs/index.tsx b/web/packages/tca-manage/src/modules/jobs/archived-jobs/index.tsx new file mode 100644 index 000000000..d06a3b835 --- /dev/null +++ b/web/packages/tca-manage/src/modules/jobs/archived-jobs/index.tsx @@ -0,0 +1,62 @@ +/** + * Job 归档任务列表页面 + * biz-start + * 目前适用于全平台 + * biz-end + */ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Row, Col, Tabs } from 'tdesign-react'; +import Search from '@tencent/micro-frontend-shared/tdesign-component/search'; +import { useURLParams, useFetch } from '@tencent/micro-frontend-shared/hooks'; + +// 项目内 +import { jobAPI } from '@src/services/jobs'; + +// 模块内 +import { ARCHIVE_JOB_FILTER_FIELDS as filterFields, ARCHIVE_JOB_MORE_SEARCH_FIELDS, ARCHIVE_JOB_SEARCH_FIELDS, DEFAULT_ARCHIVE_JOB_FILTER } from '../constants'; +import JobTable from '../job-table'; +import s from '../../style.scss'; + +const { TabPanel } = Tabs; + +const ArchivedJobs = () => { + const { t } = useTranslation(); + const { filter, currentPage, pageSize, searchParams } = useURLParams(filterFields); + const [{ data, isLoading }] = useFetch(jobAPI.getArchived, [{ ...DEFAULT_ARCHIVE_JOB_FILTER, ...filter }]); + const { results: listData = [], count = 0 } = data || {}; + + return ( + <> + + + + + + + + +
+ +
+ + ); +}; + +export default ArchivedJobs; diff --git a/web/packages/tca-manage/src/modules/jobs/cancel-job-modal.tsx b/web/packages/tca-manage/src/modules/jobs/cancel-job-modal.tsx new file mode 100644 index 000000000..69353358b --- /dev/null +++ b/web/packages/tca-manage/src/modules/jobs/cancel-job-modal.tsx @@ -0,0 +1,67 @@ +import React, { useRef } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Dialog, Form, message, Input, FormInstanceFunctions } from 'tdesign-react'; + +// 项目内 +import { jobAPI } from '@src/services/jobs'; +import { JobData } from './types'; + +const { FormItem } = Form; + +interface CancelJobModalProps { + visible: boolean; + onOk: () => void; + onCancel: () => void; + jobInfo: JobData; +} + +const CancelJobModal = ({ jobInfo, visible, onOk, onCancel }: CancelJobModalProps) => { + const formRef = useRef(null); + const { t } = useTranslation(); + + /** + * 表单保存操作 + */ + const onSubmitHandle = () => { + formRef.current?.validate().then((result) => { + if (result === true) { + const fieldsValue = formRef.current?.getFieldsValue(true); + jobAPI.cancel(jobInfo.id, fieldsValue).then(() => { + message.success(t('取消任务成功')); + onOk(); + }); + } + }); + }; + + /** 重置表单操作 */ + const onReset = () => { + formRef.current?.reset(); + }; + + return ( + +
+ + + + +
+ ); +}; + +export default CancelJobModal; diff --git a/web/packages/tca-manage/src/modules/jobs/constants.ts b/web/packages/tca-manage/src/modules/jobs/constants.ts index ad819f2e1..fc508024f 100644 --- a/web/packages/tca-manage/src/modules/jobs/constants.ts +++ b/web/packages/tca-manage/src/modules/jobs/constants.ts @@ -1,92 +1,183 @@ -import { t } from '@src/i18n/i18next'; - -export const RUN_TYPE_ENUM = { - TEMP: 0, - DAILY: 2, - APP: 5, - CIBUILD: 6, - LOCAL: 7, +import moment from 'moment'; +import { flatMap } from 'lodash'; + +import { t } from '@tencent/micro-frontend-shared/i18n'; +import { SearchFormField } from '@tencent/micro-frontend-shared/tdesign-component/search'; +import { FilterField } from '@tencent/micro-frontend-shared/util/types'; +import { generateOptions, formatDate } from '@tencent/micro-frontend-shared/util'; + +/** 任务执行状态 */ +export enum StateEnum { + /** 等待中 */ + WAITING, + /** 执行中 */ + RUNNING, + /** 已结束 */ + CLOSED, + /** 入库中 */ + CLOSING, + /** 初始化中 */ + INITING, + /** 已初始化 */ + INITED, +} + +/** 任务执行状态 kv */ +export const STATE_CHOICES = { + [StateEnum.WAITING]: t('等待中'), + [StateEnum.RUNNING]: t('执行中'), + [StateEnum.CLOSED]: t('已结束'), + [StateEnum.CLOSING]: t('入库中'), + [StateEnum.INITING]: t('初始化'), + [StateEnum.INITED]: t('已初始化'), }; -export const RUN_TYPE_CHOICES = { - [RUN_TYPE_ENUM.TEMP]: t('手动触发'), - [RUN_TYPE_ENUM.DAILY]: t('定时触发'), - [RUN_TYPE_ENUM.APP]: t('API接口触发'), - [RUN_TYPE_ENUM.CIBUILD]: t('流水线触发'), - [RUN_TYPE_ENUM.LOCAL]: t('本地客户端触发'), +/** 任务执行状态 options */ +export const STATE_OPTIONS = generateOptions(STATE_CHOICES, true); + +/** 任务执行结果 */ +export enum ResultEnum { + /** 执行成功 */ + SUCCESS, + /** 执行异常 */ + EXCEPTION +} + +/** 任务执行结果 kv */ +export const RESULT_CHOICES = { + [ResultEnum.SUCCESS]: t('执行成功'), + [ResultEnum.EXCEPTION]: t('执行异常'), }; -export const RUN_TYPE_OPTIONS = [{ - text: RUN_TYPE_CHOICES[RUN_TYPE_ENUM.TEMP], - value: RUN_TYPE_ENUM.TEMP, +/** 任务执行结果 options */ +export const RESULT_OPTIONS = generateOptions(RESULT_CHOICES, true); + +/** 定义筛选字段结构 */ +export const JOB_SEARCH_FIELDS: SearchFormField[] = [{ + name: 'state', + label: '状态', + type: 'number', + formType: 'select', + options: STATE_OPTIONS, +}, { + name: 'result', + label: '结果', + type: 'number', + formType: 'select', + options: RESULT_OPTIONS, }, { - text: RUN_TYPE_CHOICES[RUN_TYPE_ENUM.DAILY], - value: RUN_TYPE_ENUM.DAILY, + name: 'repo', + label: 'ID', + type: 'string', + formType: 'input', + placeholder: '代码库 ID', +}]; + +/** 高级搜索的筛选字段 */ +export const JOB_MORE_SEARCH_FIELDS: SearchFormField[] = [{ + name: 'result_msg', + label: t('详情'), + type: 'string', + formType: 'input', + placeholder: t('结果详情'), }, { - text: RUN_TYPE_CHOICES[RUN_TYPE_ENUM.APP], - value: RUN_TYPE_ENUM.APP, + name: 'created_from', + label: t('渠道'), + type: 'string', + formType: 'input', + placeholder: t('启动渠道'), }, { - text: RUN_TYPE_CHOICES[RUN_TYPE_ENUM.CIBUILD], - value: RUN_TYPE_ENUM.CIBUILD, + name: 'scm_url', + label: t('URL'), + type: 'string', + formType: 'input', + placeholder: t('代码库地址'), }, { - text: RUN_TYPE_CHOICES[RUN_TYPE_ENUM.LOCAL], - value: RUN_TYPE_ENUM.LOCAL, + name: 'creator', + label: t('启动人'), + type: 'string', + formType: 'input', + placeholder: t('启动人'), }]; -export const RESULT_ENUM = { - SUCCESS: 0, - EXCEPTION: 1, - NULL: null, -}; +/** 整体的筛选字段 */ +export const JOB_FILTER_FIELDS = JOB_SEARCH_FIELDS.concat(JOB_MORE_SEARCH_FIELDS); -export const RESULT_CHOICES = { - [RESULT_ENUM.SUCCESS]: t('执行成功'), - [RESULT_ENUM.EXCEPTION]: t('执行异常'), +/** 归档任务日期类型 */ +export enum PeriodEnum { + MONTH = 'month', + DAY = 'day' +} + +/** 归档任务日期类型 kv */ +export const PERIOD_CHOICES = { + [PeriodEnum.MONTH]: t('当月'), + [PeriodEnum.DAY]: t('当日'), }; -export const RESULT_OPTIONS = [{ - label: RESULT_CHOICES[RESULT_ENUM.SUCCESS], - value: RESULT_ENUM.SUCCESS, -}, { - label: RESULT_CHOICES[RESULT_ENUM.EXCEPTION], - value: RESULT_ENUM.EXCEPTION, -}]; +/** 归档日期类型 options */ +export const PERIOD_OPTIONS = generateOptions(PERIOD_CHOICES); -export const STATE_ENUM = { - WAITING: 0, - RUNNING: 1, - CLOSED: 2, - CLOSING: 3, - INITING: 4, - INITED: 5, +/** 归档分析记录默认筛选项 */ +export const DEFAULT_ARCHIVE_JOB_FILTER = { + period: 'month', + date: formatDate(moment()), }; -export const STATE_CHOICES = { - [STATE_ENUM.WAITING]: t('等待中'), - [STATE_ENUM.RUNNING]: t('执行中'), - [STATE_ENUM.CLOSED]: t('已结束'), - [STATE_ENUM.CLOSING]: t('入库中'), - [STATE_ENUM.INITING]: t('初始化'), - [STATE_ENUM.INITED]: t('已初始化'), -}; +/** 归档分析记录基础筛选项 */ +export const ARCHIVE_JOB_SEARCH_FIELDS: SearchFormField[] = [{ + label: '范围', + name: 'date', + type: 'time', + formType: 'datepicker', + defaultValue: DEFAULT_ARCHIVE_JOB_FILTER.date, +}, { + name: 'period', + type: 'string', + formType: 'select', + options: PERIOD_OPTIONS, + defaultValue: DEFAULT_ARCHIVE_JOB_FILTER.period, +}, { + name: 'result', + label: '结果', + type: 'number', + formType: 'select', + options: RESULT_OPTIONS, +}]; -export const STATE_OPTIONS = [ +/** 归档分析记录高级筛选项 */ +export const ARCHIVE_JOB_MORE_SEARCH_FIELDS: SearchFormField[] = [ + ...JOB_MORE_SEARCH_FIELDS, { - label: STATE_CHOICES[STATE_ENUM.INITING], - value: STATE_ENUM.INITING, + name: 'create_time', + label: '开始', + type: 'string', + formType: 'rangepicker', + placeholder: '任务开始日期', }, { - label: STATE_CHOICES[STATE_ENUM.INITED], - value: STATE_ENUM.INITED, - }, { - label: STATE_CHOICES[STATE_ENUM.WAITING], - value: STATE_ENUM.WAITING, - }, { - label: STATE_CHOICES[STATE_ENUM.RUNNING], - value: STATE_ENUM.RUNNING, - }, { - label: STATE_CHOICES[STATE_ENUM.CLOSING], - value: STATE_ENUM.CLOSING, - }, { - label: STATE_CHOICES[STATE_ENUM.CLOSED], - value: STATE_ENUM.CLOSED, - }]; + name: 'end_time', + label: '结束', + type: 'string', + formType: 'rangepicker', + placeholder: '任务结束日期', + }, +]; + +/** 归档分析记录整体的筛选字段 */ +const ARCHIVE_JOB_ALL_SEARCH_FIELDS = ARCHIVE_JOB_SEARCH_FIELDS.concat(ARCHIVE_JOB_MORE_SEARCH_FIELDS); + +export const ARCHIVE_JOB_FILTER_FIELDS: FilterField[] = flatMap(ARCHIVE_JOB_ALL_SEARCH_FIELDS, (filter: any) => { + if (filter.formType === 'rangepicker') { + return [{ + name: `${filter.name}_gte`, + type: filter.type, + }, { + name: `${filter.name}_lte`, + type: filter.type, + }]; + } + return { + name: filter.name, + type: filter.type, + }; +}); diff --git a/web/packages/tca-manage/src/modules/jobs/index.tsx b/web/packages/tca-manage/src/modules/jobs/index.tsx index 360a89d5a..293097b7c 100644 --- a/web/packages/tca-manage/src/modules/jobs/index.tsx +++ b/web/packages/tca-manage/src/modules/jobs/index.tsx @@ -1,108 +1,78 @@ +/** + * Job 任务列表页面 + * biz-start + * 目前适用于全平台 + * biz-end + */ import React, { useState } from 'react'; -import { useHistory } from 'react-router-dom'; -import { Row, Col, Tabs } from 'coding-oa-uikit'; -import { toNumber } from 'lodash'; +import { useTranslation } from 'react-i18next'; +import { Row, Col, Tabs } from 'tdesign-react'; +import Search from '@tencent/micro-frontend-shared/tdesign-component/search'; +import { useURLParams, useFetch } from '@tencent/micro-frontend-shared/hooks'; // 项目内 -import { t } from '@src/i18n/i18next'; -import { getPaginationParams, getFilterURLPath } from '@src/utils'; -import { DEFAULT_PAGER } from '@src/common/constants'; -import { useURLParams, useDeepEffect } from '@src/utils/hooks'; -import { getJobs } from '@src/services/jobs'; +import { jobAPI } from '@src/services/jobs'; // 模块内 -import s from './style.scss'; -import Search from './search'; +import { JOB_FILTER_FIELDS as filterFields, JOB_SEARCH_FIELDS, JOB_MORE_SEARCH_FIELDS } from './constants'; import JobTable from './job-table'; +import CancelJobModal from './cancel-job-modal'; +import { JobData } from './types'; +import s from '../style.scss'; -const { TabPane } = Tabs; - -const FILTER_FIELDS = [ - // 'run_type', - 'state', - 'repo', - 'result', - // 'origin', - // 'platform', - // 'project', - // 'organization', - // 'project_team', -]; - -const customFilterURLPath = (params = {}) => getFilterURLPath(FILTER_FIELDS, params); +const { TabPanel } = Tabs; const Jobs = () => { - const history = useHistory(); - const [listData, setListData] = useState>([]); - const [count, setCount] = useState(DEFAULT_PAGER.count); - const [loading, setLoading] = useState(false); - const { filter, currentPage, searchParams } = useURLParams(FILTER_FIELDS); - - /** - * 根据路由参数获取团队列表 - */ - const getListData = () => { - setLoading(true); - - // result_code <= 99 表示通过,result_code >= 99 表示异常 - const { result } = filter; - const params: any = {}; - if (toNumber(result) === 0) { - params.result_code_lte = 99; - } - - if (toNumber(result) === 1) { - params.result_code_gte = 99; - } + const { t } = useTranslation(); + const { filter, currentPage, pageSize, searchParams } = useURLParams(filterFields); + const [{ data, isLoading }, reload] = useFetch(jobAPI.get, [filter]); + const { results: listData = [], count = 0 } = data || {}; + const [modalVisible, setModalVisible] = useState(false); + const [selectedJob, setSelectedJob] = useState(); - getJobs({ ...filter, ...params }).then((response) => { - setCount(response.count); - setListData(response.results || []); - setLoading(false); - }); + /** 点击取消任务操作 */ + const onCancelJob = (jobInfo: any) => { + setSelectedJob(jobInfo); + setModalVisible(true); }; - // 当路由参数变化时触发 - useDeepEffect(() => { - getListData(); - }, [filter]); - - // 筛选 - const onSearch = (params: any) => { - history.push(customFilterURLPath({ - limit: DEFAULT_PAGER.pageSize, - offset: DEFAULT_PAGER.pageStart, - ...params, - })); - }; - - // 翻页 - const onChangePageSize = (page: number, pageSize: number) => { - const params = getPaginationParams(page, pageSize); - history.push(customFilterURLPath(params)); + /** 取消任务后操作 */ + const afterCancelJob = () => { + setModalVisible(false); + reload(); }; return ( <>
- - + + -
- -
+
`${range[0]} - ${range[1]} 条数据,共 ${total} 条`, - onChange: onChangePageSize, + pageSize, }} + cancelJob={onCancelJob} + /> + setModalVisible(false)} />
diff --git a/web/packages/tca-manage/src/modules/jobs/job-table.tsx b/web/packages/tca-manage/src/modules/jobs/job-table.tsx index aeabcf24d..4b691cfab 100644 --- a/web/packages/tca-manage/src/modules/jobs/job-table.tsx +++ b/web/packages/tca-manage/src/modules/jobs/job-table.tsx @@ -1,156 +1,144 @@ import React from 'react'; -import { Link } from 'react-router-dom'; -import { Table, Tag, Progress } from 'coding-oa-uikit'; -// import Waiting from 'coding-oa-uikit/lib/icon/Waiting'; -// import Runing from 'coding-oa-uikit/lib/icon/Runing'; -// import Failed from 'coding-oa-uikit/lib/icon/Failed'; -// import Aborted2 from 'coding-oa-uikit/lib/icon/Aborted2'; -// import Success from 'coding-oa-uikit/lib/icon/Success'; -// import Attention from 'coding-oa-uikit/lib/icon/Attention'; - -// import DotCircle from 'coding-oa-uikit/lib/icon/DotCircle'; -// import { get } from 'lodash'; +import { useTranslation } from 'react-i18next'; +import { get } from 'lodash'; +import { PaginationProps, Tag, Progress, Button, PrimaryTableCol, Space, Loading } from 'tdesign-react'; +import Table from '@tencent/micro-frontend-shared/tdesign-component/table'; +import { secToHMS, formatDateTime } from '@tencent/micro-frontend-shared/util/time'; // 项目内 -import { t } from '@src/i18n/i18next'; -// import EllipsisTemplate from '@src/components/ellipsis'; -import { formatDateTime, getUserName, secondToDate } from '@src/utils'; -import { getJobRouter } from '@src/utils/getRoutePath'; -// import OrgAndTeamAndProjectInfo from '@src/modules/components/org-team-project-info'; +import { ColumnOrgInfo } from '@plat/job'; +import { getUserName } from '@src/utils'; +import { getJobRouter } from '@plat/util'; // 模块内 -import OrgAndTeamInfo from '@src/modules/components/org-team-info'; -import { STATE_CHOICES } from './constants'; - -const { Column } = Table; +import { STATE_CHOICES, StateEnum } from './constants'; +import { JobData } from './types'; -interface IProps { - dataSource: Array; - pagination: any; +interface JobTableProps { + loading: boolean; + dataSource: JobData[]; + pagination: PaginationProps; + archived?: boolean; + cancelJob?: (jobInfo: JobData) => void; } -const JobTable = ({ dataSource, pagination }: IProps) => ( - <> -
item.id} dataSource={dataSource}> - ( - <> - - {repo_scm_url} - -
- 分支:{branch} / 启动人:{getUserName(job.creator)} / 启动来源: - {job.created_from} -
- - )} - /> - ( - - )} - /> +const JobTable = ({ loading, dataSource, pagination, cancelJob, archived = false }: JobTableProps) => { + const { t } = useTranslation(); - {/* {created_from}} - /> - getUserName(creator)} - /> */} + const getColumns = () => { + const columns: PrimaryTableCol[] = [{ + colKey: 'job', + title: t('分析任务'), + width: 450, + fixed: 'left', + cell: ({ row }) => (<> + + {row.project?.repo_scm_url} + +
+ 分支:{row.project?.branch} / 启动人:{getUserName(row.creator)} / 启动来源:{row.created_from} +
+ ), + }, { + colKey: 'task_num', + title: t('执行进度'), + width: 200, + cell: ({ row }) => ( +
+ +
+ ), + }, { + colKey: 'state', + title: t('执行状态'), + width: 220, + cell: ({ row }) => ( + + {row.state !== StateEnum.CLOSED + ? + : {STATE_CHOICES[row.state as StateEnum]}} + {row.state !== StateEnum.CLOSED && } + + ), + }, { + colKey: 'result_msg', + title: t('执行结果'), + width: 250, + cell: ({ row }) => ( + <> + {row.result_code !== null && ( + + {row.result_code_msg} + + )} + {row.result_msg &&
{row.result_msg}
} + + ), + }, { + colKey: 'total_time', + title: t('总耗时'), + width: 220, + cell: ({ row }) => ( + +
等待:{secToHMS(row.waiting_time)}
+
执行:{secToHMS(row.execute_time)}
+
入库:{secToHMS(row.save_time)}
+
+ ), + }, { + colKey: 'create_time', + title: t('创建时间'), + width: 200, + cell: ({ row }: any) => ( +
{formatDateTime(get(row, 'create_time'))}
+ ), + }, { + colKey: 'start_time', + title: t('启动时间'), + width: 200, + cell: ({ row }: any) => ( +
{formatDateTime(get(row, 'start_time'))}
+ ), + }, { + colKey: 'created_from', + title: t('启动渠道'), + width: 160, + }, { + colKey: 'creator', + title: t('启动人'), + width: 160, + }]; + // 配置额外的列 + ColumnOrgInfo && columns.splice(1, 0, ColumnOrgInfo); + return columns; + }; - ( -
- -
- )} - /> - ( -
{STATE_CHOICES[state]}
- )} - /> - ( -
- {result_code !== null && ( - - {result_code_msg} - - )} - {result_msg && ( -
{result_msg}
- )} -
- )} - /> - ( -
-
等待耗时:{secondToDate(job.waiting_time)}
-
执行耗时:{secondToDate(job.execute_time)}
-
- )} - /> - ( -
{formatDateTime(create_time)}
- )} - /> - ( -
{formatDateTime(start_time)}
- )} - /> -
- -); + return ( + + ); +}; export default JobTable; diff --git a/web/packages/tca-manage/src/modules/jobs/search.tsx b/web/packages/tca-manage/src/modules/jobs/search.tsx deleted file mode 100644 index 62d9b2785..000000000 --- a/web/packages/tca-manage/src/modules/jobs/search.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import React, { useEffect } from 'react'; -import { toNumber, isString, isEmpty, isArray, cloneDeep } from 'lodash'; -import { Form, Button, Input, Select } from 'coding-oa-uikit'; - -// 项目内 -// import SelectBorderless from '@src/components/select-borderless'; -import Filter from '@src/components/filter'; - -// 模块内 -// import { RUN_TYPE_OPTIONS, STATE_OPTIONS, RESULT_OPTIONS } from './constants'; -import { STATE_OPTIONS, RESULT_OPTIONS } from './constants'; - -const numberParams: Array = ['state', 'result']; -const arrayParams: Array = []; - -interface SearchProps { - searchParams: any; - loading: boolean; - callback: (params: any) => void; -} - -const Search = ({ searchParams, loading, callback }: SearchProps) => { - const [form] = Form.useForm(); - const initialValues = cloneDeep(searchParams); - - Object.entries(initialValues).map(([key, value]: [string, string]) => { - if (numberParams.includes(key) && isString(value)) { - initialValues[key] = value.split(',').map((item: string) => toNumber(item)); - } - - if (arrayParams.includes(key) && isString(value)) { - initialValues[key] = value.split(','); - } - return [key, value]; - }); - - useEffect(() => { - if (!loading) { - form.resetFields(); - } - }, [loading]); - - const onChange = (key: string, value: any) => { - const formatValue = value === undefined ? '' : value; - callback({ - ...searchParams, - [key]: formatValue, - }); - }; - - const onClear = () => { - form.resetFields(); - callback({ - run_type: '', - state: '', - result: '', - // organization: '', - // project_team: '', - // project: '', - repo: '', - }); - }; - - return ( - - {/* - onChange('state', value)} /> - - -
item.id} dataSource={listData}> - - desc || '- -'} - /> - (field ? '是' : '否')} - /> - ( - <> - - {/* */} - - )} - /> -
- - ); -}; - -export default TagTable; diff --git a/web/packages/tca-manage/src/modules/oauth/constants.ts b/web/packages/tca-manage/src/modules/oauth/constants.ts deleted file mode 100644 index d39d8ee88..000000000 --- a/web/packages/tca-manage/src/modules/oauth/constants.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { t } from '@src/i18n/i18next'; - -export const SCM_PLATFORM_ENUM = { - TGIT: 1, - TGITSAAS: 2, - CODING: 3, - GITHUB: 4, - GITEE: 5, - GITLAB: 6, -}; - -export const SCM_PLATFORM = { - [SCM_PLATFORM_ENUM.TGIT]: t('工蜂内网'), - [SCM_PLATFORM_ENUM.TGITSAAS]: t('腾讯工蜂'), - [SCM_PLATFORM_ENUM.CODING]: t('Coding'), - [SCM_PLATFORM_ENUM.GITHUB]: t('GitHub'), - [SCM_PLATFORM_ENUM.GITEE]: t('Gitee'), - [SCM_PLATFORM_ENUM.GITLAB]: t('GitLab'), -}; - -export const SCM_PLATFORM_OPTIONS = [{ - label: SCM_PLATFORM[SCM_PLATFORM_ENUM.TGITSAAS], - value: SCM_PLATFORM_ENUM.TGITSAAS, -}, { - label: SCM_PLATFORM[SCM_PLATFORM_ENUM.CODING], - value: SCM_PLATFORM_ENUM.CODING, -}, { - label: SCM_PLATFORM[SCM_PLATFORM_ENUM.GITHUB], - value: SCM_PLATFORM_ENUM.GITHUB, -}, { - label: SCM_PLATFORM[SCM_PLATFORM_ENUM.GITEE], - value: SCM_PLATFORM_ENUM.GITEE, -}, { - label: SCM_PLATFORM[SCM_PLATFORM_ENUM.GITLAB], - value: SCM_PLATFORM_ENUM.GITLAB, -}]; - - -export const DEFAULT_SCM_PLATFORM = [ - { - scm_platform : 2, - scm_platform_name : "tgitsaas", - scm_platform_desc : '基于Git的企业级协作开发解决方案,腾讯研发关键系统', - }, - { - scm_platform : 4, - scm_platform_name : "github", - scm_platform_desc : '通过Git进行版本控制的软件源代码托管服务平台', - }, - { - scm_platform : 5, - scm_platform_name : "gitee", - scm_platform_desc : '开源中国于2013年推出的基于Git的代码托管和协作开发平台', - }, - { - scm_platform : 6, - scm_platform_name : "gitlab", - scm_platform_desc : "由GitLab Inc.开发的基于Git的完全集成的软件开发平台", - }, -]; \ No newline at end of file diff --git a/web/packages/tca-manage/src/modules/oauth/index.tsx b/web/packages/tca-manage/src/modules/oauth/index.tsx index 99b0c6293..f885f85bb 100644 --- a/web/packages/tca-manage/src/modules/oauth/index.tsx +++ b/web/packages/tca-manage/src/modules/oauth/index.tsx @@ -1,146 +1,130 @@ +/** + * OAuth配置页面 + * biz-start + * 目前适用于全平台 + * biz-end + */ import React, { useState, useEffect } from 'react'; -import { Row, Col, Tabs, Tag, message } from 'coding-oa-uikit'; -import { unionBy, get } from 'lodash'; +import { useTranslation } from 'react-i18next'; +import { unionBy } from 'lodash'; +import { Row, Col, Tabs, MessagePlugin } from 'tdesign-react'; // 项目内 -import { t } from '@src/i18n/i18next'; -import DangerModal from '@src/components/modal/danger-modal'; -import { getAllSettings, delOAuthSetting, postOAuthSetting } from '@src/services/oauth'; +import { DeleteModal } from '@tencent/micro-frontend-shared/tdesign-component/modal'; +import { oauthSettingAPI } from '@src/services/oauth'; +import { SCM_PLATFORM_CHOICES, ScmPlatformEnum, DEFAULT_SCM_PLATFORM } from '@plat/oauth'; // 模块内 -import s from './style.scss'; -import { DEFAULT_SCM_PLATFORM, SCM_PLATFORM } from './constants'; import OAuthTable from './oauth-table'; import OAuthModal from './oauth-modal'; +import { OAuthSettingData } from './types'; +import s from '../style.scss'; -const { TabPane } = Tabs; +const { TabPanel } = Tabs; const OAuth = () => { - const [listData, setListData] = useState>([]); + const { t } = useTranslation(); + const [listData, setListData] = useState([]); const [visibleEdit, setVisibleEdit] = useState(false); const [visibleDel, setVisibleDel] = useState(false); - const [platformInfo, setPlatformInfo] = useState(null); + const [platformInfo, setPlatformInfo] = useState(null); const [reload, setReload] = useState(false); const [create, setCreate] = useState(false); - /** - * 获取OAuth平台列表 - */ - const getListData = () => { - getAllSettings().then((response) => { - setListData(unionBy(response.results,DEFAULT_SCM_PLATFORM,'scm_platform')); - }).catch(()=>{ - message.error('获取配置列表失败'); - }); - }; - useEffect(() => { - getListData(); + /** 获取OAuth配置项列表 */ + oauthSettingAPI.get({ limit: 100 }) + .then(({ results = [] }: any) => { + setListData(unionBy(results, DEFAULT_SCM_PLATFORM, 'scm_platform')); + }); }, [reload]); /** - * 编辑OAuth配置 - * @param platform_info 选中平台的配置信息 + * 点击编辑凭证配置 + * @param platformInfo 凭证配置信息 + * @param create 是否创建操作 */ - const onEditStart = ( platform_info:any, create:boolean ) => { + const onEditStart = (platformInfo: OAuthSettingData, create: boolean) => { setVisibleEdit(true); - setPlatformInfo(platform_info); + setPlatformInfo(platformInfo); setCreate(create); }; - /** - * 上传OAuth配置 - * @param platform_info 表单内容 - */ - const onEditFinish = ( platform_info:any ) => { - + /** 完成编辑操作,发起请求 */ + const onEditFinish = (platformInfo: OAuthSettingData, params: any) => { if (create) { - //初次创建配置 - postOAuthSetting(platform_info).then(() => { - message.success('已创建配置'); + // 初次创建配置 + oauthSettingAPI.create(params).then(() => { + MessagePlugin.success(t('已创建配置')); setReload(!reload); - }).catch(()=>{ - message.error('创建配置失败'); - }).finally(()=>{ onEditCancel(); }); } else { - //编辑已有配置 - delOAuthSetting(platformInfo?.scm_platform_name).then(() => { - postOAuthSetting(platform_info).then(() => { - message.success('已更新配置'); + // 编辑已有配置 + oauthSettingAPI.delete(platformInfo.scm_platform_name).then(() => { + oauthSettingAPI.create(params).then(() => { + MessagePlugin.success(t('已更新配置')); setReload(!reload); + onEditCancel(); }); - }).catch(()=>{ - message.error('更新配置失败'); - }).finally(()=>{ - onEditCancel(); }); } + }; - } - + /** 点击编辑操作取消 */ const onEditCancel = () => { setVisibleEdit(false); - setPlatformInfo(null); }; - const onDeleteStart = ( platform_info:any ) => { - setPlatformInfo(platform_info); + /** 点击删除凭证配置 */ + const onDeleteStart = (platformInfo: OAuthSettingData) => { + setPlatformInfo(platformInfo); setVisibleDel(true); - } + }; /** * 清除OAuth配置 * @param platform_info 选中平台的配置信息 */ - const onDeleteFinish = ( platform_info:any ) => { - delOAuthSetting(platform_info?.scm_platform_name).then(() => { - message.success('已删除配置'); + const onDeleteFinish = (platform_info: any) => { + oauthSettingAPI.delete(platform_info.scm_platform_name).then(() => { + MessagePlugin.success(t('已删除配置')); setReload(!reload); - }).catch(()=>{ - message.error('删除配置失败'); - }).finally(()=>{ setVisibleDel(false); }); - } + }; return ( <> - - + +
- + - + setVisibleDel(false)} onOk={() => onDeleteFinish(platformInfo)} - content={ -
- {t('确认删除 ')} - {get(SCM_PLATFORM, platformInfo?.scm_platform) || 'unknown'} - {t('的OAuth应用配置?')} -
- } - /> -
+ /> ); }; diff --git a/web/packages/tca-manage/src/modules/oauth/oauth-modal.tsx b/web/packages/tca-manage/src/modules/oauth/oauth-modal.tsx index 30afcfcd8..4ed2f46c1 100644 --- a/web/packages/tca-manage/src/modules/oauth/oauth-modal.tsx +++ b/web/packages/tca-manage/src/modules/oauth/oauth-modal.tsx @@ -1,118 +1,122 @@ -import React, { useEffect, useState } from 'react'; -import { Modal, Form, Input, Select, Tooltip } from 'coding-oa-uikit'; -import QuestionCircle from 'coding-oa-uikit/lib/icon/QuestionCircle'; +import React, { useRef } from 'react'; +import { useTranslation } from 'react-i18next'; import { get } from 'lodash'; - +import { Dialog, Form, Input, Select, Tooltip, Textarea, FormInstanceFunctions, InputAdornment } from 'tdesign-react'; +import { HelpCircleIcon } from 'tdesign-icons-react'; // 项目内 -import { t } from '@src/i18n/i18next'; +import { SCM_PLATFORM_OPTIONS } from '@plat/oauth'; // 模块内 -import { SCM_PLATFORM_OPTIONS } from './constants'; +import { OAuthSettingData } from './types'; + +const { FormItem } = Form; -const { TextArea } = Input; +const getHostName = (redirect_uri: string) => { + if (redirect_uri) { + const urlList = redirect_uri.split('/'); + const hostname = (urlList.length > 3) ? get(urlList, 2) : redirect_uri; + return hostname; + } + return ''; +}; -interface IProps { +interface OAuthModalProps { visible: boolean; - scminfo: any; + scminfo: OAuthSettingData; onCancel: () => void; - onOk: ( formData:any ) => void; + onOk: (scminfo: OAuthSettingData, formData: any) => void; } -const OAuthModal = ({ scminfo, visible, onCancel, onOk }: IProps) => { - const [form] = Form.useForm(); - const [appInfo, setAppInfo] = useState(null); - - useEffect(() => { - if (visible) { - // 格式化回调地址 - if (scminfo?.redirect_uri) { - const urlList = scminfo?.redirect_uri.split('/'); - const hostname = (urlList.length>3) ? get(urlList,2):scminfo?.redirect_uri; - scminfo.redirect_uri=hostname; - } - setAppInfo(scminfo); - } - }, [visible]); +const OAuthModal = ({ scminfo, visible, onCancel, onOk }: OAuthModalProps) => { + const formRef = useRef(null); + const { t } = useTranslation(); - useEffect(()=>{ - form.resetFields(); - }, [appInfo]) - - /** - * 表单保存操作 - * @param formData 参数 - */ + /** 表单保存操作 */ const onSubmitHandle = () => { - form.validateFields().then((formData) => { - // 格式化回调地址 - formData.redirect_uri=`http://${formData.redirect_uri}/cb_git_auth/${appInfo?.scm_platform_name}`; - onOk(formData); + formRef.current?.validate().then((result: any) => { + if (result === true) { + const fieldsValue = formRef.current?.getFieldsValue(true); + onOk(scminfo, { + ...fieldsValue, + redirect_uri: `http://${fieldsValue.redirect_uri}/cb_git_auth/${scminfo?.scm_platform_name}`, + }); + } }); }; + /** 重置表单操作 */ + const onReset = () => { + formRef.current?.reset(); + }; + return ( - -
- - + + - - + - - - + + - {t('回调地址')} - 请填入当前TCA平台配置的域名或IP地址
(如当前页面非80端口,需要显式指定端口号)

} - placement="top" - getPopupContainer={() => document.body } - > - -
+ {t('回调地址')} + {t('请填入当前TCA平台配置的域名或IP地址')}
{t('(如当前页面非80端口,需要显式指定端口号)')}

} + placement="top" + > + +
} rules={[{ required: true, message: t('回调地址为必填项') }]} + initialData={getHostName(scminfo?.redirect_uri)} > - -
- http://} + append={
/cb_git_auth/{scminfo?.scm_platform_name}
}> + + + + -