From 6bf077902b25fb22c1f0f3a570b688a53834e937 Mon Sep 17 00:00:00 2001 From: sam Date: Fri, 24 Jun 2022 13:33:47 +0800 Subject: [PATCH] =?UTF-8?q?=E9=80=9A=E8=BF=87=E7=A7=81=E4=BA=BA=E4=BB=A4?= =?UTF-8?q?=E7=89=8C=E5=AF=BC=E5=85=A5=E4=BB=93=E5=BA=93=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E8=87=AA=E5=BB=BA=20GitLab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + .../build/RepositoryController.java | 211 ++++++++++++++++-- web-vue/src/pages/repository/list.vue | 8 + 3 files changed, 198 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efa3fadf4..2e73f6b02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ 4. 【agent】文件备份对比流程异步处理,避免大文件对比耗时阻塞(感谢@ʟᴊx💎💎) 5. 【server】修复通过私人令牌导入仓库表格主键指定错误 6. 【server】修复 GitLab 通过私人令牌导入仓库接口分页错误 +7. 【server】通过私人令牌导入仓库支持自建 GitLab ### 🐞 解决BUG、优化功能 diff --git a/modules/server/src/main/java/io/jpom/controller/build/RepositoryController.java b/modules/server/src/main/java/io/jpom/controller/build/RepositoryController.java index 369490b4f..0caf22911 100644 --- a/modules/server/src/main/java/io/jpom/controller/build/RepositoryController.java +++ b/modules/server/src/main/java/io/jpom/controller/build/RepositoryController.java @@ -24,6 +24,7 @@ package io.jpom.controller.build; import cn.hutool.core.convert.Convert; import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.lang.Tuple; import cn.hutool.core.lang.Validator; import cn.hutool.core.util.StrUtil; @@ -36,6 +37,7 @@ import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpUtil; import cn.jiangzeyin.common.JsonMessage; import cn.jiangzeyin.common.validator.ValidatorItem; +import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import io.jpom.build.BuildUtil; @@ -52,6 +54,7 @@ import io.jpom.plugin.PluginFactory; import io.jpom.service.dblog.BuildInfoService; import io.jpom.service.dblog.RepositoryService; import io.jpom.system.JpomRuntimeException; +import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.util.Assert; @@ -61,8 +64,10 @@ import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import java.io.File; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; /** @@ -189,6 +194,7 @@ public class RepositoryController extends BaseServerController { Page page = repositoryService.parsePage(paramMap); String token = paramMap.get("token"); Assert.hasText(token, "请填写个人令牌"); + String gitlabAddress = paramMap.getOrDefault("gitlabAddress", "https://gitlab.com"); // String type = paramMap.get("type"); PageResultDto pageResultDto; @@ -200,7 +206,7 @@ public class RepositoryController extends BaseServerController { pageResultDto = this.githubRepos(token, page); break; case "gitlab": - pageResultDto = this.gitlabRepos(token, page); + pageResultDto = this.gitlabRepos(token, page, gitlabAddress); break; default: throw new IllegalArgumentException("不支持的类型"); @@ -215,32 +221,40 @@ public class RepositoryController extends BaseServerController { * * @param token 个人令牌 * @param page 分页 + * @param gitlabAddress gitLab 地址 * @return page */ - private PageResultDto gitlabRepos(String token, Page page) { - // - HttpResponse userResponse = HttpUtil.createGet("https://gitlab.com/api/v4/user") - .form("access_token", token) - .execute(); + private PageResultDto gitlabRepos(String token, Page page, String gitlabAddress) { + // 删除最后的 / + if (gitlabAddress.endsWith("/")) { + gitlabAddress = gitlabAddress.substring(0, gitlabAddress.length() - 1); + } + + // 内部自建 GitLab,一般都没有配置 https 协议,如果用户没有指定协议,默认走 http,gitlab 走 https + // https 访问不了的情况下自动替换为 http + if (!StrUtil.startWithAnyIgnoreCase(gitlabAddress, "http://", "https://")) { + gitlabAddress = "http://" + gitlabAddress; + } + + HttpResponse userResponse = null; + try { + userResponse = GitLabUtil.getGitLabUserInfo(gitlabAddress, token); + } catch (IORuntimeException ioRuntimeException) { + // 连接超时,切换至 http 协议进行重试 + if (StrUtil.startWithIgnoreCase(gitlabAddress, "https")) { + gitlabAddress = "http" + gitlabAddress.substring(5); + userResponse = GitLabUtil.getGitLabUserInfo(gitlabAddress, token); + } + Assert.state(userResponse != null, "无法连接至 GitLab:" + ioRuntimeException.getMessage()); + } + Assert.state(userResponse.isOk(), "令牌不正确:" + userResponse.body()); JSONObject userBody = JSONObject.parseObject(userResponse.body()); String username = userBody.getString("username"); - // 拉取仓库信息 - HttpResponse reposResponse = HttpUtil.createGet("https://gitlab.com/api/v4/projects") - .form("private_token", token) - .form("membership", true) - .form("simple", true) - .form("order_by", "updated_at") - .form("page", page.getPageNumber()) - .form("per_page", page.getPageSize()) - .execute(); - String body = reposResponse.body(); - Assert.state(userResponse.isOk(), "拉取仓库信息错误:" + body); - String totalCountStr = reposResponse.header("X-Total"); - int totalCount = Convert.toInt(totalCountStr, 0); - //String totalPage = reposResponse.header("total_page"); - JSONArray jsonArray = JSONArray.parseArray(body); + Map gitLabRepos = GitLabUtil.getGitLabRepos(gitlabAddress, token, page); + + JSONArray jsonArray = JSONArray.parseArray((String) gitLabRepos.get("body")); List objects = jsonArray.stream().map(o -> { JSONObject repo = (JSONObject) o; JSONObject jsonObject = new JSONObject(); @@ -256,7 +270,7 @@ public class RepositoryController extends BaseServerController { return jsonObject; }).collect(Collectors.toList()); // - PageResultDto pageResultDto = new PageResultDto<>(page.getPageNumber(), page.getPageSize(), totalCount); + PageResultDto pageResultDto = new PageResultDto<>(page.getPageNumber(), page.getPageSize(), (int) gitLabRepos.get("total")); pageResultDto.setResult(objects); return pageResultDto; } @@ -458,4 +472,157 @@ public class RepositoryController extends BaseServerController { FileUtil.del(rsaFile); return JsonMessage.getString(200, "删除成功"); } + + /** + * GitLab 版本号信息,参考:https://docs.gitlab.com/ee/api/version.html + */ + @Data + private static class GitLabVersionInfo { + + /** + * 版本号,如:8.13.0-pre + */ + private String version; + + /** + * 修订号,如:4e963fe + */ + private String revision; + + /** + * API 版本号,如:v4 + */ + private String apiVersion; + } + + /** + * GitLab 工具 + */ + private static class GitLabUtil { + + /** + * GitLab 版本信息容器,key:GitLab 地址,value:GitLabVersionInfo + */ + private static final Map gitlabVersionMap = new ConcurrentHashMap<>(); + + /** + * 获取 GitLab 版本 + * + * @param gitlabAddress GitLab 地址 + * @param token 用户 token + * @return 请求结果 + */ + private static HttpResponse getGitLabVersion(String gitlabAddress, String token, String apiVersion) { + // 参考:https://docs.gitlab.com/ee/api/version.html + return HttpUtil.createGet(StrUtil.format("{}/api/{}/version", gitlabAddress, apiVersion)) + .header("PRIVATE-TOKEN", token) + .execute(); + } + + /** + * 获取 GitLab 版本信息 + * + * @param url GitLab 地址 + * @param token 用户 token + */ + private static GitLabVersionInfo getGitLabVersionInfo(String url, String token) { + // 缓存中有的话,从缓存读取 + GitLabVersionInfo gitLabVersionInfo = gitlabVersionMap.get(url); + if (gitLabVersionInfo != null) + return gitLabVersionInfo; + + // 获取 GitLab 版本号信息 + GitLabVersionInfo glvi = null; + String apiVersion = "v4"; + HttpResponse v4 = getGitLabVersion(url, token, apiVersion); + if (v4 != null) { + glvi = JSON.parseObject(v4.body(), GitLabVersionInfo.class); + } else { + apiVersion = "v3"; + HttpResponse v3 = getGitLabVersion(url, token, apiVersion); + if (v3 != null) { + glvi = JSON.parseObject(v3.body(), GitLabVersionInfo.class); + } + } + + Assert.state(glvi != null, "获取 GitLab 版本号失败,请检查 GitLab 地址和 token 是否正确"); + + // 添加到缓存中 + glvi.setApiVersion(apiVersion); + gitlabVersionMap.put(url, glvi); + + return glvi; + } + + /** + * 获取 GitLab API 版本号 + * + * @param url GitLab 地址 + * @param token 用户 token + * @return GitLab API 版本号,如:v4 + */ + private static String getGitLabApiVersion(String url, String token) { + return getGitLabVersionInfo(url, token).getApiVersion(); + } + + /** + * 获取 GitLab 用户信息 + * + * @param gitlabAddress GitLab 地址 + * @param token 用户 token + * @return 请求结果 + */ + private static HttpResponse getGitLabUserInfo(String gitlabAddress, String token) { + // 参考:https://docs.gitlab.com/ee/api/users.html + return HttpUtil.createGet( + StrUtil.format( + "{}/api/{}/user", + gitlabAddress, + getGitLabApiVersion(gitlabAddress, token) + ) + ) + .form("access_token", token) + .timeout(5000) + .execute(); + } + + /** + * 获取 GitLab 仓库信息 + * + * @param gitlabAddress GitLab 地址 + * @param token 用户 token + * @return 响应结果 + */ + private static Map getGitLabRepos(String gitlabAddress, String token, Page page) { + // 参考:https://docs.gitlab.com/ee/api/projects.html + HttpResponse reposResponse = HttpUtil.createGet( + StrUtil.format( + "{}/api/{}/projects", + gitlabAddress, + getGitLabApiVersion(gitlabAddress, token) + ) + ) + .form("private_token", token) + .form("membership", true) + .form("simple", true) + .form("order_by", "updated_at") + .form("page", page.getPageNumber()) + .form("per_page", page.getPageSize()) + .execute(); + + String body = reposResponse.body(); + Assert.state(reposResponse.isOk(), "拉取仓库信息错误:" + body); + + String totalCountStr = reposResponse.header("X-Total"); + int totalCount = Convert.toInt(totalCountStr, 0); + //String totalPage = reposResponse.header("total_page"); + + Map map = new HashMap<>(2); + map.put("body", body); + map.put("total", totalCount); + + return map; + } + } + } diff --git a/web-vue/src/pages/repository/list.vue b/web-vue/src/pages/repository/list.vue index 8831346a9..4617814d4 100644 --- a/web-vue/src/pages/repository/list.vue +++ b/web-vue/src/pages/repository/list.vue @@ -154,6 +154,14 @@ + + + + +