Compare commits

...

331 Commits

Author SHA1 Message Date
yystopf 65844ab733 修复:trends commit_log为nil的情况 2026-08-05 12:09:14 +08:00
xxq250 e49201911f fixed oauth2 has login 3 2026-07-31 11:45:11 +08:00
xxq250 56082ee35e fixed oauth2 has login 2 2026-07-31 11:40:03 +08:00
xxq250 6e6774ffcb fixed oauth2 has login 2026-07-31 11:29:11 +08:00
yystopf 5c991c14be fix: 处理diff为空的情况 2026-07-28 11:11:00 +08:00
yystopf cff70fa6a1 更改:gitea_client超时时间 2026-07-21 17:04:03 +08:00
yystopf 76b4c79d2d 更改:如果 id 包含格式扩展名(如 .json),移除它 2026-07-16 17:13:18 +08:00
yystopf 4f60b4c777 修复:owners接口无法匹配特殊字符 2026-07-16 17:06:33 +08:00
yystopf 20cb2c8eb8 修复:owners放在后面以防覆盖 2026-07-16 16:58:03 +08:00
yystopf 7b8107b95e 修复:回退owners 2026-07-16 16:25:29 +08:00
yystopf dd2d5547e3 修复:owners放在后面以防覆盖 2026-07-16 16:22:44 +08:00
yystopf ab86b953ce 修复:image_type移除.判断 2026-07-16 09:23:02 +08:00
xxq250 65f8248373 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2026-07-16 09:08:10 +08:00
xxq250 32ebc94402 fixed commit log render_ok return 2026-07-16 09:08:03 +08:00
yystopf e2bf2e0854 修复:URI.escape 2026-07-16 09:06:05 +08:00
xxq250 8c32e0067e fixed 下载文件log 2026-07-13 15:43:46 +08:00
xxq250 7848e092e4 fixed 下载文件 2026-07-13 15:37:18 +08:00
yystopf 94d5a2d531 修复:转移项目后无法重新创建项目 2026-07-01 10:52:33 +08:00
yystopf 11490ecda1 修复:合并请求commit报错 2026-07-01 09:47:55 +08:00
yystopf 1cb2b821cf 修复:合并请求commit报错 2026-07-01 09:43:53 +08:00
xxq250 619b6b654c 项目增加成员支持login 2026-06-30 11:02:01 +08:00
xxq250 faff21a677 add Authorization Bearer token 2026-06-30 10:24:28 +08:00
xxq250 11bceeada0 add 增加按组织标识创建仓库 2026-06-30 09:52:19 +08:00
xxq250 46c6fcb94f add 增加按组织标识创建仓 2026-06-30 09:26:00 +08:00
xxq250 3ae18f234c fixed 调整短信发送限制 2026-06-22 14:53:15 +08:00
xxq250 591804f4f3 流水线名称查询条件传递到子集 2026-06-17 16:06:03 +08:00
xxq250 0a4381de0a 流水线名称查询 2026-06-17 14:33:41 +08:00
xxq250 97612480a6 流水线选择模板后保存json 2026-06-17 14:32:03 +08:00
xxq250 d4ee34b7f8 流水线选择模板后保存json 2026-06-17 14:32:02 +08:00
xxq250 bd786372fe 流水线选择模板后保存json 2026-06-17 14:32:02 +08:00
xxq250 f14ba00e04 流水线节点支持普通用户管理 2026-06-17 10:30:07 +08:00
xxq250 8348708c49 流水线模板支持普通用户管理 2026-06-17 10:26:46 +08:00
xxq250 e1e3c46aab Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2026-06-11 17:14:52 +08:00
xxq250 84670faae6 超算社区项目logo 2026-06-11 17:14:45 +08:00
yystopf f0247a80f8 新增:同步项目至atomgit脚本 2026-06-05 14:34:42 +08:00
yystopf 18f933d7f6 新增:项目导入到atomgit service 2026-06-05 11:47:17 +08:00
yystopf 62b970661b 新增:项目导入到atomgit service 2026-06-05 11:43:06 +08:00
xxq250 d460b4924a action node custom attr 2026-06-02 11:18:10 +08:00
xxq250 64deebf717 fixed OpenProjectDevOpsJob user nil fx 2026-06-01 14:50:19 +08:00
xxq250 286afb2ee0 fixed pr增加merged、merged、merge_commit_sha信息 2026-05-29 17:04:40 +08:00
xxq250 86f1dad815 镜像项目不同步commit log 2026-05-29 16:49:44 +08:00
xxq250 914136ad58 增加红山开源项目同步,强制更新 2026-05-28 15:59:35 +08:00
xxq250 5224acbca2 增加红山开源项目同步,补全git地址 2026-05-28 13:55:44 +08:00
xxq250 1749a90202 增加红山开源项目同步,source from SyncRepositories::Osredm 2026-05-28 09:37:33 +08:00
xxq250 10d358749f 登录成功返回信息 2026-05-28 09:22:19 +08:00
xxq250 f502aa081e 增加红山开源项目同步,无效token log 2026-05-27 17:35:34 +08:00
xxq250 552b3c2f39 增加红山开源项目同步,无效token log 2026-05-27 17:14:25 +08:00
xxq250 bb9d8eb9f3 增加红山开源项目同步,无效token log 2026-05-27 17:08:27 +08:00
xxq250 1e7e0766fa 增加红山开源项目同步,无效token log 2026-05-27 17:08:04 +08:00
xxq250 5021391d90 增加红山开源项目同步,无效token log 2026-05-27 17:06:32 +08:00
xxq250 979d1c6722 增加红山开源项目同步,无效token log 2026-05-27 16:57:22 +08:00
xxq250 a92fd3a31a 增加红山开源项目同步,无效token 2026-05-27 16:55:53 +08:00
xxq250 21d0f09bc2 增加红山开源项目同步,无效token 2026-05-27 16:53:01 +08:00
xxq250 fed9dac6cd 增加红山开源项目同步,无效token 2026-05-27 16:36:57 +08:00
xxq250 630682d49a 增加红山开源项目同步,source from 2026-05-27 16:34:22 +08:00
xxq250 2c7d76b682 同步工厂优化过的流水线代码,语法暂不验证 2026-05-26 17:15:40 +08:00
xxq250 20ba9d9ac7 增加红山开源项目同步,model 2026-05-26 16:25:37 +08:00
xxq250 68cb51e068 增加红山开源项目同步,route 2026-05-26 16:09:55 +08:00
xxq250 4f59fb7e2f 同步工厂优化过的流水线代码 2026-05-26 14:38:54 +08:00
xxq250 cdc03e5e6d 增加红山开源项目同步 2026-05-26 09:53:58 +08:00
xxq250 830dfc80eb fixed:CCF手机号授权登录手机号可能为空 2026-05-22 14:32:39 +08:00
xxq250 5c1d190be0 NSCCN 定制图标 2026-05-13 09:35:23 +08:00
xxq250 fcae803640 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2026-05-13 09:26:44 +08:00
xxq250 e323c3e9f1 NSCCN 定制图标 2026-05-13 09:26:32 +08:00
yystopf 5e3319fa59 更改:旧issue接口兼容index 2026-05-12 16:59:52 +08:00
yystopf 2ac8e47211 更改:删除分支穿参 2026-05-09 15:42:46 +08:00
yystopf cf9d22b13d 更改:更改文件传当前时间 2026-05-08 16:25:38 +08:00
yystopf f956bfda2a 修复:项目列表数量对不上 2026-05-08 11:23:36 +08:00
xxq250 2f79aab571 后台项目增加镜像项目查询 2026-04-27 13:53:57 +08:00
xxq250 6f7aad06c6 后台项目增加镜像项目查询 2026-04-27 12:28:20 +08:00
yystopf 6584b69dc1 fix 2026-04-24 09:44:26 +08:00
yystopf f625429cc1 更改:超算组织展示mirror_url中地址 2026-04-24 09:40:33 +08:00
yystopf 13e02594f7 新增:合并请求files_count为0判断 2026-04-22 16:18:56 +08:00
yystopf 3ae73ebf06 修复:创建文件需验证文件内容是否为base64 2026-04-13 14:58:06 +08:00
yystopf 17bd3034bb 修复:更新文件使用base64encode 2026-04-13 14:47:09 +08:00
yystopf 328f3a0b72 修复:替换文件更新文件创建文件接口问题 2026-04-10 16:50:54 +08:00
yystopf 1f41779e9b 修复:代码标签搜索报错 2026-04-09 15:51:15 +08:00
yystopf 4fe2dc6b28 修复:导入仓库密码为严格base64 2026-04-09 15:38:58 +08:00
xxq250 cc05584245 镜像提交记录不再记录 2026-03-30 17:30:06 +08:00
yystopf a73c3bb44a 更改:get_file斜杠处理 2026-02-28 14:55:33 +08:00
yystopf 89eb4c2393 Merge branch 'standalone_develop' of https://gitlink.org.cn/Trustie/forgeplus into standalone_develop 2026-02-27 15:34:41 +08:00
yystopf d7008003c9 fixed:项目issue数统计真实issue数 2026-02-27 15:34:31 +08:00
xxq250 63a5b83e96 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2026-02-09 09:51:10 +08:00
xxq250 bbb06d2c08 取消drone数据库连接 2026-02-09 09:51:03 +08:00
yystopf f77b2ae182 fixed:代码回退 2026-01-28 16:10:42 +08:00
yystopf bf4ff93ed9 fixed:代码回退 2026-01-28 16:09:15 +08:00
yystopf c8b80c04d6 更改:特殊字符函数使用uri.escape 2026-01-28 15:55:06 +08:00
yystopf 6c56712573 更改:特殊字符函数使用uri.escape 2026-01-28 15:54:56 +08:00
yystopf aab613ebce 更改:移除去除空格函数 2026-01-28 15:42:07 +08:00
yystopf 7759ea558c fixed:代码回退 2026-01-28 15:26:36 +08:00
yystopf f615487380 Merge branch 'standalone_develop' of https://gitlink.org.cn/Trustie/forgeplus into standalone_develop 2026-01-28 15:09:19 +08:00
yystopf 47832df856 更改:特殊字符处理 2026-01-28 15:09:10 +08:00
yystopf a8ec291050 新增:分支名与标签名称不能相同提示 2026-01-23 08:50:19 +08:00
yystopf 0ea1a843da fixed: branch_name to tag_name 2026-01-22 16:25:28 +08:00
yystopf 4341f19c21 新增:delete分支和标签接口 2026-01-22 16:19:16 +08:00
yystopf d5ef64c31d fixed:传递至gitea需要转义 2026-01-22 15:17:04 +08:00
yystopf 6f90c5ff88 更改:分支标签删除加密传输 2026-01-22 14:48:19 +08:00
yystopf 3be4408661 fixed:未更新关联疑修不产生操作记录 2026-01-22 14:35:46 +08:00
yystopf 8dd213d408 fixed:未更新标记不产生操作记录 2026-01-22 14:33:18 +08:00
yystopf dfd7e40b6a 更改:评论数返回新数据 2026-01-22 14:25:18 +08:00
yystopf 81b34f941d 更改:评论数返回新数据 2026-01-22 14:24:16 +08:00
yystopf 21a9162dba fixed:无法编辑标记内容 2026-01-22 14:06:19 +08:00
yystopf b1e59da99c 新增:关联疑修操作记录 2026-01-22 11:49:25 +08:00
yystopf 2e496a17c0 新增:关联疑修操作记录 2026-01-22 11:40:40 +08:00
yystopf 26fb01cfd8 新增:关联疑修操作记录 2026-01-22 11:37:41 +08:00
xxq250 6db7fada1d Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2026-01-22 11:01:50 +08:00
xxq250 09add4f080 特殊url转yi 2026-01-22 11:01:43 +08:00
yystopf a8140cf0a6 新增:pullrequest编辑接口status返回 2026-01-22 09:52:58 +08:00
yystopf 006054f424 fixed: 合并请求接口返回status 2026-01-22 09:45:59 +08:00
yystopf 4248c43023 fixed: 创建文件参数缺失处理 2026-01-22 09:22:13 +08:00
yystopf 4a4fe2f223 fixed: 创建文件参数缺失处理 2026-01-22 09:21:13 +08:00
yystopf e6a4b09757 fixed: 创建文件参数缺失处理 2026-01-22 09:13:39 +08:00
yystopf ea12dab204 更改:标签删除去除.json 2026-01-20 16:15:38 +08:00
yystopf 4ffc9246ac Merge branch 'standalone_develop' of https://gitlink.org.cn/Trustie/forgeplus into standalone_develop 2026-01-20 15:57:55 +08:00
yystopf 74825b224e fixed:关联issue为空无法保存 2026-01-20 15:57:46 +08:00
yystopf 153afa0c77 更改:pullrequest 的issue支持issue_tag多选查询 2026-01-16 15:02:42 +08:00
yystopf b96d32505c fix: release_version字段长度问题 2026-01-15 16:48:14 +08:00
yystopf 20cd41b5e9 新增:标签新建接口 2026-01-15 15:01:51 +08:00
xxq250 370746c79d Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2026-01-14 17:02:44 +08:00
xxq250 0eda08219c 授权登录按cookie记录url跳转 2026-01-14 17:02:30 +08:00
yystopf ef8226caf5 Merge branch 'standalone_develop' of https://gitlink.org.cn/Trustie/forgeplus into standalone_develop 2026-01-14 15:38:43 +08:00
yystopf 4f6f4d781f fixed:合并请求标记可以传多个 2026-01-14 15:38:37 +08:00
xxq250 561cc46dc8 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2026-01-14 11:53:40 +08:00
xxq250 c5d0a23345 导入项目地址时,补全url.git,params[:source_clone_url] 2026-01-14 11:53:34 +08:00
yystopf 720aaab53a Merge branch 'standalone_develop' of https://gitlink.org.cn/Trustie/forgeplus into standalone_develop 2026-01-14 11:28:38 +08:00
yystopf a74599cbed 新增:模板管理接口 2026-01-14 11:28:30 +08:00
xxq250 e21197921e 导入项目地址时,补全url.git,end 2026-01-14 11:28:17 +08:00
xxq250 46ebf2ad7e 导入项目地址时,补全url.git 2026-01-14 11:24:10 +08:00
xxq250 68d4c76774 导入项目地址时,补全url.git 2026-01-14 11:22:00 +08:00
xxq250 306310a718 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2026-01-14 11:04:39 +08:00
xxq250 9429f05f39 导入项目地址时,补全url.git 2026-01-14 11:04:27 +08:00
yystopf a701e17563 更改:标记操作记录触发条件 2026-01-13 15:14:44 +08:00
yystopf 3805feb5be 新增:审查人员与标记操作记录 2026-01-13 14:48:55 +08:00
yystopf 95899adfc4 fixed: 移除旧操作记录 2026-01-12 17:27:46 +08:00
yystopf f754b3835c fixed: 查询评论issue全量查询 2026-01-12 17:06:03 +08:00
yystopf 491a73cec5 fixed: 查询评论issue全量查询 2026-01-12 17:04:08 +08:00
yystopf fc6172935c 新增:合并请求操作记录 2026-01-12 16:50:14 +08:00
yystopf 81d4b35b3c fixed:索引错误 2025-12-31 16:08:01 +08:00
yystopf 69e4c49ed5 新增:project_trends新增索引 2025-12-31 16:01:57 +08:00
yystopf 8654bf5854 fixed 接口错误 2025-12-31 15:33:39 +08:00
yystopf 064354e508 fixed 2025-12-31 15:19:41 +08:00
yystopf edbaf2829d 更改:优化project_trends查询 2025-12-31 15:17:22 +08:00
xxq250 be8fd9e37e openi 授权登录 2025-12-31 10:57:58 +08:00
xxq250 6e6b46976e ccf 授权登录 2025-12-31 10:57:50 +08:00
xxq250 c12023c164 授权登录位置 2025-12-31 10:57:04 +08:00
xxq250 f8eb2d87f5 ProjectPullRequest通知延时 2025-12-31 10:56:10 +08:00
xxq250 346c15bae3 openi 授权登录 2025-12-24 10:11:15 +08:00
xxq250 8212e2fb3d openi 授权登录 2025-12-24 09:57:16 +08:00
xxq250 67ebbabfff ccf 授权登录 settings 2025-12-24 09:53:38 +08:00
xxq250 8ff4093738 ccf 授权登录 url 2025-12-22 17:09:35 +08:00
xxq250 fed3865fb2 ccf 授权登录 2025-12-22 17:06:02 +08:00
xxq250 53307632ae 禁用敏感词 2025-12-13 17:08:34 +08:00
xxq250 5ff91b0510 fixed github加速镜像 2025-12-04 11:46:34 +08:00
yystopf 87da55c825 新增:访问数后台管理 2025-12-01 08:45:30 +08:00
yystopf 83a7292939 更改:注释反馈意见操作栏 2025-11-10 15:22:24 +08:00
yystopf 9ea1f1627a 新增:反馈意见新增ip地址 2025-11-10 14:51:22 +08:00
yystopf 81ce0ca2b4 更改:无法获取到数组的值 2025-11-07 16:07:13 +08:00
yystopf dc8794151b fixed:恢复传数组合并请求指派者 2025-11-07 15:56:58 +08:00
yystopf 0431a42519 更改:合并请求指派者为一个 2025-11-07 15:43:09 +08:00
yystopf 8889beb968 修复:操作记录显示错误 2025-11-06 16:07:39 +08:00
xxq250 c56a25cdf4 fixed zip js/css 2025-11-05 17:39:02 +08:00
xxq250 ede4e2ab9c fixed zip js/css 2025-11-05 17:35:24 +08:00
xxq250 d0ef36e273 fixed 仓库中已有流水线文件不读取加入到列表 2025-11-05 11:26:21 +08:00
Your Name e3dd23da3f fix: 样式 2025-11-04 11:39:47 +08:00
yystopf 0d1ed72a18 fix: 更改验证密码长度 2025-11-04 11:46:28 +08:00
yystopf 217fa5cc67 fix: 更改验证密码长度 2025-11-04 11:43:26 +08:00
yystopf 4aab0ddf51 fix:回滚edit.js 2025-11-04 11:40:19 +08:00
yystopf 907ee60c1d 修复:后台管理密码位数为8 2025-11-04 11:30:14 +08:00
xxq250 114cb49c13 add 用户增加显示token 改成POST方法 2025-10-31 16:03:40 +08:00
xxq250 89e13ecfcc add 用户增加显示token和携带token请求api 2025-10-31 15:57:12 +08:00
yystopf 96cde9f76a 新增:用户参与项目数量 2025-10-28 10:29:52 +08:00
yystopf 3357e56a84 新增:用户参与项目数量 2025-10-28 10:29:33 +08:00
yystopf d316669b7f 新增:用户参与项目数量 2025-10-28 10:27:35 +08:00
yystopf bdd9518a4c 新增:用户参与项目数量 2025-10-28 10:24:58 +08:00
xxq250 a3b98ea9ce Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2025-10-27 14:50:26 +08:00
xxq250 5cd1733a82 fixed 用户账号接口增加手机号 2025-10-27 14:50:19 +08:00
yystopf 2bd58d2ad7 fix: action 数据埋点长信息移除 2025-10-17 14:01:51 +08:00
xxq250 354b8ee8a9 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2025-10-16 09:26:38 +08:00
xxq250 f044ad705d fixed pr流水线触发事件 2025-10-16 09:26:31 +08:00
yystopf 4e48bdb34f fixed: 默认不显示自定义查询 2025-10-15 15:19:19 +08:00
yystopf 2905da7b14 fixed: 传值错误修改 2025-10-15 15:11:05 +08:00
yystopf 8111c13417 fixed: 传值错误修改 2025-10-15 15:08:18 +08:00
yystopf 3a38cb675c 更改:操作日志显示 2025-10-15 15:04:30 +08:00
xxq250 c9b3500fcf fixed 调整仓库敏感词检测 2025-10-10 10:36:37 +08:00
xxq250 d5cf6e444d fixed 查询sql性能优化 2025-09-29 16:29:16 +08:00
xxq250 29cde8621c fixed 查询sql性能优化 2025-09-29 16:16:15 +08:00
yystopf 0d99ccce80 更改:组织接口路由冲突 2025-09-29 16:04:36 +08:00
xxq250 f545615567 fixed repo_git_submodule_url ssh url 2025-09-28 11:33:18 +08:00
xxq250 365345f15a Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2025-09-28 11:26:07 +08:00
xxq250 ae6540549c fixed repo_git_submodule_url ssh url 2025-09-28 11:25:56 +08:00
yystopf 02e836d730 fix: 去除name流水线验证 2025-09-26 14:33:16 +08:00
yystopf 22dedcc5ff fix:移除on验证 2025-09-24 16:04:03 +08:00
xxq250 69f1b3862a fixed 项目下载zip-url转义错误 2025-09-22 17:17:14 +08:00
xxq250 50655495a0 fixed 调整敏感词 2025-09-22 10:26:04 +08:00
xxq250 9c26319b17 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2025-09-21 11:54:25 +08:00
xxq250 2971e75cbf fixed 扩展项目接口字段 2025-09-21 11:54:15 +08:00
yystopf 65bab3d9e2 更改:错误提示 2025-09-19 14:58:56 +08:00
yystopf 11b2ca48cf 新增:action文件验证器 2025-09-19 11:41:59 +08:00
yystopf 5dfc0e74a8 修复:转移后仓库无法正确新建处理 2025-09-12 11:58:07 +08:00
yystopf e89a93e8ca 修复:更新流水线文件名处理 2025-09-10 16:51:31 +08:00
yystopf 9942974c42 更改:负责人排序规则 2025-09-04 11:30:46 +08:00
xxq250 76b70fea14 fixed md中图片处理包含中文名称,tip 2025-09-01 11:38:44 +08:00
xxq250 e82ec9d676 fixed md中图片处理包含中文名称,tip 2025-09-01 11:12:29 +08:00
xxq250 faa3f282a4 fixed md中图片处理包含中文名称,tip 2025-09-01 11:04:23 +08:00
xxq250 6d65d1163a Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2025-09-01 10:52:47 +08:00
xxq250 e2ff617715 fixed md中图片处理包含中文名称,tip 2025-09-01 10:52:41 +08:00
yystopf 5a0b1858c8 Merge branch 'standalone_develop' of https://gitlink.org.cn/Trustie/forgeplus into standalone_develop 2025-08-27 16:04:22 +08:00
yystopf 6a8ef3ac3b 新增:触发项目行为构建 2025-08-27 16:04:08 +08:00
yystopf 754ed99959 新增:项目行为构建方法 2025-08-27 15:07:04 +08:00
xxq250 a85534b394 fixed 流水线重复生成 2025-08-19 10:12:57 +08:00
yystopf 95f49450b0 新增:关联的疑修操作日志 2025-08-01 15:36:59 +08:00
yystopf bcf0b376a9 新增:关联的疑修操作日志 2025-08-01 15:25:47 +08:00
yystopf 81a5adb21a 新增:关联的疑修操作日志 2025-08-01 15:12:41 +08:00
yystopf b6de7fa96b fixed:操作日志为空的情况 2025-08-01 14:57:07 +08:00
yystopf 01ef87f5a3 fixed:操作日志为空的情况 2025-08-01 14:34:17 +08:00
yystopf a35537694c 修复:操作日志显示错误 2025-07-31 16:41:58 +08:00
yystopf 2229c732ca 新增:返回列表project_id 2025-07-30 16:48:30 +08:00
yystopf 532b924ff1 新增:返回列表project_id 2025-07-30 16:32:58 +08:00
yystopf 16a71a383b 更改:用户操作日志 2025-07-30 16:23:41 +08:00
xxq250 7d67defa24 fixed 流水线停止api 2025-07-29 10:59:30 +08:00
xxq250 41515575f1 add 流水线停止api 2025-07-29 10:33:56 +08:00
yystopf f4248ec14b fixed 2025-07-25 15:06:50 +08:00
yystopf 9544a5b22d fixed 2025-07-25 15:02:42 +08:00
yystopf 9cf4cc290c 更改:请求参数查询 2025-07-25 15:01:36 +08:00
yystopf e37d76d972 更改:请求参数查询 2025-07-25 14:58:08 +08:00
yystopf c18d1d2ce4 新增:组织项目查询路由 2025-07-25 14:53:03 +08:00
yystopf 40576e321f fixed 2025-07-25 14:50:27 +08:00
yystopf f9e7b92386 fixed 2025-07-25 14:48:50 +08:00
yystopf 40955f5a99 新增:组织下查询接口以及组织成员数 2025-07-25 14:46:21 +08:00
yystopf cece63286e 更改:排序正序倒序 2025-07-24 16:09:06 +08:00
yystopf debefe2e7c 新增:排序正序与倒序 2025-07-24 10:15:49 +08:00
yystopf ad2f42bed6 新增:排序正序与倒序 2025-07-24 10:13:43 +08:00
yystopf 473b9e3dfd 新增:负责人筛选 2025-07-24 09:59:45 +08:00
xxq250 655bb7bb4e add 敏感词调整 2025-07-23 13:55:15 +08:00
xxq250 da48040d2c Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2025-07-22 16:52:49 +08:00
xxq250 732a3a1405 add 敏感词调整 2025-07-22 16:52:42 +08:00
yystopf 31e4d73e2a 新增:周报时间筛选 2025-07-22 10:20:30 +08:00
xxq250 bd3dd5df4a add 敏感词调整 2025-07-21 15:16:13 +08:00
yystopf 85b55619c6 修复:最近提交文件数过多无法加载的问题 2025-07-18 21:08:28 +08:00
xxq250 9d968ac5db add 在线用户记录数单独显示 2025-07-18 14:59:27 +08:00
xxq250 a90bed0b8f add 在线用户记录,统计缓存取消 2025-07-18 14:20:29 +08:00
xxq250 4151b3564f add 在线用户记录,统计开关 2025-07-18 14:15:16 +08:00
xxq250 bff04758b8 add 在线用户记录,统计 2025-07-18 11:55:25 +08:00
xxq250 7f386622a3 add 在线用户记录,统计 2025-07-18 11:54:52 +08:00
xxq250 9d8600eb1c add 在线用户记录 2025-07-18 11:52:24 +08:00
xxq250 981dde45ef fixed 增加代码库文本内容显示敏感词检测3 2025-07-07 15:52:44 +08:00
xxq250 b02cf8fe79 fixed 增加代码库文本内容显示敏感词检测2 2025-07-07 15:47:13 +08:00
xxq250 44c8d0be7c fixed 增加代码库文本内容显示敏感词检测 2025-07-07 15:42:07 +08:00
xxq250 4afce7809d fixed 敏感词不显示,方法改为tip_exception 2025-07-07 15:26:48 +08:00
xxq250 10c05896fc fixed 敏感词不显示 2025-07-07 15:05:47 +08:00
xxq250 d260fcfe35 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2025-07-02 14:57:50 +08:00
xxq250 d286502224 fixed 用户搜组织增加名称模糊搜索 2025-07-02 14:57:44 +08:00
yystopf cabc9775bc 更改:导入同步项目失败处理 2025-07-02 14:56:27 +08:00
yystopf 8c9f6d6175 新增:同步github组织下项目 2025-07-01 16:19:15 +08:00
yystopf 0ae4f8decd Merge branch 'standalone_develop' of https://gitlink.org.cn/Trustie/forgeplus into standalone_develop 2025-06-27 15:26:06 +08:00
yystopf a86372dc16 修复:周报报错 2025-06-27 15:25:59 +08:00
xxq250 3eebf61f03 fixed 项目表头像处理 2025-06-27 14:40:37 +08:00
xxq250 cf6dc3a9b4 fixed projects gpid 增加索引 2025-06-27 14:39:28 +08:00
yystopf 2a0ce2972a fixed: 返回错误跳转地址 2025-06-25 16:44:44 +08:00
yystopf 8cab37587e fixed: 返回错误跳转地址 2025-06-25 16:42:12 +08:00
yystopf 542fe0b60c fixed:同一方向pullrequest不允许存在 2025-06-25 16:12:58 +08:00
yystopf bb720d20c1 fixed:同一方向pullrequest不允许存在 2025-06-25 16:03:31 +08:00
KingChan 6bcdc20500 Merge pull request 'render pm_logo_url' (#402) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-25 10:02:06 +08:00
呱呱呱 5faf040862 update pm_logo_url render 2025-06-25 10:02:05 +08:00
KingChan 991a421b67 Merge pull request 'laboratory pm logo' (#401) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-25 09:43:58 +08:00
呱呱呱 74f928bab2 setting laboratory pm logo 2025-06-25 09:43:50 +08:00
yystopf 9cb7a22e52 fix: error返回 2025-06-24 14:21:33 +08:00
yystopf 6a7174781c 新增:重新打开pr接口 2025-06-23 19:27:47 +08:00
yystopf 880f547b3b 更改:继承模板 2025-06-21 15:42:57 +08:00
yystopf f97ad21af5 Merge branch 'standalone_develop' of https://gitlink.org.cn/Trustie/forgeplus into standalone_develop 2025-06-21 15:06:32 +08:00
yystopf 5230064b29 更改:解决加载过慢的问题以及child_count查询范围 2025-06-21 15:06:24 +08:00
xxq250 8fffa5530c Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2025-06-21 08:33:30 +08:00
yystopf 02df11cc64 更改:导入工作项默认创建者 2025-06-21 08:33:10 +08:00
yystopf ba2a76de8c 更改:导入工作项默认创建者 2025-06-20 15:09:26 +08:00
yystopf f1bf41c7f6 更改 trend project可以为空 2025-06-20 15:07:45 +08:00
yystopf 1a282dc925 fix: 用户导入导出按钮隐藏 2025-06-20 13:58:32 +08:00
yystopf 829e6bfe02 更改:未拥有权限去除新增按钮 2025-06-20 11:22:10 +08:00
yystopf c3754f65cd 更改:运营人员菜单权限 2025-06-20 10:32:01 +08:00
yystopf 16a64027f1 更改:运营人员菜单权限 2025-06-20 10:29:22 +08:00
yystopf e6eea663a9 更改:运营人员菜单权限 2025-06-20 10:25:24 +08:00
KingChan 52b46f4b89 Merge pull request 'update' (#396) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-19 16:46:10 +08:00
呱呱呱 e7795faa22 update 2025-06-19 16:46:03 +08:00
KingChan 831576d4aa Merge pull request 'admin端删除组织记录渲染组织信息' (#395) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-19 16:39:34 +08:00
呱呱呱 2b9e53565a admin user action DestroyOrganization render 2025-06-19 16:39:24 +08:00
KingChan 76d3c29c42 Merge pull request '修复删除bug' (#394) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-19 16:27:45 +08:00
呱呱呱 346b815e0a fix bug 2025-06-19 16:27:33 +08:00
KingChan 7496a4bdd6 Merge pull request '增加用户删除组织时创建记录' (#393) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-19 16:24:35 +08:00
呱呱呱 470064ecd5 add Organization destroy create action 2025-06-19 16:24:02 +08:00
KingChan 974213ff59 Merge pull request '修复删除组织时记录组织信息没被记录的问题' (#392) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-19 15:54:36 +08:00
呱呱呱 b52aa0633e user action DestroyOrganization data_bank 2025-06-19 15:54:02 +08:00
xxq250 76545cf72f add 克隆检测 2025-06-19 14:10:16 +08:00
KingChan 14350f6812 Merge pull request '调整百度统计计算方法' (#391) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-19 09:49:50 +08:00
呱呱呱 0bc702b957 fix baidu tongji bug 2025-06-19 09:49:35 +08:00
KingChan d5b0fccf03 Merge pull request '调整bing网站的统计数据' (#390) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-18 14:56:30 +08:00
呱呱呱 2ff38ad402 update bing from link to search 2025-06-18 14:56:13 +08:00
KingChan 63593535a7 Merge pull request 'my_issues 增加按照创建时间倒序' (#389) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-18 10:34:10 +08:00
呱呱呱 f767d95730 pm my_issues add order desc 2025-06-18 10:33:42 +08:00
KingChan e317fafb89 Merge pull request 'task 128017 管理员界面增加删除组织下拉菜单' (#388) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-18 10:10:26 +08:00
呱呱呱 27d73f0c11 add admin user_action view filter 2025-06-18 10:09:46 +08:00
xxq250 a8f06eb022 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2025-06-17 11:17:28 +08:00
xxq250 6598eb6b76 fixed 登录更新时间5分钟写入一次 2025-06-17 11:17:21 +08:00
yystopf c97188aff8 Merge branch 'standalone_develop' of https://gitlink.org.cn/Trustie/forgeplus into standalone_develop 2025-06-09 14:09:26 +08:00
yystopf 7d466217c7 更改:查询本周和下周工作项需要增加时间参数 2025-06-09 14:09:14 +08:00
KingChan d86d7f5d3e Merge pull request 'update sonar' (#386) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-07 16:42:46 +08:00
呱呱呱 a2ab42f392 update 2025-06-07 16:42:38 +08:00
KingChan a3517e651f Merge pull request 'update sonar' (#384) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-07 16:27:25 +08:00
呱呱呱 3e859e4469 fix sonar exec error 2025-06-07 16:27:06 +08:00
KingChan 74706ab7a2 Merge pull request 'update sonar' (#382) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-07 16:00:48 +08:00
呱呱呱 8fa20c7f6b java sonar 2025-06-07 16:00:14 +08:00
xxq250 f96bcfa162 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2025-06-07 15:01:47 +08:00
xxq250 145463f967 fixed 流水线删除时删除运行记录错误 2025-06-07 15:01:40 +08:00
yystopf 6a6698bc74 Merge branch 'standalone_develop' of https://gitlink.org.cn/Trustie/forgeplus into standalone_develop 2025-06-07 14:51:02 +08:00
yystopf 41cbeae2f7 更改:查看我负责或组织工作项需要按层级展示 2025-06-07 14:48:48 +08:00
KingChan 9645f38e31 Merge pull request 'update sonar' (#380) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-07 14:27:29 +08:00
呱呱呱 fa26098426 update sonar 2025-06-07 14:27:20 +08:00
KingChan 6c937e6af6 Merge pull request 'update sonar' (#375) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-07 11:54:52 +08:00
呱呱呱 ef06a808a9 fix sonar 2025-06-07 11:54:45 +08:00
KingChan 6d51061f49 Merge pull request 'update sonar' (#373) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-07 11:13:41 +08:00
呱呱呱 4eaf5aada2 update sonar 2025-06-07 11:12:48 +08:00
KingChan fb461b2fe6 Merge pull request 'update sonar' (#371) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-07 10:51:25 +08:00
呱呱呱 045d324809 update sonar 2025-06-07 10:51:09 +08:00
KingChan e03e011f18 Merge pull request 'update sonar' (#369) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-07 10:41:46 +08:00
呱呱呱 055e916d08 update 2025-06-07 10:39:25 +08:00
KingChan 37e28fdf2e Merge pull request 'update sonar' (#367) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-07 10:32:37 +08:00
呱呱呱 1a755048bb update 2025-06-07 10:32:02 +08:00
KingChan fcabd83a9d Merge pull request 'update' (#365) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-07 10:13:26 +08:00
呱呱呱 387f33b2b0 update 2025-06-07 10:13:11 +08:00
KingChan c0e6720cd4 Merge pull request 'update sonar' (#363) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-07 09:31:58 +08:00
呱呱呱 737b6bc18f update 2025-06-07 09:31:46 +08:00
KingChan 616e99cc86 Merge pull request 'update sonar' (#361) from KingChan/forgeplus:standalone_develop into standalone_develop 2025-06-07 08:46:52 +08:00
呱呱呱 ee47f29264 sonar 2025-06-07 08:46:35 +08:00
260 changed files with 2977 additions and 290615 deletions

View File

@ -134,11 +134,11 @@ $(document).on('turbolinks:load', function() {
rules: {
"user[password]": {
required: false,
minlength: 5
minlength: 8
},
"user[password_confirmation]": {
required: false,
minlength: 5,
minlength: 8,
equalTo: "#user_password"
},
},

View File

@ -292,6 +292,7 @@ class AccountsController < ApplicationController
UserAction.create(:action_id => user.try(:id), :action_type => "Login", :user_id => user.try(:id), :ip => request.remote_ip)
user.update_column(:last_login_on, Time.now)
session[:"#{default_yun_session}"] = user.id
UserOnline.login(user.id)
Rails.logger.info("#########_____session_default_yun_session__________###############{default_yun_session}")
# 注册完成后有一天的试用申请(先去掉)
# UserDayCertification.create(user_id: user.id, status: 1)

View File

@ -1,5 +1,5 @@
class Action::NodeInputsController < ApplicationController
before_action :require_admin, except: [:index]
before_action :require_login, except: [:index]
before_action :find_action_node
def index

View File

@ -1,6 +1,6 @@
class Action::NodeSelectsController < ApplicationController
before_action :require_admin, except: [:index]
before_action :require_loginrequire_login, except: [:index]
before_action :find_action_node
def index

View File

@ -1,5 +1,5 @@
class Action::NodeTypesController < ApplicationController
before_action :require_admin, except: [:index]
before_action :require_login, except: [:index]
before_action :find_node_type, except: [:index, :create, :new]
def index

View File

@ -1,5 +1,5 @@
class Action::TemplatesController < ApplicationController
before_action :require_admin, except: [:index]
before_action :require_login, except: [:index]
before_action :find_action_template, except: [:index, :create, :new]
def index

View File

@ -1,4 +1,6 @@
class Admins::DashboardsController < Admins::BaseController
before_action :require_business
def index
# 用户活跃数
day_user_ids = CommitLog.where(created_at: today).pluck(:user_id).uniq
@ -65,17 +67,16 @@ class Admins::DashboardsController < Admins::BaseController
CommitLog.count
end
@subject_name = ["用户数", "项目数", "组织数", "Issue数", "Issue评论数", "PR数", "Commit数"]
@subject_icon = ["fa-user","fa-git", "fa-sitemap", "fa-warning", "fa-comments", "fa-share-alt", "fa-upload"]
@subject_data = [@user_count, @project_count, @organization_count, @issue_count, @comment_count, @pr_count, @commit_count]
@subject_name = ["用户数", "项目数", "组织数", "Issue数", "Issue评论数", "PR数", "Commit数", "在线用户数"]
@subject_icon = ["fa-user","fa-git", "fa-sitemap", "fa-warning", "fa-comments", "fa-share-alt", "fa-upload", "fa-user"]
@subject_data = [@user_count, @project_count, @organization_count, @issue_count, @comment_count, @pr_count, @commit_count, UserOnline.count]
if EduSetting.get("open_baidu_tongji").to_s == "true"
tongji_service = Baidu::TongjiService.new
@access_token = tongji_service.access_token
Rails.logger.info "baidu_tongji_auth access_token ===== #{@access_token}"
# @overview_data = tongji_service.api_overview
last_date = DailyPlatformStatistic.order(:date).last || Time.now
start_date = last_date.date
start_date = DailyPlatformStatistic.order(:date).last.try(:date) || Time.now.to_date
end_date = Time.now
if @access_token.present?
@overview_data = Rails.cache.fetch("dashboardscontroller:baidu_tongji:overview_data", expires_in: 10.minutes) do

View File

@ -23,7 +23,7 @@ class Admins::LaboratorySettingsController < Admins::BaseController
def form_params
params.permit(:identifier, :name,
:nav_logo, :login_logo, :tab_logo, :oj_banner,
:nav_logo, :login_logo, :tab_logo, :pm_logo, :oj_banner,
:subject_banner, :course_banner, :competition_banner, :moop_cases_banner,
:footer, navbar: %i[name link hidden index])
end

View File

@ -1,5 +1,5 @@
class Admins::OrganizationsController < Admins::BaseController
before_action :require_admin
before_action :require_business
before_action :finder_org, except: [:index]
def index
@ -30,9 +30,11 @@ class Admins::OrganizationsController < Admins::BaseController
end
def destroy
data_bank = @org.attributes.to_json
@org.destroy!
Admins::DeleteOrganizationService.call(@org.login)
UserAction.create(action_id: @org.id, action_type: "DestroyOrganization", user_id: current_user.id, :ip => request.remote_ip, data_bank: @org.attributes.to_json)
data_bank = @org.attributes.to_json
UserAction.create(action_id: @org.id, action_type: "DestroyOrganization", user_id: current_user.id, :ip => request.remote_ip, data_bank: data_bank)
render_delete_success
end

View File

@ -1,5 +1,5 @@
class Admins::ProjectCategoriesController < Admins::BaseController
before_action :require_admin
before_action :require_business
before_action :get_category, only: [:edit,:update, :destroy]
before_action :validate_names, only: [:create, :update]

View File

@ -1,5 +1,5 @@
class Admins::ProjectIgnoresController < Admins::BaseController
before_action :require_admin
before_action :require_business
before_action :set_ignore, only: [:edit,:update, :destroy,:show]
# before_action :validate_params, only: [:create, :update]

View File

@ -1,5 +1,5 @@
class Admins::ProjectLanguagesController < Admins::BaseController
before_action :require_admin
before_action :require_business
before_action :get_language, only: [:edit,:update, :destroy]
before_action :validate_names, only: [:create, :update]

View File

@ -1,5 +1,5 @@
class Admins::ProjectLicensesController < Admins::BaseController
before_action :require_admin
before_action :require_business
before_action :set_license, only: [:edit,:update, :destroy,:show]
# before_action :validate_params, only: [:create, :update]

View File

@ -1,5 +1,5 @@
class Admins::ProjectsController < Admins::BaseController
before_action :require_admin
before_action :require_business
before_action :find_project, only: [:edit, :update]
def index
@ -16,6 +16,8 @@ class Admins::ProjectsController < Admins::BaseController
projects = projects.where.not(forked_from_project_id: nil)
when 'original'
projects = projects.where(forked_from_project_id: nil, project_type: 'common')
when 'mirror'
projects = projects.where(project_type: 'sync_mirror')
end
@projects = paginate projects.includes(:owner, :members, :issues, :versions, :attachments, :project_score)
end

View File

@ -1,5 +1,5 @@
class Admins::UsersController < Admins::BaseController
before_action :require_admin
before_action :require_business
before_action :finder_user, except: [:index]
def index

View File

@ -18,10 +18,10 @@ class Api::Pm::DashboardsController < Api::Pm::BaseController
return render_error('请输入正确的pm_issue_types.') if params[:pm_issue_types].blank?
pm_project_ids = params[:pm_project_ids].split(",") rescue []
pm_issue_types = params[:pm_issue_types].split(",") rescue []
@all_issues = Issue.where(pm_project_id: pm_project_ids, pm_issue_type: pm_issue_types)
@all_issues = Issue.where(pm_project_id: pm_project_ids, pm_issue_type: pm_issue_types).order(created_on: :desc)
@issues = @all_issues.joins(:issue_participants).where(issue_participants: {participant_id: current_user.id, participant_type: 'assigned'})
@issues = @issues.where.not(status_id: 5) if params[:category] == "opened"
@issues = @issues.where("issues.id > 0")
@issues = kaminari_paginate(@issues.distinct.pm_includes)
@my_assign_requirements_count = @all_issues.where(pm_issue_type: 1).joins(:issue_participants).where(issue_participants: {participant_id: current_user.id, participant_type: 'assigned'}).size
@my_assign_tasks_count = @all_issues.where(pm_issue_type: 2).joins(:issue_participants).where(issue_participants: {participant_id: current_user.id, participant_type: 'assigned'}).size

View File

@ -177,7 +177,7 @@ class Api::Pm::IssuesController < Api::Pm::BaseController
issue.fake_id = row[0]
issue.subject = row[1]
issue.description = row[2]
author = User.find_by(login: row[3])
author = User.find_by(login: row[3]) || User.where(admin: true).first
issue.user = author
issue.created_on = row[4]
changer = User.find_by(login: row[5])

View File

@ -8,9 +8,15 @@ class Api::Pm::PipelinesController < Api::Pm::BaseController
tip_exception('没有查看组织的权限')
end
@project_ids = @owner.projects.ids
project_gpids = @owner.projects.pluck(:gpid)
if params[:project_ids].present?
@project_ids = params[:project_ids].split(",")
end
projects = Project.where(id: @project_ids)
projects = projects.where(is_public: ActiveModel::Type::Boolean.new.cast(params[:is_public])) if params[:is_public].present?
project_gpids = projects.pluck(:gpid)
@project_ids = projects.ids
action_runs = Gitea::ActionRun.where(owner_id: @owner.gitea_uid)
group_data = action_runs.where(status: [1,2]).group(:workflow_id, :status).count
group_data = action_runs.where(status: [1,2]).group(:workflow_id, :status, :repo_id).count
pipelines = Action::Pipeline.where(project_id: @project_ids).order(updated_at: :desc)
run_files = Gitea::ActionRun.select(:workflow_id, :repo_id).where(owner_id: @owner.gitea_uid).group(:workflow_id, :repo_id)
db_files = pipelines.pluck(:file_name)
@ -19,21 +25,24 @@ class Api::Pm::PipelinesController < Api::Pm::BaseController
file = file_info.workflow_id
project = Project.find_by(gpid: file_info.repo_id)
next if project.blank?
unless db_files.include?(".gitea/workflows/#{file}")
pipeline = Action::Pipeline.find_or_initialize_by(pipeline_name: file.to_s.gsub(".yml", "").gsub(".yaml", ""),
file_name: ".gitea/workflows/#{file}",
branch: project.default_branch,
is_graphic_design: false,
disable: false,
project_id: project.id)
interactor = Repositories::EntriesInteractor.call(@owner, project.identifier, ".gitea/workflows/#{file}", ref: project.default_branch)
if interactor.success?
pipeline.yaml = decode64_content(interactor.result, @owner, project.repository, project.default_branch, nil)
if params[:generate_actions].present?
unless db_files.include?(".gitea/workflows/#{file}")
pipeline = Action::Pipeline.find_or_initialize_by(pipeline_name: file.to_s.gsub(".yml", "").gsub(".yaml", ""),
file_name: ".gitea/workflows/#{file}",
branch: project.default_branch,
disable: false,
project_id: project.id)
interactor = Repositories::EntriesInteractor.call(@owner, project.identifier, ".gitea/workflows/#{file}", ref: project.default_branch)
if interactor.success?
pipeline.yaml = decode64_content(interactor.result, @owner, project.repository, project.default_branch, nil)
file = interactor.result
pipeline.sha = file['content']['sha']
end
pipeline.user_id = current_user.id
pipeline.save
# 导入的流水线统一先禁用
# $gitea_hat_client.post_repos_actions_disable(project&.owner&.login, project&.identifier, {query: {workflow: file}}) rescue nil
end
pipeline.user_id = current_user.id
pipeline.save
# 导入的流水线统一先禁用
$gitea_hat_client.post_repos_actions_disable(project&.owner&.login, project&.identifier, {query: {workflow: file}}) rescue nil
end
last_action_run = action_runs.where(repo_id: project.gpid).where(workflow_id: file).order(updated: :desc).first
last_action_run_json = last_action_run.present? ? {
@ -42,9 +51,9 @@ class Api::Pm::PipelinesController < Api::Pm::BaseController
title: last_action_run.title,
index: last_action_run.index,
status: last_action_run.status,
started: last_action_run.started,
stopped: last_action_run.stopped,
length: last_action_run.stopped-last_action_run.started,
started: last_action_run.started.zero? ? Time.now.to_i : last_action_run.started,
stopped: last_action_run.stopped.zero? ? Time.now.to_i : last_action_run.stopped,
length: last_action_run.stopped.zero? ? 0 : last_action_run.stopped-last_action_run.started,
created: last_action_run.created,
updated: last_action_run.updated,
} : {}
@ -53,12 +62,12 @@ class Api::Pm::PipelinesController < Api::Pm::BaseController
success = 0
failure = 0
group_data.each do |k,v|
total += v if k[0] == file
success += v if k[0] == file && k[1] == 1
failure += v if k[0] == file && k[1] == 2
total += v if k[0] == file && k[2] == file_info.repo_id
success += v if k[0] == file && k[1] == 1 && k[2] == file_info.repo_id
failure += v if k[0] == file && k[1] == 2 && k[2] == file_info.repo_id
end
@run_result << {
repo_id: last_action_run.repo_id,
repo_id: last_action_run&.repo_id,
filename: ".gitea/workflows/#{file}",
total: total,
success: success,
@ -67,8 +76,8 @@ class Api::Pm::PipelinesController < Api::Pm::BaseController
end
# Rails.logger.info("@run_result======#{@run_result}")
@disabled_workflows = Gitea::RepoUnit.where(repo_id: project_gpids, type: 10).where("config is not null")
@pipelines = Action::Pipeline.select("distinct project_id,max(updated_at) as updated_at")
.where(project_id: @project_ids).group(:project_id).order("updated_at desc")
@pipelines = Action::Pipeline.select("distinct project_id,max(updated_at) as updated_at, max(created_at) as created_at")
.where(project_id: @project_ids).group(:project_id).order("created_at desc")
@pipelines = @pipelines.where("pipeline_name like ?", "%#{params[:pipeline_name]}%") if params[:pipeline_name].present?
@pipelines = @pipelines.where(pipeline_type: params[:pipeline_type]) if params[:pipeline_type].present?
@pipelines = kaminari_paginate(@pipelines)

View File

@ -5,16 +5,18 @@ class Api::Pm::WeeklyIssuesController < Api::Pm::BaseController
return render_error('请输入正确的用户ID.') if params[:user_id].blank?
@all_issues = Issue.joins(:issue_participants).where(issue_participants: {participant_id: params[:user_id], participant_type: ['assigned']})
@all_issues = @all_issues.where(enterprise_identifier: @enterprise_identifier, pm_issue_type: [1,2,3])
@this_week_all_issues = @all_issues.where("due_date >= ? and start_date <= ?", Date.today.beginning_of_week.to_s, Date.today.end_of_week.to_s).distinct
@this_week_all_issues = @this_week_all_issues.order('created_on desc')
@next_week_all_issues = @all_issues.where("due_date >= ? and start_date <=?", (Date.today.beginning_of_week+1.week).to_s, (Date.today.end_of_week+1.week).to_s).distinct
@next_week_all_issues = @next_week_all_issues.order('created_on desc')
@weekly_begin_date = params[:weekly_begin_date].to_time || Date.today.beginning_of_week rescue Date.today.beginning_of_week
@weekly_end_date = params[:weekly_end_date].to_time || Date.today.end_of_week rescue Date.today.end_of_week
@this_week_all_issues = @all_issues.where("due_date >= ? and start_date <= ?", @weekly_begin_date.to_s, @weekly_end_date.to_s).distinct
@this_week_all_issues = @this_week_all_issues.order('created_on desc').pm_includes
@next_week_all_issues = @all_issues.where("due_date >= ? and start_date <=?", (@weekly_begin_date+1.week).to_s, (@weekly_end_date+1.week).to_s).distinct
@next_week_all_issues = @next_week_all_issues.order('created_on desc').pm_includes
@this_week_requirement_issues = @this_week_all_issues.where(pm_issue_type: 1)
@this_week_task_issues = @this_week_all_issues.where(pm_issue_type: 2)
@this_week_bug_issues = @this_week_all_issues.where(pm_issue_type: 3)
@close_requirement_issues = @this_week_requirement_issues.where(status_id: [3,5])
@close_task_issues = @this_week_task_issues.where(status_id: [3,5])
@close_bug_issues = @this_week_bug_issues.where(status_id: [3,5])
@close_requirement_issues = @this_week_requirement_issues.where(status_id: [3,5]).pm_includes
@close_task_issues = @this_week_task_issues.where(status_id: [3,5]).pm_includes
@close_bug_issues = @this_week_bug_issues.where(status_id: [3,5]).pm_includes
this_week_page = params[:this_week_page] || 1
this_week_limit = params[:this_week_limit] || 15
@this_week_all_issues = @this_week_all_issues.page(this_week_page).per(this_week_limit)
@ -27,7 +29,9 @@ class Api::Pm::WeeklyIssuesController < Api::Pm::BaseController
@enterprise_identifier = params[:enterprise_identifier] || ''
@all_issues = Issue.where(enterprise_identifier: @enterprise_identifier, pm_issue_type: [1,2,3])
@all_issues = @all_issues.where(pm_project_id: params[:pm_project_ids].split(",")) if params[:pm_project_ids].present?
@this_week_all_issues = @all_issues.where("due_date >= ? and start_date <= ?", Date.today.beginning_of_week.to_s, Date.today.end_of_week.to_s)
@weekly_begin_date = params[:weekly_begin_date] || Date.today.beginning_of_week.to_s
@weekly_end_date = params[:weekly_end_date] || Date.today.end_of_week.to_s
@this_week_all_issues = @all_issues.where("due_date >= ? and start_date <= ?", @weekly_begin_date, @weekly_end_date)
@this_week_all_issues_group_count = @this_week_all_issues.group(:pm_project_id).count
data = {}
@this_week_all_issues_group_count.keys.each do |pm_project_id|
@ -55,22 +59,40 @@ class Api::Pm::WeeklyIssuesController < Api::Pm::BaseController
def group_issues
@enterprise_identifier = params[:enterprise_identifier] || ''
@all_issues = Issue.where(enterprise_identifier: @enterprise_identifier, pm_issue_type: [1,2,3])
root_id = params[:root_id] || -1
@weekly_begin_date = params[:weekly_begin_date] || Date.today.beginning_of_week.to_s
@weekly_end_date = params[:weekly_end_date] || Date.today.end_of_week.to_s
@all_issues = Issue.none
if root_id.to_i == -1
@all_issues = Issue.where(root_id: nil, enterprise_identifier: @enterprise_identifier, pm_issue_type: [1,2,3])
elsif root_id.to_i.positive?
@all_issues = Issue.where(root_id: root_id, enterprise_identifier: @enterprise_identifier, pm_issue_type: [1,2,3])
else
@all_issues = Issue.where(enterprise_identifier: @enterprise_identifier, pm_issue_type: [1,2,3])
end
@all_issues = @all_issues.where(pm_project_id: params[:pm_project_ids].split(",")) if params[:pm_project_ids].present?
@all_issues = @all_issues.where(pm_issue_type: params[:pm_issue_type].to_i) if params[:pm_issue_type].present?
@this_week_all_issues = @all_issues.where("due_date >= ? and start_date <= ?", Date.today.beginning_of_week.to_s, Date.today.end_of_week.to_s)
@this_week_all_issues = @this_week_all_issues.order('created_on desc')
@this_week_all_issues = @all_issues.where("due_date >= ? and start_date <= ?", @weekly_begin_date, @weekly_end_date)
@this_week_all_issues = @this_week_all_issues.order('created_on desc').pm_includes
@this_week_all_issues = kaminari_paginate(@this_week_all_issues)
end
def personal_issues
@enterprise_identifier = params[:enterprise_identifier] || ''
return render_error('请输入正确的用户ID.') if params[:user_id].blank?
@all_issues = Issue.joins(:issue_participants).where(issue_participants: {participant_id: params[:user_id], participant_type: ['assigned']})
@weekly_begin_date = params[:weekly_begin_date] || Date.today.beginning_of_week.to_s
@weekly_end_date = params[:weekly_end_date] || Date.today.end_of_week.to_s
root_id = params[:root_id] || -1
@all_issues = Issue.none
if root_id.to_i == -1
@all_issues = Issue.joins(:issue_participants).where(root_id: nil, issue_participants: {participant_id: params[:user_id], participant_type: ['assigned']})
else
@all_issues = Issue.joins(:issue_participants).where(root_id: root_id, issue_participants: {participant_id: params[:user_id], participant_type: ['assigned']})
end
@all_issues = @all_issues.where(enterprise_identifier: @enterprise_identifier, pm_issue_type: [1,2,3])
@this_week_all_issues = @all_issues.where("due_date >= ? and start_date <= ?", Date.today.beginning_of_week.to_s, Date.today.end_of_week.to_s).distinct
@this_week_all_issues = @this_week_all_issues.order('created_on desc')
@next_week_all_issues = @all_issues.where("due_date >= ? and start_date <=?", (Date.today.beginning_of_week+1.week).to_s, (Date.today.end_of_week+1.week).to_s).distinct
@next_week_all_issues = @next_week_all_issues.order('created_on desc')
@this_week_all_issues = @all_issues.where("due_date >= ? and start_date <= ?", @weekly_begin_date, @weekly_end_date).distinct
@this_week_all_issues = @this_week_all_issues.order('created_on desc').pm_includes
@next_week_all_issues = @all_issues.where("due_date >= ? and start_date <=?", (@weekly_begin_date.to_date+1.week).to_s, (@weekly_end_date.to_date+1.week).to_s).distinct
@next_week_all_issues = @next_week_all_issues.order('created_on desc').pm_includes
this_week_page = params[:this_week_page] || 1
this_week_limit = params[:this_week_limit] || 15
@this_week_all_issues = @this_week_all_issues.page(this_week_page).per(this_week_limit)

View File

@ -37,6 +37,11 @@ class Api::V1::BaseController < ApplicationController
params.fetch(:page, 1)
end
def load_organization
@organization = Organization.find_by(login: params[:owner]) || Organization.find_by(id: params[:owner])
return render_not_found("组织不存在") if @organization.nil?
end
# 具有对仓库的管理权限
def require_manager_above
@project = load_project

View File

@ -46,7 +46,7 @@ class Api::V1::Issues::JournalsController < Api::V1::BaseController
end
def load_issue
@issue = @project.issues.issue_issue.where(project_issues_index: params[:index]).where.not(id: params[:index]).take || @project.issues.issue_issue.find_by_id(params[:index])
@issue = @project.issues.where(project_issues_index: params[:index]).where.not(id: params[:index]).take || @project.issues.find_by_id(params[:index])
if @issue.blank?
render_not_found("疑修不存在!")
end

View File

@ -5,6 +5,7 @@ class Api::V1::IssuesController < Api::V1::BaseController
def index
IssueTag.init_data(@project.id) unless $redis_cache.hget("project_init_issue_tags", @project.id)
ProjectTemplates::Issue.init_data(@project.id)
@object_result = Api::V1::Issues::ListService.call(@project, query_params, current_user)
@total_issues_count = @object_result[:total_issues_count]
@opened_issues_count = @object_result[:opened_issues_count]

View File

@ -0,0 +1,46 @@
class Api::V1::Organizations::ProjectsController < Api::V1::BaseController
before_action :load_organization
def index
public_projects_sql = @organization.projects.where(is_public: true).to_sql
private_projects_sql = @organization.projects
.where(is_public: false)
.joins(team_projects: {team: :team_users})
.where(team_users: {user_id: current_user.id}).to_sql
@projects = Project.from("( #{ public_projects_sql} UNION #{ private_projects_sql } ) AS projects")
# 表情处理
keywords = params[:search].to_s.each_char.select { |c| c.bytes.first < 240 }.join('')
@projects = @projects.where(is_public: params[:is_public]) if params[:is_public].present?
@projects = @projects.where(id: params[:pm_project_repository_ids].split(',')) if params[:pm_project_repository_ids].present?
@projects = @projects.where.not(id: params[:exclude_ids].to_s.split(",")) if params[:exclude_ids].present?
@projects = @projects.where(project_type: ['mirror', 'common']).where("gpid is not null") if params[:actived].present?
@projects = @projects.ransack(name_or_identifier_cont: keywords).result if params[:search].present?
@projects = @projects.includes(:owner).order("projects.#{sort} #{sort_direction}")
@projects = kaminari_paginate(@projects)
end
private
def load_organization
@organization = Organization.find_by(login: params[:owner]) || Organization.find_by(id: params[:owner])
return render_not_found("组织不存在") if @organization.nil?
return render_forbidden("没有查看组织的权限") if org_limited_condition || org_privacy_condition
end
def org_limited_condition
@organization.organization_extension.limited? && !current_user.logged?
end
def org_privacy_condition
return false if current_user.admin?
@organization.organization_extension.privacy? && @organization.organization_users.where(user_id: current_user.id).blank?
end
def sort
Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'updated_on'
end
def sort_direction
%w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc'
end
end

View File

@ -0,0 +1,42 @@
class Api::V1::Organizations::SyncRepositoriesController < Api::V1::BaseController
before_action :load_organization, only: [:create]
def create
return render_error("请输入正确的外部组织login") unless params[:external_login].present?
# 拉取外部组织仓库列表
url = URI("https://api.github.com/orgs/#{params[:external_login]}/repos?page=#{params[:page]}&per_page=#{params[:limit]}")
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = "Bearer #{params[:external_token]}"
response = https.request(request)
if response.code == "200"
repos = JSON.parse(response.body)
repos.each do |repo|
# 检查是否已经存在该仓库
@project = @organization.projects.find_by(identifier: repo["name"])
if @project.present?
if @project.repository.mirror.failed?
@project.destroy!
mirror_params = {name: repo["name"], user_id: @organization.id, description: repo["description"], repository_name: repo["name"], auth_token: params[:external_token], clone_addr: repo["clone_url"]}
@project = ::Projects::MigrateService.call(current_user, mirror_params)
end
@project.update_column(:created_on, repo['created_at'].to_time)
@project.update_column(:updated_on, repo['updated_at'].to_time)
next
else
mirror_params = {name: repo["name"], user_id: @organization.id, description: repo["description"], repository_name: repo["name"], auth_token: params[:external_token], clone_addr: repo["clone_url"]}
@project = ::Projects::MigrateService.call(current_user, mirror_params)
@project.update_column(:created_on, repo['created_at'].to_time)
@project.update_column(:updated_on, repo['updated_at'].to_time)
end
end
end
end
private
def sync_repository_params
params.permit(:external_token)
end
end

View File

@ -2,7 +2,7 @@ class Api::V1::ProjectTopicsController < Api::V1::BaseController
def index
@project_topics = ProjectTopic
@project_topics = @project_topics.ransack(name_cont: params[:keyword]) if params[:keyword].present?
@project_topics = @project_topics.ransack(name_cont: params[:keyword]).result if params[:keyword].present?
# @project_topics = @project_topics.includes(:projects)
@project_topics = kaminary_select_paginate(@project_topics)
end

View File

@ -1,10 +1,10 @@
class Api::V1::Projects::Actions::RunsController < Api::V1::Projects::Actions::BaseController
def index
@files = $gitea_client.get_repos_contents_by_owner_repo_filepath(@project&.owner&.login, @project&.identifier, ".gitea/workflows") rescue []
@files = $gitea_client.get_repos_contents_by_owner_repo_filepath(@project&.owner&.login, @project&.identifier, ".gitea/workflows", {query: {ref: params[:ref]}}) rescue []
@has_file = @files.select { |i| i['name'] == params[:workflow] }.present?
if @has_file
@result_object = Api::V1::Projects::Actions::Runs::ListService.call(@project, {workflow: params[:workflow], page: page, limit: limit}, current_user&.gitea_token)
@result_object = Api::V1::Projects::Actions::Runs::ListService.call(@project, {workflow: params[:workflow], page: page, limit: limit, ref: params[:ref]}, current_user&.gitea_token)
@begin_num = (page.to_i - 1) * limit.to_i
end
end
@ -12,7 +12,7 @@ class Api::V1::Projects::Actions::RunsController < Api::V1::Projects::Actions::B
def create
return render_error("请输入正确的流水线文件!") if params[:workflow].blank?
return render_error("请输入正确的分支!") if params[:ref].blank?
gitea_result = $gitea_hat_client.post_repos_actions_runs_by_owner_repo(@project&.owner&.login, @project&.identifier, {query: {workflow: params[:workflow], ref: params[:ref]}})
gitea_result = $gitea_hat_client.post_repos_actions_runs_by_owner_repo(@project&.owner&.login, @project&.identifier, {query: {workflow: params[:workflow], ref: params[:ref], access_token: current_user.gitea_token}})
if gitea_result
render_ok
else
@ -22,7 +22,7 @@ class Api::V1::Projects::Actions::RunsController < Api::V1::Projects::Actions::B
def rerun
return render_error("请输入正确的流水线记录ID") if params[:run_id].blank?
gitea_result = $gitea_hat_client.post_repos_actions_runs_rerun_by_owner_repo_run(@project&.owner&.login, @project&.identifier, params[:run_id]) rescue nil
gitea_result = $gitea_hat_client.post_repos_actions_runs_rerun_by_owner_repo_run(@project&.owner&.login, @project&.identifier, params[:run_id], {query: {access_token: current_user.gitea_token}}) rescue nil
if gitea_result
render_ok
else
@ -33,7 +33,7 @@ class Api::V1::Projects::Actions::RunsController < Api::V1::Projects::Actions::B
def job_rerun
return render_error("请输入正确的流水线记录ID") if params[:run_id].blank?
return render_error("请输入正确的流水线任务ID") if params[:job].blank?
gitea_result = $gitea_hat_client.post_repos_actions_runs_jobs_rerun_by_owner_repo_run_job(@project&.owner&.login, @project&.identifier, params[:run_id], params[:job]) rescue nil
gitea_result = $gitea_hat_client.post_repos_actions_runs_jobs_rerun_by_owner_repo_run_job(@project&.owner&.login, @project&.identifier, params[:run_id], params[:job], {query: {access_token: current_user.gitea_token}}) rescue nil
if gitea_result
render_ok
else
@ -58,4 +58,15 @@ class Api::V1::Projects::Actions::RunsController < Api::V1::Projects::Actions::B
redirect_to file_path
end
def cancel
return render_error("请输入正确的流水线记录ID") if params[:run_id].blank?
http = Gitea::Api::Hat::Http.new($gitea_hat_client.config)
gitea_result = http.post("/repos/#{@project&.owner&.login}/#{@project&.identifier}/actions/runs/#{params[:run_id]}/cancel") rescue nil
if gitea_result
render_ok
else
render_error("停止流水线任务失败")
end
end
end

View File

@ -24,7 +24,25 @@ class Api::V1::Projects::BranchesController < Api::V1::BaseController
render :json => response.read_body
end
def index
def osredm
url = URI("https://osredm.com/api/#{params[:owner]}/#{params[:repo]}/branches?access_token=#{params[:token]}&page=#{page}&per_page=#{limit}")
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Get.new(url)
# request["Authorization"] = "Bearer #{params[:token]}"
response = https.request(request)
res = JSON.parse(response.body)
if res[0].blank? && res['status'].to_i == 401
render :json => { "message": "无效token" }
return
end
render :json => response.read_body
end
def index
@result_object = Api::V1::Projects::Branches::ListService.call(@project, {name: params[:keyword], state: params[:state], page: page, limit: limit}, current_user&.gitea_token)
end
@ -32,15 +50,35 @@ class Api::V1::Projects::BranchesController < Api::V1::BaseController
@result_object = Api::V1::Projects::Branches::AllListService.call(@project, current_user&.gitea_token)
end
before_action :require_operate_above, only: [:create, :destroy, :restore]
before_action :require_operate_above, only: [:create, :delete, :destroy, :restore]
def create
@result_object = Api::V1::Projects::Branches::CreateService.call(@project, branch_params, current_user&.gitea_token)
@project.update_column(:updated_on, Time.now)
end
def delete
@result_object = Api::V1::Projects::Branches::DeleteService.call(@project, params[:branch_name], current_user&.gitea_token)
if @result_object
@project.update_column(:updated_on, Time.now)
# 有开启的pr需要一同关闭
# 1、删除本仓库中存在未关闭的pr,即本仓库分支1->分支2
# 2、如果是fork仓库考虑删除主仓库中存在未关闭的pr,即本仓库分支1->主分支2同时分两种删除1删除本仓库分支12删除主仓库分支2
close_pull_requests_by(@project, params[:branch_name])
if @project.forked_from_project_id.present?
# fork项目中删除分支
close_pull_requests_by(@project.fork_project, params[:branch_name])
end
return render_ok
else
return render_error('删除分支失败!')
end
end
def destroy
@result_object = Api::V1::Projects::Branches::DeleteService.call(@project, params[:name], current_user&.gitea_token)
@name = params[:name].include?('.json') ? params[:name][0..-6] : params[:name]
@result_object = Api::V1::Projects::Branches::DeleteService.call(@project, Base64.decode64(@name), current_user&.gitea_token)
if @result_object
@project.update_column(:updated_on, Time.now)
# 有开启的pr需要一同关闭

View File

@ -12,7 +12,7 @@ class Api::V1::Projects::CommitsController < Api::V1::BaseController
def files
if params[:filepath].present?
@result_object = $gitea_hat_client.get_repos_commits_files_by_owner_repo_sha_filepath(@project&.owner.login, @project&.identifier, params[:sha], CGI.escape(params[:filepath]), {query: {token: current_user&.gitea_token}})
@result_object = $gitea_hat_client.get_repos_commits_files_by_owner_repo_sha_filepath(@project&.owner.login, @project&.identifier, params[:sha], URI.escape(params[:filepath]), {query: {token: current_user&.gitea_token}})
else
@result_object = $gitea_hat_client.get_repos_commits_files_by_owner_repo_sha(@project&.owner.login, @project&.identifier, params[:sha], {query: {token: current_user&.gitea_token, page: page, limit: limit}})
end

View File

@ -1,31 +1,32 @@
class Api::V1::Projects::PipelinesController < Api::V1::BaseController
include RepositoriesHelper
include Api::ActionValidatorHelper
before_action :require_operate_above, except: [:upload_results, :run_results]
def index
pipelines = Action::Pipeline.where(project_id: @project.id).order(updated_at: :desc)
@files = $gitea_client.get_repos_contents_by_owner_repo_filepath(@project&.owner&.login, @project&.identifier, ".gitea/workflows") rescue []
@action_runs = Gitea::ActionRun.where(repo_id: @project.gpid)
group_data = @action_runs.where(status: [1,2]).group(:workflow_id, :status).count
group_data = @action_runs.where(status: [1,2]).group(:workflow_id, :status, :repo_id).count
db_files = pipelines.pluck(:file_name)
@run_result = []
@files.map { |i| i['name'] }.each do |file|
unless db_files.include?(".gitea/workflows/#{file}")
pipeline = Action::Pipeline.find_or_initialize_by(pipeline_name: file.to_s.gsub(".yml", "").gsub(".yaml", ""),
file_name: ".gitea/workflows/#{file}",
branch: @project.default_branch,
is_graphic_design: false,
disable: false,
project_id: @project.id)
interactor = Repositories::EntriesInteractor.call(@owner, @project.identifier, ".gitea/workflows/#{file}", ref: @project.default_branch)
if interactor.success?
pipeline.yaml = decode64_content(interactor.result, @owner, @project.repository, @project.default_branch, nil)
end
pipeline.user_id = current_user.id
pipeline.save
# 导入的流水线统一先禁用
$gitea_hat_client.post_repos_actions_disable(@project&.owner&.login, @project&.identifier, {query: {workflow: file}}) rescue nil
end
# unless db_files.include?(".gitea/workflows/#{file}")
# pipeline = Action::Pipeline.find_or_initialize_by(pipeline_name: file.to_s.gsub(".yml", "").gsub(".yaml", ""),
# file_name: ".gitea/workflows/#{file}",
# branch: @project.default_branch,
# is_graphic_design: false,
# disable: false,
# project_id: @project.id)
# interactor = Repositories::EntriesInteractor.call(@owner, @project.identifier, ".gitea/workflows/#{file}", ref: @project.default_branch)
# if interactor.success?
# pipeline.yaml = decode64_content(interactor.result, @owner, @project.repository, @project.default_branch, nil)
# end
# pipeline.user_id = current_user.id
# pipeline.save
# # 导入的流水线统一先禁用
# $gitea_hat_client.post_repos_actions_disable(@project&.owner&.login, @project&.identifier, {query: {workflow: file}}) rescue nil
# end
last_action_run = @action_runs.where(workflow_id: file).order(updated: :desc).first
last_action_run_json = last_action_run.present? ? {
id: last_action_run.id,
@ -44,9 +45,9 @@ class Api::V1::Projects::PipelinesController < Api::V1::BaseController
success = 0
failure = 0
group_data.each do |k,v|
total += v if k[0] == file
success += v if k[0] == file && k[1] == 1
failure += v if k[0] == file && k[1] == 2
total += v if k[0] == file && k[2] == @project.gpid
success += v if k[0] == file && k[1] == 1 && k[2] == @project.gpid
failure += v if k[0] == file && k[1] == 2 && k[2] == @project.gpid
end
@run_result << {
filename: ".gitea/workflows/#{file}",
@ -73,42 +74,65 @@ class Api::V1::Projects::PipelinesController < Api::V1::BaseController
end
def create
@pipeline = params[:id].present? ? Action::Pipeline.find(params[:id]) : Action::Pipeline.find_or_initialize_by(pipeline_name: params[:pipeline_name], project_id: @project.id)
branch = params[:branch] || @project.default_branch
@pipeline = params[:id].present? ? Action::Pipeline.find(params[:id]) : Action::Pipeline.find_or_initialize_by(pipeline_name: params[:pipeline_name], project_id: @project.id, branch: branch)
if @pipeline.pipeline_name != params[:pipeline_name]
has_pipeline = Action::Pipeline.where(pipeline_name: params[:pipeline_name], project_id: @project.id).where.not(id: @pipeline.id)
tip_exception("已经存在#{params[:pipeline_name]}流水线!") if has_pipeline.present?
if @pipeline.yaml.present?
sha = get_pipeline_file_sha(@pipeline.file_name, @pipeline.branch)
if sha.present?
interactor = Gitea::UpdateFileInteractor.call(current_user.gitea_token, @owner.login, content_params("update", params[:pipeline_name]).merge(sha: sha, from_path: "#{@pipeline.pipeline_name}"))
new_file_sha = get_pipeline_file_sha(".gitea/workflows/#{params[:pipeline_name]}.yml", @pipeline.branch)
# 重命名的新文件存在时直接修改新文件new_file_sha, 同时删除老文件
if sha.present? && new_file_sha.present?
interactor = Gitea::UpdateFileInteractor.call(current_user.gitea_token, @owner.login, content_params("update", params[:pipeline_name]).merge(sha: new_file_sha, from_path: ".gitea/workflows/#{params[:pipeline_name]}.yml"))
tip_exception(interactor.error) unless interactor.success?
del_interactor = Gitea::DeleteFileInteractor.call(current_user.gitea_token, @owner.login, del_content_params(sha).merge(identifier: @project.identifier))
tip_exception(del_interactor.error) unless del_interactor.success?
elsif sha.present? && new_file_sha.blank?
interactor = Gitea::UpdateFileInteractor.call(current_user.gitea_token, @owner.login, content_params("update", params[:pipeline_name]).merge(sha: sha, from_path: ".gitea/workflows/#{@pipeline.pipeline_name}.yml"))
tip_exception(interactor.error) unless interactor.success?
end
end
else
has_pipeline = Action::Pipeline.where(pipeline_name: params[:pipeline_name], project_id: @project.id)
tip_exception("已经存在#{params[:pipeline_name]}流水线!") if has_pipeline.present?
end
@pipeline.user_id = current_user.id
@pipeline.pipeline_name = params[:pipeline_name]
@pipeline.file_name = ".gitea/workflows/#{@pipeline.pipeline_name}.yml"
@pipeline.branch = params[:branch] || @project.default_branch
@pipeline.branch = branch
@pipeline.is_graphic_design = params[:pipeline_type] == 2 ? true : false
@pipeline.pipeline_type = params[:pipeline_type] if params[:pipeline_type].present?
if params[:pipelineTemplateJson].present?
@pipeline.json = params[:pipelineTemplateJson].to_unsafe_hash.to_json
@pipeline.yaml = build_pipeline_yaml_new(@pipeline.pipeline_name, params[:pipelineTemplateJson])
end
@pipeline.save!
end
def save_yaml
@pipeline = params[:id].present? ? Action::Pipeline.find(params[:id]) : Action::Pipeline.find_or_initialize_by(pipeline_name: params[:pipeline_name], project_id: @project.id)
branch = params[:branch] || @project.default_branch
@pipeline = params[:id].present? ? Action::Pipeline.find(params[:id]) : Action::Pipeline.find_or_initialize_by(pipeline_name: params[:pipeline_name], project_id: @project.id, branch: branch)
@pipeline.file_name = ".gitea/workflows/#{@pipeline.pipeline_name}.yml"
@pipeline.branch = params[:branch] if params[:branch].present?
@pipeline.json = params[:pipeline_json].to_json if params[:pipeline_json].present?
@pipeline.pipeline_name = params[:pipeline_name] if params[:pipeline_name].present?
pipeline_yaml = params[:pipeline_yaml].present? ? params[:pipeline_yaml] : build_pipeline_yaml_new(@pipeline.pipeline_name, params[:pipeline_json])
tip_exception("流水线yaml内空不能为空") if pipeline_yaml.blank?
# result = validate_string(pipeline_yaml, @pipeline.branch, @project.default_branch)
# unless result[:valid]
# tip_exception(result[:errors].join(","))
# end
@pipeline.yaml = pipeline_yaml
#Rails.logger.info "pipeline_yaml base64=========================#{Base64.encode64(@pipeline.yaml).gsub(/\n/, '')}"
sha = get_pipeline_file_sha(@pipeline.file_name, @pipeline.branch)
#Rails.logger.info "content_params=========#{content_params("create")}"
interactor = sha.present? ? Gitea::UpdateFileInteractor.call(current_user.gitea_token, @owner.login, content_params("update").merge(sha: sha)) : Gitea::CreateFileInteractor.call(current_user.gitea_token, @owner.login, content_params("create"))
interactor = sha.present? ? Gitea::UpdateFileInteractor.call(current_user.gitea_token, @owner.login, content_params("update").merge(sha: sha, from_path: ".gitea/workflows/#{@pipeline.pipeline_name}.yml")) : Gitea::CreateFileInteractor.call(current_user.gitea_token, @owner.login, content_params("create"))
tip_exception(interactor.error) unless interactor.success?
file = interactor.result
@pipeline.pipeline_type = @pipeline.json.present? ? 2 : 1
@pipeline.user_id = current_user.id if @pipeline.user_id.blank?
@pipeline.pipeline_type = params[:pipeline_json].present? ? 2 : 1
@pipeline.sha = sha.present? ? sha : file['content']['sha']
@pipeline.save
render_ok({ pipeline_yaml: pipeline_yaml, pipeline_name: @pipeline.pipeline_name, file_name: @pipeline.file_name, sha: sha.present? ? sha : file['content']['sha'] })
end
@ -148,7 +172,7 @@ class Api::V1::Projects::PipelinesController < Api::V1::BaseController
interactor = Gitea::DeleteFileInteractor.call(current_user.gitea_token, @owner.login, del_content_params(sha).merge(identifier: @project.identifier))
tip_exception(interactor.error) unless interactor.success?
end
Gitea::ActionRun.where(repo_id: @pipeline.project.gpid).destroy_all
Gitea::ActionRun.where(repo_id: @pipeline.project.gpid, workflow_id: "#{@pipeline.pipeline_name}.yml").destroy_all
@pipeline.destroy!
end
render_ok
@ -291,16 +315,22 @@ class Api::V1::Projects::PipelinesController < Api::V1::BaseController
node.node_id = input_node["id"] if input_node["id"].present?
run_values = {}
input_values = {}
env_input_values = {}
if input_node["data"]["inputs"].present?
input_node["data"]["inputs"].each do |input|
if input["name"].to_s.gsub("--", "") == "run"
run_values = run_values.merge({ "#{input["name"].gsub("--", "")}": "#{input["value"]}" })
else
input_values = input_values.merge({ "#{input["name"].gsub("--", "")}": "#{input["value"]}" })
if input["type"] == "Action::NodeInputs::Env"
env_input_values = env_input_values.merge({ "#{input["name"].gsub("--", "")}": "#{input["value"]}" })
else
input_values = input_values.merge({ "#{input["name"].gsub("--", "")}": "#{input["value"]}" })
end
end
end
node.run_values = run_values
node.input_values = input_values
node.env_input_values = env_input_values
end
node
end

View File

@ -0,0 +1,55 @@
class Api::V1::Projects::ProjectTemplatesController < Api::V1::BaseController
before_action :require_public_and_member_above, only: [:index, :show]
def index
@project_templates = ProjectTemplate.where(project_id: @project.id)
@project_templates = @project_templates.where(type: params[:type]) if params[:type].present?
@project_templates = @project_templates.order(created_at: :asc)
@project_templates = kaminari_paginate(@project_templates)
end
def show
@project_template = ProjectTemplate.find_by_id(params[:id])
if @project_template.blank?
render_not_found("模板不存在!")
end
end
before_action :require_operate_above, only: [:create, :update, :destroy]
def create
@project_template = ProjectTemplate.new(project_template_params)
@project_template.project_id = @project.id
if @project_template.save!
render_ok
else
render_error("创建模板失败!")
end
end
def update
@project_template = ProjectTemplate.find_by_id(params[:id])
return render_not_found("模板不存在!") if @project_template.blank?
if @project_template.update!(project_template_params)
render_ok
else
render_error("更新模板失败!")
end
end
def destroy
@project_template = ProjectTemplate.find_by_id(params[:id])
return render_not_found("模板不存在!") if @project_template.blank?
if @project_template.destroy!
render_ok
else
render_error("删除模板失败!")
end
end
private
def project_template_params
params.require(:project_template).permit(:type, :name, :content)
end
end

View File

@ -6,16 +6,38 @@ class Api::V1::Projects::Pulls::PullsController < Api::V1::BaseController
@pulls = kaminari_paginate(@pulls)
end
before_action :load_pull_request, only: [:show, :files]
before_action :load_pull_request, only: [:show, :reopen, :files]
def show
@result_object = Api::V1::Projects::Pulls::GetService.call(@project, @pull_request, current_user&.gitea_token)
@last_review = @pull_request.reviews.order(created_at: :desc).take
end
def reopen
can_merge = @project&.pull_requests.where(head: @pull_request.head, base: @pull_request.base, status: 0, is_original: @pull_request.is_original, fork_project_id: @pull_request.fork_project_id)
if can_merge.present?
render json: {
status: -1,
redirect_url:"/#{@project.owner.login}/#{@project.identifier}/pulls/#{can_merge.first.gitea_number}",
message: "在这些分支之间的合并请求已存在",
}
else
@result_object = Api::V1::Projects::Pulls::ReopenService.call(@project, @pull_request, current_user)
if @result_object
# 修改状态
journal = @pull_request.issue.journals.create!({user_id: current_user.id, operate_by: 'Issue'})
journal.journal_details.create!({property: "attr", prop_key: "status", old_value: 0, value: 4})
ProjectActions::PullRequestEvent.build(@project, current_user, @pull_request, "reopened")
render_ok
else
render_error("合并请求重新打开失败!")
end
end
end
def files
if params[:filepath].present?
@result_object = $gitea_hat_client.get_repos_pulls_files_by_owner_repo_index_filepath(@project&.owner.login, @project&.identifier, @pull_request.gitea_number, CGI.escape(params[:filepath]), {query: {token: current_user&.gitea_token}})
@result_object = $gitea_hat_client.get_repos_pulls_files_by_owner_repo_index_filepath(@project&.owner.login, @project&.identifier, @pull_request.gitea_number, URI.escape(params[:filepath]), {query: {token: current_user&.gitea_token}})
else
@result_object = $gitea_hat_client.get_repos_pulls_files_by_owner_repo_index(@project&.owner.login, @project&.identifier, @pull_request.gitea_number, {query: {isNew: "true",token: current_user&.gitea_token, page: page, limit: limit}})
end

View File

@ -7,17 +7,41 @@ class Api::V1::Projects::TagsController < Api::V1::BaseController
end
def show
@result_object = Api::V1::Projects::Tags::GetService.call(@project, params[:name], current_user&.gitea_token)
@name = params[:name].include?('.json') ? params[:name][0..-6] : params[:name]
@result_object = Api::V1::Projects::Tags::GetService.call(@project, Base64.decode64(@name), current_user&.gitea_token)
end
before_action :require_operate_above, only: [:destroy]
before_action :require_operate_above, only: [:create, :delete, :destroy]
def destroy
@result_object = Api::V1::Projects::Tags::DeleteService.call(@project, params[:name], current_user&.gitea_token)
def create
@result_object = Api::V1::Projects::Tags::CreateService.call(@project, tag_params, current_user&.gitea_token)
Rails.logger.info "create tag result: #{@result_object}"
@project.update_column(:updated_on, Time.now)
end
def delete
@result_object = Api::V1::Projects::Tags::DeleteService.call(@project, params[:tag_name], current_user&.gitea_token)
if @result_object
return render_ok
else
return render_error('删除标签失败!')
end
end
def destroy
@name = params[:name].include?('.json') ? params[:name][0..-6] : params[:name]
@result_object = Api::V1::Projects::Tags::DeleteService.call(@project, Base64.decode64(@name), current_user&.gitea_token)
if @result_object
return render_ok
else
return render_error('删除标签失败!')
end
end
private
def tag_params
params.require(:tag).permit(:tag_name, :target, :message)
end
end

View File

@ -41,24 +41,13 @@ class Api::V1::SonarqubesController < Api::V1::BaseController
puts %Q{\033[33m [warning] soanrqube config or configuration.yml missing,
please add it or execute 'cp config/configuration.yml.example config/configuration.yml' \033[0m}
end
sonar_content = build_sonar_content(params[:owner], @project.id, languages_precentagable.keys)
sonar_scanner_content = {
filepath: '.gitea/workflows/SonarScanner.yaml',
branch: params[:branch],
new_branch: nil,
content: "
on:
# Trigger analysis when pushing to your main branches, and when creating a pull request.
push:
branches:
- main
- master
- develop
- 'releases/**'
pull_request:
types: [opened, synchronize, reopened]
name: Main Workflow
name: Sonar Scanner
jobs:
sonarqube:
runs-on: ubuntu-latest
@ -66,10 +55,22 @@ class Api::V1::SonarqubesController < Api::V1::BaseController
- uses: #{checkout_url}
with:
# Disabling shallow clones is recommended for improving the relevancy of reporting
fetch-depth: 0
fetch-depth: 1
single-branch: true
- name: curl doxygen
run: |
curl -X GET #{doxygen_url}/generate?repo=#{@project.repository.url}
- name: new properties
run: |
cat <<EOF > sonar-project.properties
#{sonar_content}
EOF
- name: new build_tmp_folder
run: |
mkdir build_tmp_folder
- name: SonarQube Scan
uses: #{scanner_url}
env:
@ -91,29 +92,7 @@ class Api::V1::SonarqubesController < Api::V1::BaseController
sonar_scanner_content[:content] = Base64.strict_encode64(sonar_scanner_content[:content])
Gitea::CreateFileInteractor.call(@owner.gitea_token, @owner.login, sonar_scanner_content)
end
sonar_content = build_sonar_content(params[:owner], @project.id, languages_precentagable.keys)
sonar_project_content = {
filepath: 'sonar-project.properties',
branch: params[:branch],
new_branch: nil,
"content": sonar_content,
"message": 'Add sonar-project.properties',
committer: {
email: @owner.mail,
name: @owner.login
},
identifier: @project.identifier
}
sonar_project_exit = Repositories::EntriesInteractor.call(@owner, @project.identifier, 'sonar-project.properties', ref: params[:branch])
if sonar_project_exit.success?
Gitea::UpdateFileInteractor.call(@owner.gitea_token, @owner.login, sonar_project_content.merge(sha:sonar_project_exit.result['sha']))
else
sonar_project_content[:content] = Base64.strict_encode64(sonar_project_content[:content])
Gitea::CreateFileInteractor.call(@owner.gitea_token, @owner.login, sonar_project_content)
end
$gitea_hat_client.post_repos_actions_runs_by_owner_repo(@project&.owner&.login, @project&.identifier, {query: {workflow: "SonarScanner.yaml", ref: params[:branch]}})
render_ok
end

View File

@ -0,0 +1,10 @@
class Api::V1::UserTokensController < Api::V1::BaseController
include AesCryptHelper
before_action :require_login
def show
password = decrypt(params[:password]) rescue params[:password].to_s
tip_exception("密码不正确") unless current_user.check_password?(password)
render_ok({access_token: current_user.gitea_token})
end
end

View File

@ -4,7 +4,7 @@ class Api::V1::Users::FeedbacksController < Api::V1::BaseController
before_action :check_auth_for_observe_user
def create
@result = Api::V1::Users::Feedbacks::CreateService.call(@observe_user, feedback_params)
@result = Api::V1::Users::Feedbacks::CreateService.call(@observe_user, feedback_params, request.remote_ip)
return render_error("反馈意见创建失败.") if @result.nil?
return render_ok
end

View File

@ -313,8 +313,13 @@ class ApplicationController < ActionController::Base
header = request.authorization
pattern = /^Bearer /i
token = header.gsub(pattern, "")
User.current, message = Bot.decode_jwt_token(token)
tip_exception(401, message) if message.present?
user = User.find_by(gitea_token: token)
if user.present?
User.current = user
else
User.current, message = Bot.decode_jwt_token(token)
tip_exception(401, message) if message.present?
end
else
if @doorkeeper_token.present?
# client方法对接需要一直带着用户标识uid
@ -326,24 +331,14 @@ class ApplicationController < ActionController::Base
end
end
end
elsif params[:access_token].present?
user = User.find_by(gitea_token: params[:access_token])
tip_exception(401, "无效token") if user.blank?
User.current = user
else
User.current = find_current_user
uid_logger("user_setup: " + (User.current.logged? ? "#{User.current.try(:login)} (id=#{User.current.try(:id)})" : "anonymous"))
# # 开放课程通过链接访问的用户
# if !User.current.logged? && !params[:chinaoocTimestamp].blank? && !params[:websiteName].blank? && !params[:chinaoocKey].blank?
# content = "#{OPENKEY}#{params[:websiteName]}#{params[:chinaoocTimestamp]}"
#
# if Digest::MD5.hexdigest(content) == params[:chinaoocKey]
# user = open_class_user
# if user
# start_user_session(user)
# set_autologin_cookie(user)
# end
# User.current = user
# end
# end
if !User.current.logged? && Rails.env.development?
user = User.find 1
User.current = user
@ -366,6 +361,7 @@ class ApplicationController < ActionController::Base
end
end
end
UserOnline.login(User.current.id)
# User.current = User.find 81403
end
@ -602,7 +598,7 @@ class ApplicationController < ActionController::Base
end
def find_user_with_id
@user = User.find_by(type: 'User', id: params[:user_id])
@user = User.find_by(type: 'User', id: params[:user_id]) || User.find_by(type: 'User', login: params[:login])
# render_not_found("未找到’#{params[:login]}’相关的用户") unless @user
render_error("未找到相关的用户") unless @user
end

View File

@ -31,12 +31,10 @@ class AttachmentsController < ApplicationController
def get_file
Rails.logger.info("request.host===#{request.host},request.referer===#{request.referer}")
tip_exception(403, "你没有权限访问") if request.host.present? && !request.referer.to_s.include?(request.host.to_s.gsub("www.",""))
# tip_exception(403, "你没有权限访问") if request.host.present? && !request.referer.to_s.include?(request.host.to_s.gsub("www.",""))
normal_status(-1, "参数缺失") if params[:download_url].blank?
tip_exception('不可访问') if params[:download_url].to_s.include?("chinese_dictionary.txt")
url = base_url.starts_with?("https:") ? params[:download_url].to_s.gsub("http:", "https:") : params[:download_url].to_s
md5_file = Digest::MD5.hexdigest(params[:download_url])
FileUtils.mkdir_p("#{Rails.root}#{EduSetting.get("attachment_folder")}gitea/") unless Dir.exists?("#{Rails.root}#{EduSetting.get("attachment_folder")}gitea/")
tmp_path = "#{Rails.root}#{EduSetting.get("attachment_folder")}gitea/#{Time.now.strftime('%Y%m%d')}-#{md5_file}"
if url.starts_with?(base_url) && !url.starts_with?("#{base_url}/repo")
domain = GiteaService.gitea_config[:domain]
api_url = GiteaService.gitea_config[:base_url]
@ -45,18 +43,23 @@ class AttachmentsController < ApplicationController
url.gsub!(url.split("/")[-1], '')
# Rails.logger.info("url===#{url}")
Rails.logger.info(filepath)
ref = ref.blank? ? "" : URI.escape(ref.split('ref=')[1])
request_url = [domain, api_url, URI.encode(url), URI.escape(filepath), "?ref=#{ref}&access_token=#{User.where(admin: true).take&.gitea_token}"].join
request_url = [domain, api_url, URI.encode(url), URI.escape(filepath), "?ref=#{URI.escape(ref.split('ref=')[1])}&access_token=#{User.where(admin: true).take&.gitea_token}"].join
Rails.logger.info("request_url===#{request_url}")
File.delete(tmp_path) if File.exist?(tmp_path) # 删除之前的文件
Util.download_file(request_url, tmp_path)
filename = filepath
suffix = request_url.split("?").first.split(".").last
suffix_arr = ["zip", "rar", "xls", "xlsx", "pdf", "doc", "docx", "exe"] # 非文本文件直接重定向下载
Rails.logger.info("suffix===#{suffix}#{suffix_arr.include?(suffix.to_s.downcase)}")
if suffix_arr.include?(suffix.to_s.downcase)
redirect_to request_url
return
else
response = Faraday.get(request_url)
filename = filepath
end
else
File.delete(tmp_path) if File.exist?(tmp_path) # 删除之前的文件
Util.download_file(URI.encode(url), tmp_path)
response = Faraday.get(URI.encode(url))
filename = params[:download_url].to_s.split("/").pop()
end
send_file(tmp_path, filename: filename, type: "application/octet-stream", disposition: 'attachment')
send_data(response.body.force_encoding("UTF-8"), filename: filename, type: "application/octet-stream", disposition: 'attachment')
end
def create

View File

@ -16,13 +16,18 @@ class CommitLogsController < ApplicationController
project = Project.where(identifier: repository_name).where(user_id: owner&.id)&.first
project = Project.where(identifier: repository_name).where(gpid: repository_id)&.first if project.blank?
project.update_column(:updated_on, Time.now) if project.present?
if project.project_type == 'sync_mirror'
render_ok
return
end
params[:commits].each do |commit|
commit_id = commit[:id]
message = commit[:message]
commit_date = Time.parse(commit[:timestamp]) || Time.now
commit_log = CommitLog.create(user: user, project: project, repository_id: repository_id,
name: repository_name, full_name: repository_full_name,
ref: ref, commit_id: commit_id, message: message)
commit_log.project_trends.create(user_id: user.id, project_id: project&.id, action_type: "create") if user.id !=2
ref: ref, commit_id: commit_id, message: message, created_at: commit_date, updated_at: commit_date)
commit_log.project_trends.create(user_id: user.id, project_id: project&.id, action_type: "create") if user.present? && user.id !=2 && project.present?
# 统计数据新增
CacheAsyncSetJob.perform_later("project_common_service", {commits: 1}, project.id)
@ -36,6 +41,7 @@ class CommitLogsController < ApplicationController
solve_issue_content = message.to_s.scan(/\b(Fix|Fixes|Fixed|Fixing|fix|fixes|fixed|fixing|Resolve|Resolves|Resolved|Resolving|resolve|resolves|resolved|resolving|Implement|Implements|Implemented|Implementing|implement|implements|implemented|implementing)\s*(#\d+(,\s*#\d+)*)?\b/)
ChangeIssueStatusByMessageJob.perform_later(commit_id, project, commit_user, solve_issue_content[0][1], 3) if solve_issue_content[0].present? && solve_issue_content[0][1].present?
end
render_ok
end
end

View File

@ -0,0 +1,169 @@
require 'yaml'
require 'json'
module Api::ActionValidatorHelper
extend ActiveSupport::Concern
def validate_string(yaml_string, branch, default_branch)
begin
yaml_content = YAML.safe_load(yaml_string)
validate_content(yaml_content, branch, default_branch)
rescue Psych::SyntaxError => e
{ valid: false, errors: ["YAML语法错误: #{e.message}"] }
rescue => e
{ valid: false, errors: ["数据读取错误: #{e.message}"] }
end
end
def validate_content(data, branch, default_branch)
errors = []
warnings = []
# 基本结构验证
errors.concat(validate_basic_structure(data))
# 触发器验证
errors.concat(validate_trigger(data, branch, default_branch))
# 任务验证
errors.concat(validate_job(data))
# 步骤验证
if data['jobs']
data['jobs'].each do |job_name, job_config|
errors.concat(validate_job_steps(job_name, job_config))
end
end
{
valid: errors.empty?,
errors: errors,
warnings: warnings,
job_count: data['jobs']&.size || 0
}
end
private
def validate_basic_structure(data)
errors = []
unless data.is_a?(Hash)
errors << "Action文件必须是YAML对象"
return errors
end
unless data.key?('name')
errors << "缺少必需字段: name"
end
unless data.key?(true)
errors << "缺少触发器配置: on"
end
unless data.key?('jobs')
errors << "缺少任务配置: jobs"
end
errors
end
def validate_trigger(data, branch, default_branch)
errors = []
Rails.logger.info "data==#{data}"
Rails.logger.info "data==true#{data[true]}"
if data[true]
if data[true]['push']
# unless data[true]['push'].key?('branches')
# errors << "push触发器缺少branches配置"
# end
#
# unless data[true]['push'].key?('paths-ignore')
# errors << "push触发器缺少paths-ignore配置"
# end
end
if data[true]['schedule'] && branch != default_branch
errors << "只有#{default_branch}分支支持定时触发器"
end
end
errors
end
def validate_job(data)
errors = []
if data['jobs']
jobs = data['jobs']
unless jobs.is_a?(Hash)
errors << "jobs字段必须是对象"
return errors
end
jobs.each do |job_name, job_config|
unless job_config.is_a?(Hash)
errors << "任务 #{job_name} 配置必须是对象"
next
end
# unless job_config.key?('name')
# errors << "任务 #{job_name} 缺少name字段"
# end
unless job_config.key?('runs-on')
errors << "任务 #{job_name} 缺少runs-on字段"
end
unless job_config.key?('steps')
errors << "任务 #{job_name} 缺少steps字段"
end
end
end
errors
end
def validate_job_steps(job_name, job_config)
errors = []
steps = job_config['steps'] || []
unless steps.is_a?(Array)
errors << "任务 #{job_name} 的steps必须是数组"
return errors
end
# 验证每个步骤的详细配置
steps.each_with_index do |step, index|
errors.concat(validate_step(job_name, step, index))
end
errors
end
def validate_step(job_name, step, index)
errors = []
unless step.is_a?(Hash)
errors << "任务 #{job_name} 步骤 #{index + 1} 必须是对象"
return errors
end
# unless step.key?('name')
# errors << "任务 #{job_name} 步骤 #{index + 1} 缺少name字段"
# end
# 验证uses或run字段
has_uses = step.key?('uses')
has_run = step.key?('run')
unless has_uses || has_run
errors << "任务 #{job_name} 步骤 #{index + 1} 必须包含uses或run字段"
end
if has_uses && has_run
errors << "任务 #{job_name} 步骤 #{index + 1} 不能同时包含uses和run字段"
end
errors
end
end

View File

@ -63,6 +63,7 @@ module LoginHelper
user.delete_autologin_token(autologin)
user.delete_session_token(session[:tk])
UserOnline.logout(User.current.id)
self.logged_user = nil
end

View File

@ -513,7 +513,7 @@ class IssuesController < ApplicationController
end
def set_issue
@issue = Issue.find_by_id(params[:id])
@issue = @project.issues.issue_issue.where(project_issues_index: params[:id]).where.not(id: params[:id]).take || @project.issues.issue_issue.find_by_id(params[:id])
if @issue.blank?
return render_not_found
elsif !(@project.is_public || (current_user.present? && (@project.member?(current_user) || current_user&.admin? || (@project.user_id == current_user&.id))))

View File

@ -48,7 +48,7 @@ class JournalsController < ApplicationController
AtmeService.call(current_user, @atme_receivers, journal) if @atme_receivers.size > 0
TouchWebhookJob.set(wait: 5.seconds).perform_later('PullRequestComment', @issue&.id, current_user.id, journal.id, 'created', {})
# @issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "journal")
ProjectActions::PullRequestReviewCommentEvent.build(@issue.project, current_user, @issue.pull_request, journal) if @issue.issue_classify == "pull_request"
# author: zxh
# 调用上链API
push_activity_2_blockchain("issue_comment_create", journal) if Site.has_blockchain? && @project.use_blockchain

View File

@ -5,7 +5,7 @@ class MainController < ApplicationController
skip_before_action :setup_laboratory
def first_stamp
render :json => { status: 0, message: Time.now.to_i }
render :json => { status: 0, message: Time.now.to_i, online: UserOnline.count }
end
def test_404

View File

@ -71,4 +71,8 @@ class Oauth::BaseController < ActionController::Base
Rails.logger.info("[educoder] set sesstion edulogin: #{login}")
session[:edulogin] = login
end
def go_page_by_cookie(new_user)
cookies["go_page"].present? ? cookies["go_page"] : root_path(new_user: new_user)
end
end

View File

@ -54,7 +54,7 @@ class Oauth::CallbacksController < Oauth::BaseController
"OpenUsers::#{platform.to_s.capitalize}".constantize.create!(user: current_user, uid: login, extra: auth_hash.extra)
end
end
redirect_to root_path(new_user: new_user)
redirect_to go_page_by_cookie(new_user)
end
def process_callback_new
@ -69,7 +69,7 @@ class Oauth::CallbacksController < Oauth::BaseController
open_user = "OpenUsers::#{platform.to_s.capitalize}".constantize.find_by(uid: uid)
if open_user.present? && open_user.user.present?
successful_authentication(open_user.user)
redirect_to root_path(new_user: false)
redirect_to go_page_by_cookie(false)
return
else
if current_user.blank? || !current_user.logged?

View File

@ -0,0 +1,61 @@
class Oauth::CcfController < Oauth::BaseController
include RegisterHelper
def oauth_url
redirect_to Ccf::Service.oauth_url
end
# 需要educoder那边设置回调地址
def create
begin
code = params['_access_code'].to_s.strip
tip_exception("_access_code不能为空") if code.blank?
# {"code":"1","msg":"success","data":{"token":"cef9607dc43540e4a75cc484b2d42a24","id":"3a267da772c068866bde35fee6efff42","openId":"","unionId":"","loginName":"肖小琼","username":"13429151265","name":"肖小琼","cardNumber":null,"email":null,"userState":"2","contact":"13429151265","gender":null,"englishName":"xiaoxiaoqiong","timeForNow":null,"roleCodes":"07","roleNames":"注册专业会员","regTime":1766375090000,"effDate":null,"lifeLongState":"2","committees":[]}}
new_user = false
# token = Ccf::Service.access_token(code)
# Rails.logger.info("[OAuth2] result -> #{token}")
result = Ccf::Service.user_info(code)
tip_exception("请求用户信息错误") if result['code'].to_i != 1
user_info = result['data']
login = Pinyin.t(user_info['loginName'], splitter: '') || user_info['username']
email = user_info['email']
# 存在该用户
open_user = OpenUsers::Ccf.find_by(uid: login)
if open_user.present? && open_user.user.present?
successful_authentication(open_user.user)
else
if current_user.blank? || !current_user.logged?
# 优先手机号匹配用户
has_user = nil
has_user = User.find_by(phone: user_info['contact']) if user_info['contact'].present?
has_user = User.find_by(mail: email) if has_user.blank? && email.present?
if has_user.present?
OpenUsers::Ccf.create!(user_id: has_user.id, uid: login, extra: result)
successful_authentication(has_user)
else
new_user = true
session[:unionid] = user_info['username']
##如果用户名被占用,重新生成
login = User.generate_user_login('p') if User.find_by(login: login).present?
email = "#{login}@forge.com" if email.blank?
reg_result = autologin_register(login, email, "Ec#{User.generate_user_login('p')}2021#", 'ccf', user_info['contact'], user_info['name'])
Rails.logger.info("[OAuth2] reg_result -> #{reg_result}")
if reg_result[:message].blank?
open_user = OpenUsers::Ccf.create!(user_id: reg_result[:user][:id], uid: login, extra: user_info)
successful_authentication(open_user.user)
else
tip_exception(reg_result[:message])
end
end
else
OpenUsers::Ccf.create!(user: current_user, uid: login, extra: user_info)
end
end
Rails.logger.info("[OAuth2] session[:unionid] -> #{session[:unionid]}")
redirect_to go_page_by_cookie(new_user)
rescue Exception => ex
render_error(ex.message)
end
end
end

View File

@ -22,7 +22,7 @@ class Oauth::Ci4sController < Oauth::BaseController
open_user = OpenUsers::Ci4s.find_by(uid: user_info['username'])
if open_user.present? && open_user.user.present?
successful_authentication(open_user.user)
redirect_to root_path(new_user: false)
redirect_to go_page_by_cookie(false)
return
else
if current_user.blank? || !current_user.logged?
@ -45,7 +45,7 @@ class Oauth::Ci4sController < Oauth::BaseController
end
end
Rails.logger.info("[OAuth2] session[:unionid] -> #{session[:unionid]}")
redirect_to root_path(new_user: new_user)
redirect_to go_page_by_cookie(new_user)
rescue Exception => ex
render_error(ex.message)
end

View File

@ -49,7 +49,7 @@ class Oauth::EducoderController < Oauth::BaseController
open_user = OpenUsers::Educoder.find_by(uid: result['login'])
if open_user.present? && open_user.user.present?
successful_authentication(open_user.user)
redirect_to root_path(new_user: false)
redirect_to go_page_by_cookie(false)
return
else
if current_user.blank? || !current_user.logged?
@ -71,7 +71,7 @@ class Oauth::EducoderController < Oauth::BaseController
Rails.logger.info("[OAuth2] session[:unionid] -> #{session[:unionid]}")
redirect_to "/bindlogin/educoder"
# redirect_to root_path(new_user: new_user)
# redirect_to go_page_by_cookie(new_user)
rescue Exception => ex
render_error(ex.message)
end

View File

@ -0,0 +1,88 @@
class Oauth::OpeniController < Oauth::BaseController
def bind
begin
login = params[:login]
mail = params[:mail] || nil
callback_url = params[:callback_url]
token = params[:token]
::OauthEducoderForm.new({ login: login, token: token, callback_url: callback_url }).validate!
open_user = OpenUsers::Openi.find_by(uid: login) || OpenUsers::Openi.find_by(uid: mail)
if open_user.present? && open_user.user.present? && open_user.user.email_binded?
Rails.logger.info "######## open_user exist and open_user.user exsit and email is binded ok"
successful_authentication(open_user.user)
redirect_to callback_url
else
Rails.logger.info "######## open user not exits"
user = User.find_by(login: login) || User.find_by(mail: mail)
if user.is_a?(User) && !user.is_a?(AnonymousUser)
OpenUsers::Openi.create!(user: user, uid: login)
successful_authentication(user)
redirect_to callback_url
else
redirect_to oauth_register_path(login: login, mail: mail, callback_url: callback_url)
end
end
rescue WechatOauth::Error => ex
render_error(ex.message)
end
end
# 需要gitlink那边设置回调地址
def create
# begin
code = params['code'].to_s.strip
tip_exception("code不能为空") if code.blank?
result = OpeniOauth::Service.access_token(code)
Rails.logger.info("[Openi] [access_token result] #{result} ")
tip_exception("获取授权Token失败") if result.blank? || result[:access_token].blank?
result = OpeniOauth::Service.user_info(result[:access_token])
Rails.logger.info("[Openi] [user_info result] #{result} ")
# 存在该用户
login = result['login']
mail = result['email']
nickName = result['full_name'] || result['username']
new_user = false
# 存在该用户
open_user = OpenUsers::Openi.find_by(uid: login)
if open_user.present? && open_user.user.present?
successful_authentication(open_user.user)
else
if current_user.blank? || !current_user.logged?
has_user = User.find_by(mail: mail)
if has_user.present?
OpenUsers::Openi.create!(user_id: has_user.id, uid: login, extra: result)
successful_authentication(has_user)
else
new_user = true
##如果用户名被占用,重新生成
login = User.generate_user_login('p') if User.find_by(login: login).present?
mail = "#{login}@example.org" if mail.blank?
reg_result = autologin_register(login, mail, "Ec#{User.generate_user_login('p')}2021#", 'openi', nil, nickName)
Rails.logger.info("[Openi] [reg_result] #{reg_result} ")
if reg_result[:message].blank?
open_user = OpenUsers::Openi.create!(user_id: reg_result[:user][:id], uid: login, extra: result)
successful_authentication(open_user.user)
else
tip_exception(reg_result.present? ? reg_result[:message] : "授权失败")
end
end
else
OpenUsers::Openi.create!(user: current_user, uid: login, extra: result)
end
end
redirect_to go_page_by_cookie(new_user)
# rescue Exception => ex
# render_error(ex.message)
# end
end
end

View File

@ -1,4 +1,4 @@
class Oauth2Controller < ActionController::Base
class Oauth2Controller < ApplicationController
layout 'doorkeeper/application'
include LoginHelper
@ -9,6 +9,12 @@ class Oauth2Controller < ActionController::Base
end
def create
if User.current.logged?
to_url = params[:call_url] + "&auth=" + User.current.login
to_url = params[:call_url] + "&auth=" + User.current.login + "&state=" + params[:state] if params[:state].present?
redirect_to to_url
return
end
if params[:login].blank?
@error = {msg: '邮箱地址或用户名不能为空', id: 'login'}
elsif params[:password].blank?
@ -33,7 +39,9 @@ class Oauth2Controller < ActionController::Base
return
end
login_control.clear
redirect_to params[:call_url] + "&auth=" + @user.login
to_url = params[:call_url] + "&auth=" + @user.login
to_url = params[:call_url] + "&auth=" + @user.login + "&state=" + params[:state] if params[:state].present?
redirect_to to_url
end
end
end

View File

@ -107,8 +107,10 @@ class Organizations::OrganizationsController < Organizations::BaseController
tip_exception("密码不正确") unless current_user.check_password?(password)
ActiveRecord::Base.transaction do
gitea_destroy = Gitea::Organization::DeleteService.call(current_user.gitea_token, @organization.login)
if gitea_destroy[:status] == 204
if gitea_destroy[:status] == 204
data_bank = @organization.attributes.to_json
@organization.destroy!
UserAction.create(action_id: @organization.id, action_type: "DestroyOrganization", user_id: current_user.id, :ip => request.remote_ip, data_bank: data_bank)
render_ok
elsif gitea_destroy[:status] == 500
tip_exception("当组织内含有仓库时,无法删除此组织")

View File

@ -18,6 +18,23 @@ class Organizations::ProjectsController < Organizations::BaseController
@projects = paginate(@projects)
end
def create
ActiveRecord::Base.transaction do
org_project_params = project_params.merge({ user_id: @organization.id })
Projects::CreateForm.new(org_project_params).validate!
@project = Projects::CreateService.new(current_user, org_project_params).call
OpenProjectDevOpsJob.set(wait: 5.seconds).perform_later(@project&.id, current_user.id)
UpdateProjectTopicJob.perform_later(@project.id) if @project.id.present?
end
rescue Gitea::Api::ServerError => ex
uid_logger_error(ex.message)
# tip_exception(ex.http_code, ex.message)
tip_exception(ex.message)
rescue ApplicationService::Error => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def search
tip_exception("请输入搜索关键词") if params[:search].nil?
# 表情处理
@ -48,4 +65,10 @@ class Organizations::ProjectsController < Organizations::BaseController
def sort_direction
%w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc'
end
def project_params
params.permit(:name, :description, :repository_name, :website, :lesson_url, :default_branch, :identifier,
:project_category_id, :project_language_id, :license_id, :ignore_id, :private, :has_actions,
:blockchain, :blockchain_token_all, :blockchain_init_token, :pr_view_admin)
end
end

View File

@ -11,8 +11,9 @@ class OwnersController < ApplicationController
end
def show
# login = params[:id].to_s[0..-6]
login = params[:id].to_s
# 如果 id 包含格式扩展名(如 .json移除它
login = login.sub(/\.(json|html|xml|csv|pdf|png|jpg|jpeg|gif|txt)$/, '')
@owner = Owner.find_by(login: login) || Owner.find_by(id: login)
clear_user_cookie unless @owner.present?
return render_not_found unless @owner.present?
@ -45,6 +46,7 @@ class OwnersController < ApplicationController
org_projects = Project.joins(team_projects: [team: :team_users]).where(team_users: {user_id: @owner.id}).to_sql
projects = Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct
user_projects = User.current.logged? && (User.current.admin? || User.current.login == @owner.login) ? projects : projects.visible
@user_projects_count = user_projects.size
@projects_common_count = user_projects.common.size
@projects_mirrior_count = user_projects.mirror.size
@projects_sync_mirrior_count = user_projects.sync_mirror.size

View File

@ -3,43 +3,72 @@ class ProjectTrendsController < ApplicationController
before_action :check_project_public, except: [:last]
def index
project_trends = @project.project_trends.preload(:user, trend: :user, project: :owner)
@project_trends = @project.project_trends.preload(:user, trend: :user, project: :owner).order(created_at: :desc)
check_time = params[:time] #时间的筛选
check_type = params[:type] #动态类型的筛选,目前已知的有 Issue, PullRequest, Version
check_status = params[:status] #类型的选择 "create", "close", "journal",
check_time = params[:time]&.to_i #时间的筛选
check_type = params[:type]&.strip #动态类型的筛选,目前已知的有 Issue, PullRequest, Version
check_status = params[:status] #类型的选择 "create", "close", "journal",
# 构建基础查询条件
scope = @project_trends
# 时间筛选
if check_time.present?
check_time = check_time.to_i
project_trends = project_trends.where("created_at between ? and ?",(Time.now.beginning_of_day - check_time.days), Time.now.end_of_day)
start_time = Time.now.beginning_of_day - check_time.days
end_time = Time.now.end_of_day
scope = scope.where(created_at: start_time..end_time)
end
@project_close_issues_count = project_trends.where(trend_type: "Issue", action_type: "close").size
@project_issues_count = project_trends.where(trend_type: "Issue", action_type: "create").size
@project_open_issues_count = @project_issues_count - @project_close_issues_count
# 提前计算统计信息(避免重复查询)
if check_type.blank? && check_status.blank?
# 使用单次查询获取所有统计信息
stats = scope.select(
"user_id", "trend_type", "trend_id", "action_type", "project_id",
"COUNT(*) as total",
"COUNT(CASE WHEN trend_type = 'Issue' AND action_type = 'close' THEN 1 END) as closed_issues",
"COUNT(CASE WHEN trend_type = 'Issue' AND action_type = 'create' THEN 1 END) as created_issues",
"COUNT(CASE WHEN trend_type = 'PullRequest' AND action_type IN ('close', 'merge') THEN 1 END) as closed_prs",
"COUNT(CASE WHEN trend_type = 'PullRequest' AND action_type = 'create' THEN 1 END) as created_prs"
).take
@project_close_issues_count = stats.closed_issues
@project_issues_count = stats.created_issues
@project_open_issues_count = @project_issues_count - @project_close_issues_count
@project_pr_count = stats.closed_prs
@project_pr_all_count = stats.created_prs
@project_new_pr_count = @project_pr_all_count - @project_pr_count
end
@project_pr_count = project_trends.where(trend_type: "PullRequest", action_type: ["close", "merge"]).size
@project_pr_all_count = project_trends.where(trend_type: "PullRequest", action_type: "create").size
@project_new_pr_count = @project_pr_all_count - @project_pr_count
# 类型筛选
if check_type.present?
project_trends = project_trends.where(trend_type: check_type.to_s.strip)
scope = scope.where(trend_type: check_type)
end
# 状态筛选
if check_status.present?
if check_status == "delay" || check_status == "close"
project_trends = project_trends.where(action_type: ["close", "merge"])
else
project_trends = project_trends.where(action_type: ["create"]).where.not(trend_id: project_trends.where(action_type: ["close", "merge"]).pluck(:trend_id))
case check_status
when "delay", "close"
scope = scope.where(action_type: ["close", "merge"])
when "create"
# 使用子查询优化,避免重复扫描表
closed_trend_ids = scope.where(action_type: ["close", "merge"]).select(:trend_id)
scope = scope.where(action_type: "create")
.where.not(trend_id: closed_trend_ids)
else
# 其他状态的处理
scope = scope.where(action_type: check_status)
end
else
project_trends = project_trends.where(action_type: "create")
# 默认只显示创建记录
scope = scope.where(action_type: "create")
end
project_trends = project_trends.order("created_at desc")
@page = params[:page]
# 获取总数和分页数据
@project_trends_size = scope.size
@page = params[:page] || 1
@limit = params[:limit] || 15
@project_trends_size = project_trends.size
@project_trends = project_trends.page(@page).per(@limit)
@project_trends = scope.page(@page).per(@limit)
end
def last

View File

@ -46,7 +46,7 @@ class ProjectsController < ApplicationController
if category_id.blank? && params[:search].blank? && params[:topic_id].blank? && params[:topic_name].blank?
# 默认查询时count性能问题处理
not_category_count = Project.where(project_category_id: nil).count
ProjectCategory.sum("projects_count") - Project.visible.joins("left join organization_extensions on organization_extensions.organization_id = projects.user_id").where("organization_extensions.visibility =2").count + not_category_count
ProjectCategory.sum("projects_count") + not_category_count - Project.visible.joins("left join organization_extensions on organization_extensions.organization_id = projects.user_id").where("organization_extensions.visibility =2").where(status: 1).count - Project.where(status: 0).count - Project.is_private.where(status:1).count
elsif params[:search].present? || params[:topic_id].present? || params[:topic_name].present?
@projects.total_count
else
@ -83,14 +83,21 @@ class ProjectsController < ApplicationController
if EduSetting.get("mirror_address").to_s.include?("github") && enable_accelerator?(mirror_params[:clone_addr])
source_clone_url = mirror_params[:clone_addr]
uid_logger("########## 已动加速器 ##########")
result = Gitea::Accelerator::MigrateService.call(mirror_params)
if result[:status] == :success
Rails.logger.info "########## 加速镜像成功 ########## "
Projects::MigrateService.call(current_user,
mirror_params.merge(source_clone_url: source_clone_url,
clone_addr: accelerator_url(mirror_params[:repository_name])))
hasProject = Gitea::Accelerator::GetService.call("root", mirror_params[:repository_name])
if hasProject.present? && hasProject["original_url"] == source_clone_url
uid_logger("########## 已在加速器查询到#{source_clone_url} ##########")
Projects::MigrateService.call(current_user, mirror_params.merge(source_clone_url: source_clone_url, clone_addr: accelerator_url(mirror_params[:repository_name])))
else
Projects::MigrateService.call(current_user, mirror_params)
result = Gitea::Accelerator::MigrateService.call(mirror_params)
uid_logger("########## 已动加速器#{result} ###########{mirror_params}")
if result[:status] == :success
Rails.logger.info "########## 加速镜像成功 ########## "
Projects::MigrateService.call(current_user,
mirror_params.merge(source_clone_url: source_clone_url,
clone_addr: accelerator_url(mirror_params[:repository_name])))
else
Projects::MigrateService.call(current_user, mirror_params)
end
end
elsif EduSetting.get("mirror_address").to_s.include?("cnpmjs") && mirror_params[:clone_addr].include?("github.com")
source_clone_url = mirror_params[:clone_addr]
@ -383,6 +390,24 @@ class ProjectsController < ApplicationController
end
end
def clone_detection
forge_url = "#{Rails.application.config_for(:configuration)['platform_url']}"
target_dir = "2-#{SecureRandom.hex(6)}"
source_url = "#{forge_url}/#{@project.owner.name}/#{@project.identifier}.git"
source_dir = "#{@project.id}-#{SecureRandom.hex(6)}"
clone_dir = File.join(Rails.root, "public", "clone_detection")
system("cd #{clone_dir} && git clone #{params[:target_url]} #{target_dir}")
system("cd #{clone_dir} && git clone #{source_url} #{source_dir}")
system("cd /var/www/NiCad-6.2 && ./nicad6cross functions java /var/www/forgeplus/public/clone_detection/#{source_dir} /var/www/forgeplus/public/clone_detection/#{target_dir}")
view_url = "#{forge_url}/#{source_dir}_functions-blind-crossclones/#{source_dir}_functions-blind-crossclones-0.30-classes-withsource.html"
system("rm -rf /var/www/forgeplus/public/clone_detection/#{source_dir}")
system("rm -rf /var/www/forgeplus/public/clone_detection/#{target_dir}")
render_ok({ view_url: view_url })
rescue
Rails.logger.info("clone_detection...............error#{forge_url},#{source_url}")
system("rm -rf /var/www/forgeplus/public/clone_detection/#{source_dir}")
system("rm -rf /var/www/forgeplus/public/clone_detection/#{target_dir}")
end
private

View File

@ -13,6 +13,7 @@ class PullRequestsController < ApplicationController
def index
# @issues = Gitea::PullRequest::ListService.new(@user,@repository.try(:identifier)).call #通过gitea获取
ProjectTemplates::PullRequest.init_data(@project.id)
issues = @project.issues.issue_pull_request.issue_index_includes.includes(pull_request: :user)
issues = issues.where(is_private: false) unless current_user.present? && (current_user.admin? || @project.member?(current_user))
@all_issues = issues.distinct
@ -71,11 +72,11 @@ class PullRequestsController < ApplicationController
@pull_request.bind_gitea_pull_request!(@gitea_pull_request[:body]["number"], @gitea_pull_request[:body]["id"])
reviewers = User.where(id: params[:reviewer_ids])
@pull_request.reviewers = reviewers
SendTemplateMessageJob.perform_later('PullRequestAssigned', current_user.id, @pull_request&.id) if Site.has_notice_menu?
SendTemplateMessageJob.perform_later('ProjectPullRequest', current_user.id, @pull_request&.id) if Site.has_notice_menu?
SendTemplateMessageJob.set(wait: 5.seconds).perform_later('PullRequestAssigned', current_user.id, @pull_request&.id) if Site.has_notice_menu?
SendTemplateMessageJob.set(wait: 5.seconds).perform_later('ProjectPullRequest', current_user.id, @pull_request&.id) if Site.has_notice_menu?
Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}"
AtmeService.call(current_user, @atme_receivers, @pull_request) if @atme_receivers.size > 0
ProjectActions::PullRequestEvent.build(@project, current_user, @pull_request, "opened")
# author: zxh
# 调用上链API
push_activity_2_blockchain("pull_request_create", @pull_request) if Site.has_blockchain? && @project.use_blockchain
@ -93,6 +94,7 @@ class PullRequestsController < ApplicationController
@fork_project_user_name = @pull_request&.fork_project&.owner.try(:show_real_name)
@fork_project_user = @pull_request&.fork_project&.owner.try(:login)
@fork_project_identifier = @pull_request&.fork_project&.repository.try(:identifier)
@gitea_pull = Gitea::PullRequest::GetService.call(@owner.login, @repository.identifier, @pull_request.gitea_number, @owner&.gitea_token)
end
def update
@ -111,15 +113,51 @@ class PullRequestsController < ApplicationController
@pull_request.reviewers = reviewers
if @issue.update_attributes(@issue_params)
# 更改负责人
if @issue.previous_changes["assigned_to_id"].present?
journal = @issue.journals.create!({user_id: current_user.id, operate_by: "Issue"})
journal.journal_details.create!({property: "reviewer", prop_key: 1, old_value: @issue.previous_changes["assigned_to_id"][0], value: @issue.previous_changes["assigned_to_id"][1]})
end
# 更改标题
if @issue.previous_changes["subject"].present?
journal = @issue.journals.create!({user_id: current_user.id, operate_by: "Issue"})
journal.journal_details.create!({property: "attr", prop_key: "subject", old_value: @issue.previous_changes["subject"][0], value: @issue.previous_changes["subject"][1]})
end
# 更改描述
if @issue.previous_changes["description"].present?
journal = @issue.journals.create!({user_id: current_user.id, operate_by: 'Issue'})
journal.journal_details.create!({property: "attr", prop_key: "description", old_value: @issue.previous_changes["description"][0], value: @issue.previous_changes["description"][1]})
end
# 修改优先级
if @issue.previous_changes["priority_id"].present?
journal = @issue.journals.create!({user_id: current_user.id, operate_by: 'Issue'})
journal.journal_details.create!({property: "attr", prop_key: "priority_id", old_value: @issue.previous_changes["priority_id"][0], value: @issue.previous_changes["priority_id"][1]})
end
# 修改里程碑
if @issue.previous_changes["fixed_version_id"].present?
journal = @issue.journals.create!({user_id: current_user.id, operate_by: 'Issue'})
journal.journal_details.create!({property: "attr", prop_key: "fixed_version_id", old_value: @issue.previous_changes["fixed_version_id"][0], value: @issue.previous_changes["fixed_version_id"][1]})
end
if @pull_request.update_attributes(@local_params.compact)
gitea_pull = Gitea::PullRequest::UpdateService.call(@owner.login, @repository.identifier,
@pull_request.gitea_number, @requests_params, current_user.gitea_token)
if gitea_pull[:status] === :success
if params[:issue_tag_ids].present?
if params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size > 1
return normal_status(-1, "最多只能创建一个标记。")
elsif params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size == 1
#if params[:issue_tag_ids].present?
#if params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size > 1
# return normal_status(-1, "最多只能创建一个标记。")
#elsif params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size == 1
if params[:issue_tag_ids].is_a?(Array) #&& params[:issue_tag_ids].present?
new_issue_tag_ids = params[:issue_tag_ids].sort!
now_issue_tag_ids = @issue.issue_tags.pluck(:id).sort!
if (!(now_issue_tag_ids & new_issue_tag_ids).empty? || !(now_issue_tag_ids.empty? && new_issue_tag_ids.empty?)) && new_issue_tag_ids != now_issue_tag_ids
journal = @issue.journals.create!({user_id: current_user.id, operate_by: 'Issue'})
journal.journal_details.create!({property: "issue_tag", prop_key: "#{new_issue_tag_ids.size}", old_value: now_issue_tag_ids.join(","), value: new_issue_tag_ids.join(",")})
end
@issue&.issue_tags_relates&.destroy_all
params[:issue_tag_ids].each do |tag|
IssueTagsRelate.create!(issue_id: @issue.id, issue_tag_id: tag)
@ -127,18 +165,22 @@ class PullRequestsController < ApplicationController
else
return normal_status(-1, "请输入正确的标记。")
end
end
if params[:attached_issue_ids].present?
if params[:attached_issue_ids].is_a?(Array) && params[:attached_issue_ids].size > 1
return normal_status(-1, "最多只能关联一个疑修。")
elsif params[:attached_issue_ids].is_a?(Array) && params[:attached_issue_ids].size == 1
@pull_request&.pull_attached_issues&.destroy_all
params[:attached_issue_ids].each do |issue|
PullAttachedIssue.create!(issue_id: issue, pull_request_id: @pull_request.id)
end
else
return normal_status(-1, "请输入正确的疑修。")
#end
if params[:attached_issue_ids].is_a?(Array) && params[:attached_issue_ids].size > 1
return normal_status(-1, "最多只能关联一个疑修。")
elsif params[:attached_issue_ids].is_a?(Array) #&& params[:attached_issue_ids].size == 1
new_attached_issue_ids = params[:attached_issue_ids].sort!
now_attached_issue_ids = @pull_request.attached_issues.pluck(:id).sort!
if (!(now_attached_issue_ids & new_attached_issue_ids).empty? || !(now_attached_issue_ids.empty? && new_attached_issue_ids.empty?)) && new_attached_issue_ids != now_attached_issue_ids
journal = @issue.journals.create!({user_id: current_user.id, operate_by: 'Issue'})
journal.journal_details.create!({property: "attached_issue", prop_key: "#{new_attached_issue_ids.size}", old_value: now_attached_issue_ids.join(","), value: new_attached_issue_ids.join(",")})
end
@pull_request&.pull_attached_issues&.destroy_all
params[:attached_issue_ids].each do |issue|
PullAttachedIssue.create!(issue_id: issue, pull_request_id: @pull_request.id)
end
else
return normal_status(-1, "请输入正确的疑修。")
end
if params[:status_id].to_i == 5
@issue.issue_times.update_all(end_time: Time.now)
@ -176,7 +218,11 @@ class PullRequestsController < ApplicationController
@pull_request.project_trends.create!(user: current_user, project: @project,action_type: ProjectTrend::CLOSE)
# 合并请求下issue处理为关闭
@issue&.update_attributes!({status_id:5})
# 修改状态
journal = @issue.journals.create!({user_id: current_user.id, operate_by: 'Issue'})
journal.journal_details.create!({property: "attr", prop_key: "status", old_value: 0, value: 2})
SendTemplateMessageJob.perform_later('PullRequestClosed', current_user.id, @pull_request.id) if Site.has_notice_menu?
ProjectActions::PullRequestEvent.build(@project, current_user, @pull_request, "closed")
normal_status(1, "已拒绝")
else
normal_status(-1, '合并失败')
@ -222,8 +268,10 @@ class PullRequestsController < ApplicationController
if success_condition && @pull_request.merge!
# @pull_request.project_trend_status!
@pull_request.project_trends.create!(user: current_user, project: @project,action_type: ProjectTrend::MERGE)
@issue&.custom_journal_detail("merge", "", "该合并请求已被合并", current_user&.id)
# @issue&.custom_journal_detail("merge", "", "该合并请求已被合并", current_user&.id)
# 修改状态
journal = @issue.journals.create!({user_id: current_user.id, operate_by: 'Issue'})
journal.journal_details.create!({property: "attr", prop_key: "status", old_value: 0, value: 1})
# author: zxh
# 调用上链API
push_activity_2_blockchain("pull_request_merge", @pull_request) if Site.has_blockchain? && @project.use_blockchain
@ -247,6 +295,7 @@ class PullRequestsController < ApplicationController
# 合并请求下issue处理为关闭
@issue&.update_attributes!({status_id:5})
ProjectActions::PullRequestEvent.build(@project, current_user, @pull_request, "closed")
SendTemplateMessageJob.perform_later('PullRequestMerged', current_user.id, @pull_request.id) if Site.has_notice_menu?
normal_status(1, "合并成功")
else
@ -324,7 +373,7 @@ class PullRequestsController < ApplicationController
# milestone: 0, #里程碑,未与本地的里程碑关联
}
assignee_login = User.find_by_id(params[:assigned_to_id])&.login
@requests_params = @local_params.merge({assignees: [assignee_login.to_s].reject!(&:empty?)})
@requests_params = @local_params.merge({assignees: [assignee_login.to_s].reject{|item|item.empty?}})
@issue_params = {
author_id: current_user.id,
project_id: @project.id,

View File

@ -83,8 +83,8 @@ class RepositoriesController < ApplicationController
end
def sub_entries
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s))
tip_exception('不可访问') if params[:filepath].to_s.include?("chinese_dictionary.txt")
if @project.educoder?
if params[:type] === 'file'
@sub_entries = Educoder::Repository::Entries::GetService.call(@project&.project_educoder&.repo_name, file_path_uri)
@ -294,7 +294,7 @@ class RepositoriesController < ApplicationController
def archive
domain = GiteaService.gitea_config[:domain]
api_url = GiteaService.gitea_config[:base_url]
archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{Addressable::URI.escape(params[:archive])}"
archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{URI.encode(params[:archive])}"
file_path = [domain, api_url, archive_url].join
file_path = [file_path, "access_token=#{@owner&.gitea_token}"].join("?")
@ -307,6 +307,7 @@ class RepositoriesController < ApplicationController
def raw
domain = GiteaService.gitea_config[:domain]
api_url = GiteaService.gitea_config[:base_url]
tip_exception('不可访问') if params[:filepath].to_s.include?("chinese_dictionary.txt")
url = "/repos/#{@owner.login}/#{@repository.identifier}/raw/#{CGI.escape(params[:filepath])}?ref=#{CGI.escape(params[:ref])}"
file_path = [domain, api_url, url].join
@ -336,7 +337,7 @@ class RepositoriesController < ApplicationController
# TODO 获取最新commit信息
def project_commits
if params[:filepath].present?
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s))
Gitea::Repository::Commits::FileListService.new(@project.owner.login, @project.identifier, file_path_uri,
sha: get_ref, page: 1, limit: 1, token: @project&.owner&.gitea_token).call
else
@ -367,6 +368,7 @@ class RepositoriesController < ApplicationController
def content_params
{
base64_filepath: params[:base64_filepath],
filepath: params[:filepath],
branch: params[:branch],
new_branch: params[:new_branch],

View File

@ -89,6 +89,11 @@ class SettingsController < ApplicationController
def get_third_party_new
@third_party_new = []
@third_party_new << {
name: 'ccf',
url: "/oauth_ccf",
method: 'get'
}
@third_party_new << {
name: 'educoder',
url: EducoderOauth.oauth_url,
@ -96,13 +101,18 @@ class SettingsController < ApplicationController
}
platform_url = Rails.application.config_for(:configuration)['platform_url']
config = Rails.application.config_for(:configuration)
(config.dig("oauth").keys - ["educoder", "wechat"]).each do |provider|
(config.dig("oauth").keys - ["educoder", "wechat", "ccf","openi"]).each do |provider|
@third_party_new << {
name: provider,
url: "#{platform_url}/auth/#{provider}",
method: 'get'
}
end
@third_party_new << {
name: 'openi',
url: OpeniOauth.oauth_url,
method: 'get'
}
end
def get_top_system_notification

View File

@ -9,7 +9,7 @@ class Users::OrganizationsController < Users::BaseController
@organizations = observed_user.organizations.with_visibility("common")
end
@organizations = @organizations.ransack(login_cont: params[:search]).result if params[:search].present?
@organizations = @organizations.ransack(login_or_nickname_cont: params[:search]).result if params[:search].present?
@home_top_ids = @organizations.joins(:home_top_settings).where(home_top_settings: {user_id: observed_user.id}).order("home_top_settings.created_at asc").pluck(:id)

View File

@ -5,7 +5,7 @@ class UsersController < ApplicationController
before_action :load_user, only: [:show, :homepage_info, :sync_token, :sync_gitea_pwd, :projects, :watch_users, :fan_users, :hovercard, :hovercard4proj]
before_action :check_user_exist, only: [:show, :homepage_info,:projects, :watch_users, :fan_users, :hovercard, :hovercard4proj]
before_action :require_login, only: %i[me sync_user_info]
before_action :connect_to_ci_db, only: [:get_user_info]
# before_action :connect_to_ci_db, only: [:get_user_info]
before_action :convert_image!, only: [:update, :update_image]
skip_before_action :check_sign, only: [:attachment_show]
before_action :sso_login, only: [:get_user_info]
@ -50,6 +50,7 @@ class UsersController < ApplicationController
org_projects = Project.joins(team_projects: [team: :team_users]).where(team_users: {user_id: @user.id}).to_sql
projects = Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct
user_projects = User.current.logged? && (User.current.admin? || User.current.login == @user.login) ? projects : projects.visible
@user_projects_count = user_projects.size
@projects_common_count = user_projects.common.size
@projects_mirrior_count = user_projects.mirror.size
@projects_sync_mirrior_count = user_projects.sync_mirror.size

View File

@ -56,6 +56,7 @@ class VersionReleasesController < ApplicationController
if params[:attachment_ids].present?
create_attachments(params[:attachment_ids], version_release)
end
ProjectActions::ReleaseEvent.build(@project, current_user, version_release)
normal_status(0, "发布成功")
else
normal_status(-1, "发布失败")

View File

@ -27,6 +27,9 @@ class WatchersController < ApplicationController
def follow
begin
return normal_status(2, "你已关注了") if current_user.watched?(@target)
if @target.is_a?(Project)
ProjectActions::StarEvent.build(@target, current_user)
end
current_user.watch!(@target)
render_ok({watchers_count: @target.watchers_count, watched: true})
rescue Exception => e

View File

@ -157,6 +157,12 @@ module ApplicationHelper
end
end
def customized_img(project_id)
icons = Array.new(43) { |i| "icon_#{i + 1}.png" }
index = project_id % icons.size
File.join("images/NSCCN", "","#{index}.png")
end
def url_to_avatar_with_platform_url(source)
platform_url = Rails.application.config_for(:configuration)['platform_url']
if platform_url

View File

@ -14,15 +14,15 @@ module ProjectsHelper
end
def render_zip_url(owner, repository, archive)
[base_url, archive_repositories_path(owner&.login, repository, "#{archive}.zip")].join
[base_url, archive_repositories_path(owner&.login, repository.identifier, "#{archive}.zip")].join
end
def render_tar_url(owner, repository, archive)
[base_url, archive_repositories_path(owner&.login, repository, "#{archive}.tar.gz")].join
[base_url, archive_repositories_path(owner&.login, repository.identifier, "#{archive}.tar.gz")].join
end
def render_download_file_url(owner, repository, filepath, ref)
[base_url, "/api/#{owner&.login}/#{repository.identifier}/raw/#{CGI.escape(filepath)}?ref=#{CGI.escape(ref)}"].join
[base_url, "/api/#{owner&.login}/#{repository.identifier}/raw/#{URI.escape(filepath)}?ref=#{CGI.escape(ref)}"].join
end
def render_http_url(project)
@ -84,9 +84,10 @@ module ProjectsHelper
image_url: project.project_educoder.image_url
}
else
name = project.repository&.mirror_url.present? ? project.repository&.mirror_url.split('/')[-2] : project.owner.real_name
{
login: @owner.login,
name: @owner.real_name,
name: @owner.login == "NSCCN" ? name : @owner.real_name,
type: @owner.type,
image_url: url_to_avatar(@owner)
}

View File

@ -157,10 +157,12 @@ module RepositoriesHelper
next if s_content.starts_with?('http://') || s_content.starts_with?('https://') || s_content.starts_with?('mailto:') || s_content.blank?
ext = File.extname(s_content)[1..-1]
ext = ext.split("?")[0] if ext.include?("?")
ext = ext.gsub(/ \".*\"/, "") if ext.include?(" ")
if (image_type?(ext) || download_type(ext)) && !ext.blank?
s_content = s_content.starts_with?("/") ? s_content[1..-1] : s_content[0..-1]
s_content = File.expand_path(s_content, file_path)
s_content = s_content.split("#{Rails.root}/")[1]
s_content = s_content.gsub(/ \".*\"/, "") if s_content.include?(" ")
next if s_content.blank?
# content = content.gsub(s[0], "/#{s_content}")
join_xxx = s_content.include?("?") ? "&" : "?"
@ -184,8 +186,16 @@ module RepositoriesHelper
content = content.gsub("href=\"#{s[0]}\"", "href=\"#{s_content}\"")
when 'ss_href_1'
content = content.gsub("href=\'#{s[0]}\'", "href=\'#{s_content}\'")
else
content = content.gsub("(#{s[0]})", "(#{s_content})")
else
## md中图片处理包含中文名称tip
if s[0].to_s.include?(" ")
s1 = s[0].to_s.split(" ")
str1 = s1[0]
str2 = s1[1] if s1.size >1
content = content.gsub("(#{str1} #{str2})", "(#{s_content} #{str2})")
else
content = content.gsub("(#{s[0]})", "(#{s_content})")
end
end
else
path = [owner&.login, repo&.identifier, 'tree', ref, file_path].join("/")
@ -203,7 +213,7 @@ module RepositoriesHelper
content = content.gsub("href=\"#{s[0]}\"", "href=\"#{s_content}\"")
when 'ss_href_1'
content = content.gsub("href=\'#{s[0]}\'", "href=\'#{s_content}\'")
else
else
content = content.gsub("(#{s[0]})", "(#{s_content})")
end
end
@ -244,7 +254,9 @@ module RepositoriesHelper
Rails.logger.info("entry===#{entry["type"]} #{entry["name"]}")
content = entry['content'].present? ? entry['content'] : Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content']
# readme_render_decode64_content(content, owner, repo, ref)
new_readme_render_decode64_content(content, owner, repo, ref, entry['path'], entry['name'])
text = new_readme_render_decode64_content(content, owner, repo, ref, entry['path'], entry['name'])
# text = "该内容不合规,请修改。" if text.present? && !HarmoniousDictionary.clean?(text)
text
end
def decode64_content(entry, owner, repo, ref, path=nil)
@ -253,9 +265,13 @@ module RepositoriesHelper
content = entry['content'].present? ? entry['content'] : Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content']
# Rails.logger.info("content===#{content}")
return Base64.decode64(content).force_encoding("GBK").encode("UTF-8") unless Base64.decode64(content).force_encoding('UTF-8').valid_encoding?
return Base64.decode64(content).force_encoding('UTF-8')
text = Base64.decode64(content).force_encoding('UTF-8')
# text = "该内容不合规,请修改。" if text.present? && !HarmoniousDictionary.clean?(text)
text
elsif entry['is_text_file'] == true
return render_decode64_content(entry['content'])
text = render_decode64_content(entry['content'])
# text = "该内容不合规,请修改。" if text.present? && !HarmoniousDictionary.clean?(text)
text
else
file_type = File.extname(entry['name'].to_s)[1..-1]
if image_type?(file_type)
@ -264,7 +280,9 @@ module RepositoriesHelper
if download_type(file_type)
return entry['content']
end
render_decode64_content(entry['content'])
text = render_decode64_content(entry['content'])
# text = "该内容不合规,请修改。" if text.present? && !HarmoniousDictionary.clean?(text)
text
end
end
@ -305,7 +323,13 @@ module RepositoriesHelper
def repo_git_submodule_url(owner, repo, path)
unless (path.starts_with?('http://') || path.starts_with?('https://'))
path = File.expand_path(path, "/#{owner&.login}/#{repo&.identifier}")
if path.starts_with?('git@code.gitlink.org.cn')
path = path.gsub(":","/").gsub("git@code.gitlink.org.cn","https://gitlink.org.cn")
elsif path.starts_with?('git@code.gitlink.org.cn')
path = path.gsub(":","/").gsub("git@","https://")
else
path = File.expand_path(path, "/#{owner&.login}/#{repo&.identifier}")
end
end
return path

View File

@ -56,6 +56,8 @@ module Gitea
else
@params[:filepath]
end
rescue
@params[:filepath]
end
def valid_params
@ -73,6 +75,7 @@ module Gitea
file_params = file_params.merge(content: @params[:content] || "")
file_params = file_params.merge(message: @params[:message]) unless @params[:message].blank?
file_params = file_params.merge(committer: @params[:committer])
file_params = file_params.merge(dates: {"author": Time.now, "committer": Time.now})
file_params
end
end

View File

@ -51,10 +51,12 @@ module Gitea
def file_path
if @params[:base64_filepath].present?
Base64.decode64(params[:base64_filepath])
Base64.strict_decode64(params[:base64_filepath])
else
@params[:filepath]
end
rescue
@params[:filepath]
end
def valid_params
@ -70,6 +72,7 @@ module Gitea
sha: @params[:sha],
new_branch: @params[:new_branch],
message: @params[:message],
dates: {"author": Time.now, "committer": Time.now}
).compact
end
end

View File

@ -50,18 +50,22 @@ module Gitea
def file_path
if @params[:base64_filepath].present?
Base64.decode64(params[:base64_filepath])
Base64.strict_decode64(params[:base64_filepath])
else
@params[:filepath]
end
rescue
@params[:filepath]
end
def from_file_path
if @params[:base64_from_path].present?
Base64.decode64(params[:base64_from_path])
Base64.strict_decode64(params[:base64_from_path])
else
@params[:from_path]
end
rescue
@params[:from_path]
end
def valid_params
@ -80,7 +84,8 @@ module Gitea
new_branch: @params[:new_branch],
from_path: from_file_path,
message: @params[:message],
content: Base64.encode64(@params[:content])
content: Base64.encode64(@params[:content]),
dates: {"author": Time.now, "committer": Time.now}
).compact
end
end

View File

@ -8,6 +8,7 @@ class OpenProjectDevOpsJob < ApplicationJob
return if project.blank?
return if EduSetting.get("jianmu_devops_url").blank?
user = User.find_by(id: user_id)
return if user.blank?
code = jianmu_devops_code(project, user)
uri = URI.parse("#{jianmu_devops_url}/activate?code=#{URI.encode_www_form_component(code)}")
response = Net::HTTP.get_response(uri)

54
app/libs/ccf/service.rb Normal file
View File

@ -0,0 +1,54 @@
require 'oauth2'
module Ccf::Service
module_function
def client_id
config = Rails.application.config_for(:configuration)
config.dig("oauth", "ccf", "client_id")
end
def client_secret
config = Rails.application.config_for(:configuration)
config.dig("oauth", "ccf", "client_secret")
end
def base_url
config = Rails.application.config_for(:configuration)
config.dig("oauth", "ccf", "base_url")
end
def redirect_uri
config = Rails.application.config_for(:configuration)
config.dig("oauth", "ccf", "redirect_uri")
end
def oauth_url
"#{base_url}/auth?appid=GitLink&from=#{Base64.strict_encode64(redirect_uri)}&source=42Rb6s"
end
def access_token(code)
begin
Rails.logger.info("[CcfOauth] [code] #{code} ")
Rails.logger.info("[CcfOauth] [redirect_uri] #{redirect_uri} ")
client = OAuth2::Client.new(client_id, client_secret, site: base_url)
result = client.auth_code.get_token(code, redirect_uri: redirect_uri).to_hash
return result
rescue Exception => e
raise Gitlink::TipException.new(e.message)
end
end
def user_info(access_token)
url = "#{base_url}/api/v1/json/auth_info"
auth_json = { "appid": "GitLink", "code": access_token, "scope": "user_info" }
uri = URI.parse(url)
http = Net::HTTP.new(uri.hostname, uri.port)
if url.include?("https://")
http.use_ssl = true
end
response = http.send_request('POST', uri.path, auth_json.to_json, {'Content-Type' => 'application/json'})
Rails.logger.info("#######_________CcfOauth user_info_____#########{response.body}")
JSON.parse(response.body)
end
end

18
app/libs/openi_oauth.rb Normal file
View File

@ -0,0 +1,18 @@
module OpeniOauth
class << self
attr_accessor :client_id, :client_secret, :base_url, :redirect_uri
def logger
@_logger ||= STDOUT
end
def logger=(l)
@_logger = l
end
def oauth_url
"#{base_url}/login/oauth/authorize?client_id=#{client_id}&redirect_uri=#{URI.encode_www_form_component(redirect_uri)}&response_type=code"
end
end
end

View File

@ -0,0 +1,38 @@
require 'oauth2'
module OpeniOauth::Service
module_function
def request(method, url, params)
begin
Rails.logger.info("[OpeniOauth] [#{method.to_s.upcase}] #{url} || #{params}")
client = Faraday.new(url: OpeniOauth.base_url)
response = client.public_send(method, url, params)
result = JSON.parse(response.body)
Rails.logger.info("[OpeniOauth] [#{response.status}] #{result}")
result
rescue Exception => e
raise Gitlink::TipException.new(e.message)
end
end
def access_token(code)
begin
Rails.logger.info("[OpeniOauth] [code] #{code} ")
Rails.logger.info("[OpeniOauth] [redirect_uri] #{OpeniOauth.redirect_uri} ")
client = OAuth2::Client.new(OpeniOauth.client_id, OpeniOauth.client_secret, site: OpeniOauth.base_url, token_url: "/login/oauth/access_token")
result = client.auth_code.get_token(code, redirect_uri: OpeniOauth.redirect_uri)
Rails.logger.info("[OpeniOauth] [get_token result] #{result}")
return result.to_hash
rescue Exception => e
raise Gitlink::TipException.new(e.message)
end
end
def user_info(access_token)
request(:get, '/api/v1/user', { access_token: access_token })
end
end

View File

@ -13,30 +13,31 @@ module SonarService
lines = []
lines << "sonar.projectKey=#{owner}-#{project_id}"
lines << "sonar.projectVersion=1.0"
lines << "sonar.sourceEncoding=UTF-8"
lines << "sonar.sources=."
lines << "sonar.exclusions=**/test/**,**/tests/**,**/vendor/**,**/node_modules/**,**/__pycache__/**"
lines << " sonar.projectVersion=1.0"
lines << " sonar.sourceEncoding=UTF-8"
lines << " sonar.sources=."
lines << " sonar.exclusions=**/test/**,**/tests/**,**/vendor/**,**/node_modules/**,**/__pycache__/**"
if detected_langs.include?(:java)
lines << "sonar.java.binaries="
lines << " sonar.java.binaries=build_tmp_folder"
lines << " sonar.language=java"
end
if detected_langs.include?(:js)
lines << "sonar.javascript.lcov.reportPaths=coverage/lcov.info"
lines << " sonar.javascript.lcov.reportPaths=coverage/lcov.info"
end
if detected_langs.include?(:ts)
lines << "sonar.typescript.tsconfigPath=tsconfig.json"
lines << " sonar.typescript.tsconfigPath=tsconfig.json"
end
if detected_langs.include?(:python)
lines << "sonar.python.coverage.reportPaths=coverage.xml"
lines << " sonar.python.coverage.reportPaths=coverage.xml"
end
if detected_langs.include?(:cpp)
lines << "sonar.cxx.file.suffixes=.cpp,.c,.cc,.h,.hpp,.hh"
lines << "sonar.cxx.errorRecoveryEnabled=true"
lines << " sonar.cxx.file.suffixes=.cpp,.c,.cc,.h,.hpp,.hh"
lines << " sonar.cxx.errorRecoveryEnabled=true"
end
lines.join("\n")

89
app/libs/user_online.rb Normal file
View File

@ -0,0 +1,89 @@
module UserOnline
class << self
redis_config = YAML.load_file(Rails.root.join('config', 'redis.yml'))
if redis_config.present?
redis_url = redis_config[Rails.env]['url']
$redis = Redis.new({:url=>redis_url})
else
$redis = Rails.cache.data
end
def login(user_id)
set_bit(user_id, 1)
end
def logout(user_id)
if Rails.cache.is_a?(ActiveSupport::Cache::RedisStore)
now = Time.now()
keys = []
(1..5).each do |i|
key = (now - i.minutes).strftime("%H-%M")
keys << "cache:online_user_#{key}"
set_bit_and_expire(cache_key, user_id, 0) unless $redis.ttl(cache_key) == -2
end
end
set_bit(user_id, 0)
end
def set_bit(user_id, flag)
if $redis.ttl(cache_key) == -2
# 如果key不存在
set_bit_and_expire(cache_key, user_id, flag)
else
if $redis.ttl(cache_key) ==-1
# 可以存在,但过期时间没关联
# 这种情况大概率是并发原因导致对相应key设置过期时跳过了。所以删除并重新设置过期时间
$redis.del(cache_key)
set_bit_and_expire(cache_key, user_id, flag)
end
# 统计当前时间下前5分钟的活跃人数
$redis.setbit(cache_key, user_id, flag)
end
end
def set_bit_and_expire(cache_key, user_id, flag)
$redis.setbit(cache_key, user_id, flag)
# $redis.setbit(last_cache_key, user_id, flag)
# 现在统计5分钟在线人数这里设置6分钟过期目的是统计当前时刻的前5分钟
$redis.expire(cache_key, 6 * 60)
end
def count
if Rails.cache.is_a?(ActiveSupport::Cache::RedisStore)
now = Time.now()
keys = []
(0..4).each do |i|
key = (now - i.minutes).strftime("%H-%M")
keys << "cache:online_user_#{key}"
end
# 防止每个分钟数据有重复,所以去重去除
$redis.bitop("OR", "daiao", keys)
$redis.bitcount("daiao")
else
0
end
end
def cache_key
if Rails.cache.is_a?(ActiveSupport::Cache::RedisStore)
"cache:online_user_#{Time.now().strftime("%H-%M")}"
else
raise '请配置config.cache_store = redis_store'
end
end
def last_cache_key
if Rails.cache.is_a?(ActiveSupport::Cache::RedisStore)
# 如设置5分钟内即300秒有请求的用户视为在线, 则最大会统计到10分钟内有活动用户
# TODO 更精确时长
begin_hour = Time.now.beginning_of_hour
minutes_piece = (Time.now - begin_hour) / (60 * 5)
time = begin_hour.since((minutes_piece.to_i - 1) * (60 * 5)).strftime("%H-%M")
"cache:online_user_#{time}"
else
raise '请配置config.cache_store = redis_store'
end
end
end
end

View File

@ -38,11 +38,12 @@ class Action::Node < ApplicationRecord
belongs_to :user, optional: true
attr_accessor :cust_name, :run_values, :input_values, :parent_node_id, :sub_nodes, :link_type_array, :node_id
attr_accessor :cust_name, :run_values, :input_values, :env_input_values, :parent_node_id, :sub_nodes, :link_type_array, :node_id
validates :name, presence: { message: "不能为空" }
validates :name, presence: { message: "不能为空" }, uniqueness: true
validates :full_name, length: { maximum: 200, too_long: "不能超过200个字符" }
validates :label, length: { maximum: 200, too_long: "不能超过200个字符" }
validates :label, length: { maximum: 200, too_long: "不能超过200个字符" }, uniqueness: true
validates :description, length: { maximum: 65535, too_long: "不能超过65535个字符"}

View File

@ -7,6 +7,7 @@
# content :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
# remote_ip :string(255)
#
# Indexes
#

View File

@ -105,6 +105,24 @@ class Issue < ApplicationRecord
scope :pm_includes, -> {includes(:project, :show_issue_tags, :issue_status, :priority, :version, :user, :show_assigners, :comment_journals, :operate_journals)}
scope :closed, ->{where(status_id: 5)}
scope :opened, ->{where.not(status_id: 5)}
scope :with_assigner_info, -> {
select("DISTINCT issues.*, u.id AS user_id, CONVERT(COALESCE(u.nickname, u.login) USING gbk) AS user_name").
joins("LEFT JOIN (
SELECT ia.issue_id, ia.assigner_id
FROM issue_assigners ia
JOIN (
SELECT issue_id, MIN(id) AS min_assigner_id
FROM issue_assigners
GROUP BY issue_id
) tmp ON ia.issue_id = tmp.issue_id AND ia.id = tmp.min_assigner_id
) assigner ON issues.id = assigner.issue_id
LEFT JOIN users u ON assigner.assigner_id = u.id")
}
scope :ordered_by_assigner, -> (sort_direction) {
order("CASE WHEN u.id IS NULL THEN 1 ELSE 0 END ASC")
.order("CONVERT(COALESCE(u.nickname, u.login) USING gbk) #{sort_direction}")
}
after_create :incre_project_common, :incre_user_statistic, :incre_platform_statistic
before_save :check_pm_and_update_due_date
after_save :incre_or_decre_closed_issues_count, :change_versions_count, :send_update_message_to_notice_system, :associate_attachment_container, :generate_uuid
@ -124,6 +142,20 @@ class Issue < ApplicationRecord
end
end
def pm_issue_type_chinese_string
case pm_issue_type
when 1
"需求"
when 2
"任务"
when 3
"缺陷"
else
"疑修"
end
end
def destroy_be_pm_links
PmLink.where(be_linkable_type:"Issue",be_linkable_id:self.id).map(&:destroy)
end

View File

@ -110,7 +110,7 @@ class Journal < ApplicationRecord
content += "<b>#{self.issue.subject}</b>"
else
prefix = '将'
prefix += "计划" if self.issue.pm_issue_type == 1
prefix += "需求" if self.issue.pm_issue_type == 1
prefix += "任务" if self.issue.pm_issue_type == 2
prefix += "缺陷" if self.issue.pm_issue_type == 3
prefix += "<b>#{self.issue.subject}</b>"
@ -358,9 +358,9 @@ class Journal < ApplicationRecord
new_value = "未设置" if detail.value.blank?
content += "将工作项类型由<b>#{old_value}</b>更改为<b>#{new_value}</b>"
end
content.gsub!('1', '需求')
content.gsub!('2', '任务')
content.gsub!('3', '缺陷')
content.gsub!('<b>1</b>', '需求')
content.gsub!('<b>2</b>', '任务')
content.gsub!('<b>3</b>', '缺陷')
return content
when 'pm_sprint_id'
old_value = detail.old_value
@ -483,36 +483,30 @@ class Journal < ApplicationRecord
end
return content
when 'link_issue'
old_value = Issue.where(id: detail.old_value.to_s.split(",")).map{|i| "<b>[#{i.pm_issue_type}]-<#{i.subject}></b>"}.join("")
new_value = Issue.where(id: detail.value.to_s.split(",")).map{|i| "<b>[#{i.pm_issue_type}]-<#{i.subject}></b>"}.join("")
old_value = Issue.where(id: detail.old_value.to_s.split(",")).map{|i| i.pm_issue_type.nil? && i.pm_project_id.nil? ? "疑修 <b>【#{i.subject}】</b>" : "工作项 <b>[#{i.pm_issue_type_chinese_string}]-<#{i.subject}></b>"}.join("")
new_value = Issue.where(id: detail.value.to_s.split(",")).map{|i| i.pm_issue_type.nil? && i.pm_project_id.nil? ? "疑修 <b>【#{i.subject}】</b>" : "工作项 <b>[#{i.pm_issue_type_chinese_string}]-<#{i.subject}></b>"}.join("")
if detail.old_value.nil? || detail.old_value.blank?
content += "新建了关联的工作项#{new_value}"
content += "新建了关联的#{new_value}"
else
if detail.value.nil? || detail.value.blank?
content += "删除了关联的工作项#{old_value}"
content += "删除了关联的#{old_value}"
else
content += "新建了关联的工作项#{new_value}"
content += "新建了关联的#{new_value}"
end
end
content.gsub!('1', "需求")
content.gsub!('2', "任务")
content.gsub!('3', "缺陷")
return content
when 'tag_link_issue'
old_value = Issue.where(id: detail.old_value.to_s.split(",")).map{|i| "<b>[#{i.pm_issue_type}]-<#{i.subject}></b>"}.join("")
new_value = Issue.where(id: detail.value.to_s.split(",")).map{|i| "<b>[#{i.pm_issue_type}]-<#{i.subject}></b>"}.join("")
old_value = Issue.where(id: detail.old_value.to_s.split(",")).map{|i| i.pm_issue_type.nil? && i.pm_project_id.nil? ? "疑修 <b>【#{i.subject}】</b>" : "工作项 <b>[#{i.pm_issue_type_chinese_string}]-<#{i.subject}></b>"}.join("")
new_value = Issue.where(id: detail.value.to_s.split(",")).map{|i| i.pm_issue_type.nil? && i.pm_project_id.nil? ? "疑修 <b>【#{i.subject}】</b>" : "工作项 <b>[#{i.pm_issue_type_chinese_string}]-<#{i.subject}></b>"}.join("")
if detail.old_value.nil? || detail.old_value.blank?
content += "关联了工作项#{new_value}"
content += "关联了#{new_value}"
else
if detail.value.nil? || detail.value.blank?
content += "取消了关联的工作项#{old_value}"
content += "取消了关联的#{old_value}"
else
content += "关联了工作项#{new_value}"
content += "关联了#{new_value}"
end
end
content.gsub!('1', "需求")
content.gsub!('2', "任务")
content.gsub!('3', "缺陷")
return content
when 'issue'
issue = self.issue
@ -535,6 +529,10 @@ class Journal < ApplicationRecord
case detail.property
when 'issue'
content += "创建了<b>疑修</b>"
return content
when 'pull_request'
content += "创建了<b>合并请求</b>"
return content
when 'attachment'
old_value = Attachment.where("BINARY id in (?) or uuid in (?)", detail.old_value.to_s.split(","), detail.old_value.to_s.split(",")).pluck(:filename).join("")
new_value = Attachment.where("BINARY id in (?) or uuid in (?)", detail.value.to_s.split(","), detail.value.to_s.split(",")).pluck(:filename).join("")
@ -544,6 +542,7 @@ class Journal < ApplicationRecord
new_value = "" if new_value.blank?
content += "将附件由<b>#{old_value}</b>更改为<b>#{new_value}</b>"
end
return content
when 'issue_tag'
old_value = IssueTag.where(id: detail.old_value.split(",")).pluck(:name).join("")
new_value = IssueTag.where(id: detail.value.split(",")).pluck(:name).join("")
@ -553,6 +552,17 @@ class Journal < ApplicationRecord
new_value = "" if new_value.blank?
content += "将标记由<b>#{old_value}</b>更改为<b>#{new_value}</b>"
end
return content
when 'attached_issue'
old_value = Issue.where(id: detail.old_value.split(",")).pluck(:subject).join("")
new_value = Issue.where(id: detail.value.split(",")).pluck(:subject).join("")
if old_value.nil? || old_value.blank?
content += "添加了<b>#{new_value}</b>关联疑修"
else
new_value = "" if new_value.blank?
content += "将关联疑修由<b>#{old_value}</b>更改为<b>#{new_value}</b>"
end
return content
when 'assigner'
old_value = User.where(id: detail.old_value.split(",")).map{|u| u.real_name}.join("")
new_value = User.where(id: detail.value.split(",")).map{|u| u.real_name}.join("")
@ -562,13 +572,38 @@ class Journal < ApplicationRecord
new_value = "" if new_value.blank?
content += "将负责人由<b>#{old_value}</b>更改为<b>#{new_value}</b>"
end
return content
when 'reviewer'
old_value = User.find_by(id: detail.old_value)&.real_name
new_value = User.find_by(id: detail.value)&.real_name
if old_value.nil? || old_value.blank?
content += "添加了审查人员<b>#{new_value}</b>"
else
new_value = "" if new_value.blank?
content += "将审查人员更改为<b>#{new_value}</b>"
end
return content
when 'attr'
content += ""
case detail.prop_key
when 'subject'
content += "修改了<b>标题</b>"
content = "修改了<b>标题</b>"
return content
when 'description'
content += "修改了<b>描述</b>"
content = "修改了<b>描述</b>"
return content
when 'status'
case detail.value.to_i
when 0
content = "<b>打开了</b>合并请求"
when 1
content = "<b>合并了</b>合并请求"
when 2
content = "<b>拒绝了</b>合并请求"
when 4
content = "<b>重新打开了</b>合并请求"
end
return content
when 'status_id'
old_value = IssueStatus.find_by_id(detail.old_value)&.name
new_value = IssueStatus.find_by_id(detail.value)&.name
@ -604,8 +639,9 @@ class Journal < ApplicationRecord
new_value = "" if new_value.blank?
content += "由<b>#{old_value}</b>更改为<b>#{new_value}</b>"
end
return content
end
content = self.pm_operate_content if content == ""
end
def journal_content

View File

@ -39,7 +39,7 @@ class Laboratory < ApplicationRecord
validates :identifier, uniqueness: { case_sensitive: false }, allow_nil: true
delegate :name, :navbar, :footer, :login_logo_url, :nav_logo_url, :tab_logo_url, :default_navbar, to: :laboratory_setting
delegate :name, :navbar, :footer, :login_logo_url, :nav_logo_url, :pm_logo_url, :tab_logo_url, :default_navbar, to: :laboratory_setting
def site
rails_env = EduSetting.get('rails_env')

View File

@ -34,7 +34,9 @@ class LaboratorySetting < ApplicationRecord
def nav_logo_url
image_url('nav')
end
def pm_logo_url
image_url('pm_logo')
end
def tab_logo_url
image_url('tab')
end

View File

@ -0,0 +1,27 @@
# == Schema Information
#
# Table name: open_users
#
# id :integer not null, primary key
# user_id :integer
# type :string(255)
# uid :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# extra :text(65535)
#
# Indexes
#
# index_open_users_on_type_and_uid (type,uid) UNIQUE
# index_open_users_on_user_id (user_id)
#
class OpenUsers::Ccf < OpenUser
def nickname
extra&.[]('username')
end
def en_type
'ccf'
end
end

View File

@ -0,0 +1,27 @@
# == Schema Information
#
# Table name: open_users
#
# id :integer not null, primary key
# user_id :integer
# type :string(255)
# uid :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# extra :text(65535)
#
# Indexes
#
# index_open_users_on_type_and_uid (type,uid) UNIQUE
# index_open_users_on_user_id (user_id)
#
class OpenUsers::Openi < OpenUser
def nickname
extra&.[]('nickname')
end
def en_type
'openi'
end
end

View File

@ -0,0 +1,24 @@
# == Schema Information
#
# Table name: project_actions
#
# id :integer not null, primary key
# project_id :integer
# type :string(255)
# user_id :integer
# event_extra :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_project_actions_on_project_id (project_id)
#
class ProjectAction < ApplicationRecord
belongs_to :project
belongs_to :user, optional: true
serialize :event_extra, Hash
end

View File

@ -0,0 +1,30 @@
# == Schema Information
#
# Table name: project_actions
#
# id :integer not null, primary key
# project_id :integer
# type :string(255)
# user_id :integer
# event_extra :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_project_actions_on_project_id (project_id)
#
class ProjectActions::ForkEvent < ProjectAction
def self.build(project, user, fork_project)
project_action = new(project: project, user: user)
project_action.event_extra = {
forkee_id: fork_project.id,
forkee_name: fork_project.identifier,
forkee_owner_id: fork_project.owner.id,
forkee_owner_login: fork_project.owner.login
}.as_json
project_action.save!
end
end

View File

@ -0,0 +1,29 @@
# == Schema Information
#
# Table name: project_actions
#
# id :integer not null, primary key
# project_id :integer
# type :string(255)
# user_id :integer
# event_extra :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_project_actions_on_project_id (project_id)
#
class ProjectActions::IssueCommentEvent < ProjectAction
def self.build(project, user, issue, journal)
project_action = new(project: project, user: user)
project_action.event_extra = {
issue_id: issue.id,
issue_number: issue.project_issues_index,
body: journal.notes
}.as_json
project_action.save!
end
end

View File

@ -0,0 +1,33 @@
# == Schema Information
#
# Table name: project_actions
#
# id :integer not null, primary key
# project_id :integer
# type :string(255)
# user_id :integer
# event_extra :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_project_actions_on_project_id (project_id)
#
class ProjectActions::IssuesEvent < ProjectAction
def self.build(project, user, issue, action)
project_action = new(project: project, user: user)
project_action.event_extra = {
issue_id: issue.id,
issue_number: issue.project_issues_index,
issue_author_id: issue.author_id,
issue_author_login: issue.user.login,
title: issue.subject,
body: issue.description,
action: action
}.as_json
project_action.save!
end
end

View File

@ -0,0 +1,41 @@
# == Schema Information
#
# Table name: project_actions
#
# id :integer not null, primary key
# project_id :integer
# type :string(255)
# user_id :integer
# event_extra :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_project_actions_on_project_id (project_id)
#
class ProjectActions::PullRequestEvent < ProjectAction
def self.build(project, user, pull_request, action)
project_action = new(project: project, user: user)
gitea_pull_request = Gitea::PullRequest::FilesService.call(project.owner.login, project.identifier, pull_request.gitea_number, project&.owner&.gitea_token)
project_action.event_extra = {
action: action,
pull_id: pull_request.id,
pull_number: pull_request.gitea_number,
title: pull_request.title,
# body: pull_request.body,
# git_diff: gitea_pull_request,
additions: gitea_pull_request['TotalAddition'],
deletions: gitea_pull_request['TotalDeletion'],
changed_files: gitea_pull_request['NumFiles'],
merged: pull_request.status == 1,
base_ref: pull_request.base,
head_repo_id: pull_request.is_original ? pull_request.fork_project_id : pull_request.project_id,
head_repo_name: pull_request.project&.identifier,
head_ref: pull_request.head
}.as_json
project_action.save!
end
end

View File

@ -0,0 +1,29 @@
# == Schema Information
#
# Table name: project_actions
#
# id :integer not null, primary key
# project_id :integer
# type :string(255)
# user_id :integer
# event_extra :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_project_actions_on_project_id (project_id)
#
class ProjectActions::PullRequestReviewCommentEvent < ProjectAction
def self.build(project, user, pull_request, journal)
project_action = new(project: project, user: user)
project_action.event_extra = {
pull_id: pull_request.id,
pull_number: pull_request.gitea_number,
body: journal.notes
}.as_json
project_action.save!
end
end

View File

@ -0,0 +1,29 @@
# == Schema Information
#
# Table name: project_actions
#
# id :integer not null, primary key
# project_id :integer
# type :string(255)
# user_id :integer
# event_extra :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_project_actions_on_project_id (project_id)
#
class ProjectActions::ReleaseEvent < ProjectAction
def self.build(project, user, version_release)
project_action = new(project: project, user: user)
project_action.event_extra = {
tag_name: version_release.tag_name,
name: version_release.name,
body: version_release.body
}.as_json
project_action.save!
end
end

View File

@ -0,0 +1,24 @@
# == Schema Information
#
# Table name: project_actions
#
# id :integer not null, primary key
# project_id :integer
# type :string(255)
# user_id :integer
# event_extra :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_project_actions_on_project_id (project_id)
#
class ProjectActions::StarEvent < ProjectAction
def self.build(project, user)
project_action = new(project: project, user: user)
project_action.save!
end
end

View File

@ -0,0 +1,19 @@
# == Schema Information
#
# Table name: project_templates
#
# id :integer not null, primary key
# type :string(255)
# project_id :integer
# name :string(255)
# content :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_project_templates_on_project_id (project_id)
#
class ProjectTemplate < ApplicationRecord
end

View File

@ -0,0 +1,62 @@
# == Schema Information
#
# Table name: project_templates
#
# id :integer not null, primary key
# type :string(255)
# project_id :integer
# name :string(255)
# content :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_project_templates_on_project_id (project_id)
#
class ProjectTemplates::Issue < ProjectTemplate
def self.init_data(project_id)
if !self.exists?(project_id: project_id, name: "bug修复模板")
self.create!(
project_id: project_id,
name: "bug修复模板",
content: '## 问题描述
[Bug是什么]
## 复现步骤
1. []
2. []
3. [...]
...
## 期望行为
[]
## 实际行为
[]
## 环境信息
- [ macOS 12.3]
- /[ Chrome 101]
- /[ v1.2.0 main]
## 附加信息
[]'
)
end
if !self.exists?(project_id: project_id, name: "功能请求模板")
self.create!(
project_id: project_id,
name: "功能请求模板",
content: '## 功能描述
[]
## 解决方案
[]
## 替代方案
[]
## 影响范围
[/]
## 补充说明
[]'
)
end
end
end

View File

@ -0,0 +1,51 @@
# == Schema Information
#
# Table name: project_templates
#
# id :integer not null, primary key
# type :string(255)
# project_id :integer
# name :string(255)
# content :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_project_templates_on_project_id (project_id)
#
class ProjectTemplates::PullRequest < ProjectTemplate
def self.init_data(project_id)
if !self.exists?(project_id: project_id, name: "bug修复模板")
self.create!(
project_id: project_id,
name: "bug修复模板",
content: '## 问题描述
[]
## 问题复现步骤
1. []
2. []
3. []
...
## 根本原因
[]
## 修复方案
[]'
)
end
if !self.exists?(project_id: project_id, name: "功能增强模板")
self.create!(
project_id: project_id,
name: "功能增强模板",
content: '## 合并请求描述
[]
## 相关Issue
Issue编号#{Issue编号}
## 变更内容
### 1. 增强了xxx'
)
end
end
end

View File

@ -22,7 +22,7 @@ class ProjectTrend < ApplicationRecord
CREATE = 'create'
MERGE = 'merge'
belongs_to :project
belongs_to :project, optional: true
belongs_to :trend, polymorphic: true, optional: true
belongs_to :user
end

View File

@ -31,6 +31,7 @@ class PullRequest < ApplicationRecord
MERGED = 1
CLOSED = 2
NEEDCLA = 3
REOPENED = 4
belongs_to :issue
belongs_to :user
@ -144,6 +145,18 @@ class PullRequest < ApplicationRecord
"#{Rails.application.config_for(:configuration)['platform_url']}/#{self.project.owner.login}/#{self.project.name}/pulls/#{self.id}"
end
def closed?
self.status == CLOSED
end
def merged?
self.status == MERGED
end
def opened?
self.status == OPEN
end
def pr_status
case status
when 0

View File

@ -0,0 +1,23 @@
# == Schema Information
#
# Table name: sync_repositories
#
# id :integer not null, primary key
# project_id :integer
# type :string(255)
# repo_name :string(255)
# external_repo_address :string(255)
# sync_granularity :integer
# sync_direction :integer
# created_at :datetime not null
# updated_at :datetime not null
# external_token :string(255)
# webhook_gid :integer
#
# Indexes
#
# index_sync_repositories_on_project_id (project_id)
#
class SyncRepositories::Osredm < SyncRepository
end

View File

@ -1,19 +1,19 @@
# == Schema Information
#
# Table name: tokens
#
# id :integer not null, primary key
# user_id :integer default("0"), not null
# action :string(30) default(""), not null
# value :string(40) default(""), not null
# created_on :datetime not null
#
# Indexes
#
# index_tokens_on_user_id (user_id)
# tokens_value (value) UNIQUE
#
# == Schema Information
#
# Table name: tokens
#
# id :integer not null, primary key
# user_id :integer default("0"), not null
# action :string(30) default(""), not null
# value :string(40) default(""), not null
# created_on :datetime not null
#
# Indexes
#
# index_tokens_on_user_id (user_id)
# tokens_value (value) UNIQUE
#
#
@ -50,7 +50,10 @@ class Token < ActiveRecord::Base
token = Token.create(:user => user, :action => type)
Rails.logger.info "###### Token.get_token_from_user is nul and agine create token: #{token&.value}"
else
token.update_attribute(:created_on, Time.now)
unless Rails.cache.read("token::update::created_on::#{token.id}").present?
token.update_attribute(:created_on, Time.now)
Rails.cache.write("token::update::created_on::#{token.id}", true, expires_in: 5.minutes)
end
end
token
end

View File

@ -115,7 +115,7 @@ class User < Owner
# trustie: 来自Trustie平台
# forge: 平台本身注册的用户
# military: 军科的用户
enumerize :platform, in: [:forge, :educoder, :trustie, :military, :github, :gitee, :qq, :wechat, :bot, :acge], default: :forge, scope: :shallow
enumerize :platform, in: [:forge, :educoder, :trustie, :military, :github, :gitee, :qq, :wechat, :bot, :acge, :ccf, :openi], default: :forge, scope: :shallow
belongs_to :laboratory, optional: true
has_one :user_extension, dependent: :destroy
@ -693,8 +693,9 @@ class User < Owner
def self.try_to_autologin(key)
user = Token.find_active_user('autologin', key)
if user
Rails.cache.fetch("user::update::last_login_on::#{user.id}",:expires_in => 5.minutes) do
unless Rails.cache.read("user::update::last_login_on::#{user.id}").present?
user.update(last_login_on: Time.now)
Rails.cache.write("user::update::last_login_on::#{user.id}", true, expires_in: 5.minutes)
end
end
user

View File

@ -25,16 +25,20 @@
class UserAction < ApplicationRecord
belongs_to :user, optional: true
after_save :add_user_info
serialize :data_bank, JSON
def action_name
case action_type
when "DestroyUser" then "用户注销"
when "DestroyUser" then "删除用户"
when "DestroyProject" then "删除项目"
when "Login" then "登录"
when "Logout" then "退出登录"
when "DestroyOrganization" then "删除组织"
when "Attachment" then "上传附件"
else self.action_type
end
end
@ -42,7 +46,7 @@ class UserAction < ApplicationRecord
def opt_user_name
user = User.find_by(id: self.user_id)
if user.present?
user&.login
user&.real_name
else
del_user = UserAction.find_by(action_type: "DestroyUser", action_id: self.user_id)
del_user.present? ? del_user.user.login : "不存在用户:#{user_id}"
@ -53,6 +57,14 @@ class UserAction < ApplicationRecord
case action_type
when "DestroyUser" then "账号:#{user&.login}<br/>邮箱:#{user&.mail}<br/>手机号:#{user&.phone}<br/>昵称:#{user&.nickname}<br/>"
when "DestroyProject" then "项目名称:#{project&.name}<br/>项目标识:#{project&.identifier}<br/>"
when "DestroyOrganization" then "组织名称:#{organization&.nickname}<br/>组织标识:#{organization&.login}<br/>"
when 'Attachment'
issue = Issue.find_by_id(attachment.container_id)
if attachment.container_type == "Issue" && issue&.issue_classify == "issue"
return "上传附件issue地址<a href=\"#{Rails.application.config_for(:configuration)['platform_url']}/#{issue&.project&.owner&.login}/#{issue&.project&.identifier}/issues/#{issue&.project_issues_index}\">#{issue&.subject}</a>"
else
return "上传附件地址:<a href=\"#{Rails.application.config_for(:configuration)['platform_url']}/attachments/#{attachment.uuid}\">#{attachment.title}</a>"
end
else "--"
end
end
@ -74,6 +86,26 @@ class UserAction < ApplicationRecord
end
action_project
end
def organization
action_organization = if action_type == "DestroyOrganization" && data_bank.present?
build_mode("Organization")
else
Organization.find_by(id: self.action_id)
end
action_organization
end
def attachment
action_attachment = if action_type == "Attachment" && data_bank.present?
build_mode("Attachment")
else
Attachment.find_by(id: self.action_id)
end
action_attachment
end
def build_mode(model_name)
model = model_name.constantize.new
model_name.constantize.column_names.each do |col|

View File

@ -40,6 +40,7 @@ class Admins::SaveLaboratorySettingService < ApplicationService
save_image_file(params[:nav_logo], 'nav')
save_image_file(params[:login_logo], 'login')
save_image_file(params[:tab_logo], 'tab')
save_image_file(params[:pm_logo], 'pm_logo')
save_image_file(params[:subject_banner], '_subject_banner')
save_image_file(params[:course_banner], '_course_banner')
save_image_file(params[:competition_banner], '_competition_banner')

Some files were not shown because too many files have changed in this diff Show More