From bf4ecb349937c54eecfb53c5b7210158f6c1f495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AD=8F=E5=AE=8F=E6=96=8C?= <1602586227@qq.com> Date: Sun, 26 Mar 2023 11:32:35 +0800 Subject: [PATCH] =?UTF-8?q?feat(project):=20=E9=87=8D=E6=9E=84=20=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E7=A7=81=E4=BA=BA=E4=BB=A4=E7=89=8C=E5=AF=BC=E5=85=A5?= =?UTF-8?q?=E4=BB=93=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../build/RepositoryController.java | 272 ++---------------- .../build/repository/GitHubUtil.java | 125 -------- .../build/repository/GitLabUtil.java | 193 ------------- .../build/repository/GiteaUtil.java | 153 ---------- .../build/repository/GiteeUtil.java | 126 -------- .../controller/build/repository/GogsUtil.java | 30 -- .../repository/ImportRepoProviderConfig.java | 94 ++++++ .../build/repository/ImportRepoUtil.java | 206 +++++++++++++ .../resources/import-repo-provider/gitea.yml | 20 ++ .../resources/import-repo-provider/gitee.yml | 24 ++ .../resources/import-repo-provider/github.yml | 27 ++ .../resources/import-repo-provider/gitlab.yml | 23 ++ .../import-repo-provider/gitlab_v3.yml | 23 ++ .../resources/import-repo-provider/gogs.yml | 20 ++ web-vue/src/api/repository.js | 7 + web-vue/src/pages/repository/list.vue | 103 +++---- 16 files changed, 519 insertions(+), 927 deletions(-) delete mode 100644 modules/server/src/main/java/io/jpom/controller/build/repository/GitHubUtil.java delete mode 100644 modules/server/src/main/java/io/jpom/controller/build/repository/GitLabUtil.java delete mode 100644 modules/server/src/main/java/io/jpom/controller/build/repository/GiteaUtil.java delete mode 100644 modules/server/src/main/java/io/jpom/controller/build/repository/GiteeUtil.java delete mode 100644 modules/server/src/main/java/io/jpom/controller/build/repository/GogsUtil.java create mode 100644 modules/server/src/main/java/io/jpom/controller/build/repository/ImportRepoProviderConfig.java create mode 100644 modules/server/src/main/java/io/jpom/controller/build/repository/ImportRepoUtil.java create mode 100644 modules/server/src/main/resources/import-repo-provider/gitea.yml create mode 100644 modules/server/src/main/resources/import-repo-provider/gitee.yml create mode 100644 modules/server/src/main/resources/import-repo-provider/github.yml create mode 100644 modules/server/src/main/resources/import-repo-provider/gitlab.yml create mode 100644 modules/server/src/main/resources/import-repo-provider/gitlab_v3.yml create mode 100644 modules/server/src/main/resources/import-repo-provider/gogs.yml 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 8f2f81685..3e4eef6ef 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,7 +24,6 @@ package io.jpom.controller.build; import cn.hutool.core.collection.CollUtil; 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; @@ -32,15 +31,13 @@ import cn.hutool.core.util.URLUtil; import cn.hutool.db.Entity; import cn.hutool.db.Page; import cn.hutool.extra.servlet.ServletUtil; -import cn.hutool.http.HttpResponse; -import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import io.jpom.build.BuildUtil; import io.jpom.common.BaseServerController; import io.jpom.common.JsonMessage; import io.jpom.common.ServerConst; import io.jpom.common.validator.ValidatorItem; -import io.jpom.controller.build.repository.*; +import io.jpom.controller.build.repository.ImportRepoUtil; import io.jpom.model.data.RepositoryModel; import io.jpom.model.enums.GitProtocolEnum; import io.jpom.permission.ClassFeature; @@ -50,7 +47,6 @@ import io.jpom.plugin.IPlugin; import io.jpom.plugin.PluginFactory; import io.jpom.service.dblog.BuildInfoService; import io.jpom.service.dblog.RepositoryService; -import io.jpom.system.JpomRuntimeException; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -197,6 +193,13 @@ public class RepositoryController extends BaseServerController { return new JsonMessage<>(200, "操作成功"); } + @GetMapping(value = "/build/repository/provider_info") + @Feature(method = MethodFeature.LIST) + public JsonMessage>> providerInfo() { + Map> providerList = ImportRepoUtil.getProviderList(); + return JsonMessage.success(HttpStatus.OK.name(), providerList); + } + @GetMapping(value = "/build/repository/authorize_repos") @Feature(method = MethodFeature.LIST) public JsonMessage> authorizeRepos(HttpServletRequest request, @@ -208,258 +211,25 @@ public class RepositoryController extends BaseServerController { Map paramMap = ServletUtil.getParamMap(request); Page page = repositoryService.parsePage(paramMap); Assert.hasText(token, "请填写个人令牌"); - //String gitlabAddress = StrUtil.blankToDefault(paramMap.get("gitlabAddress"), "https://gitlab.com"); - //String giteaAddress = paramMap.get("giteaAddress"); // 搜索条件 // 远程仓库 PageResultDto pageResultDto; - switch (type) { - case "gitee": - pageResultDto = this.giteeRepos(token, page, condition, request); - break; - case "github": - // GitHub 不支持条件搜索 - pageResultDto = this.githubRepos(token, page, request); - break; - case "gitlab": - pageResultDto = this.gitlabRepos(token, page, condition, address, request); - break; - case "gitea": - pageResultDto = this.giteaRepos(token, page, condition, address, request); - break; - case "gogs": - pageResultDto = this.gogsRepos(token, page, condition, address, request); - break; - default: - throw new IllegalArgumentException("不支持的类型"); - } + ImportRepoUtil.getProviderConfig(type); + + String userName = ImportRepoUtil.getCurrentUserName(type, token, address); + cn.hutool.json.JSONObject repoList = ImportRepoUtil.getRepoList(type, condition, page, token, userName, address); + pageResultDto = new PageResultDto<>(page.getPageNumber(), page.getPageSize(), repoList.getLong("total").intValue()); + List objects = repoList.getJSONArray("data").stream().map(o -> { + cn.hutool.json.JSONObject obj = (cn.hutool.json.JSONObject) o; + JSONObject jsonObject = new JSONObject(); + jsonObject.putAll(obj); + jsonObject.put("exists", RepositoryController.this.checkRepositoryUrl(obj.getStr("url"), request)); + return jsonObject; + }).collect(Collectors.toList()); + pageResultDto.setResult(objects); return JsonMessage.success(HttpStatus.OK.name(), pageResultDto); } - /** - * gitlab 仓库 - *

- * https://docs.gitlab.com/ee/api/projects.html#list-all-projects - * - * @param token 个人令牌 - * @param page 分页 - * @param gitlabAddress gitLab 地址 - * @return page - */ - private PageResultDto gitlabRepos(String token, Page page, String condition, String gitlabAddress, HttpServletRequest request) { - gitlabAddress = StrUtil.blankToDefault(gitlabAddress, "https://gitlab.com"); - // 删除最后的 / - 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"); - - Map gitLabRepos = GitLabUtil.getGitLabRepos(gitlabAddress, token, page, condition); - - JSONArray jsonArray = JSONArray.parseArray((String) gitLabRepos.get("body")); - List objects = jsonArray.stream().map(o -> { - JSONObject repo = (JSONObject) o; - JSONObject jsonObject = new JSONObject(); - jsonObject.put("name", repo.getString("name")); - String htmlUrl = repo.getString("http_url_to_repo"); - jsonObject.put("url", htmlUrl); - jsonObject.put("full_name", repo.getString("path_with_namespace")); - // visibility 有三种:public, internal, or private.(非 public,都是 private) - jsonObject.put("private", !StrUtil.equalsIgnoreCase("public", repo.getString("visibility"))); - jsonObject.put("description", repo.getString("description")); - jsonObject.put("username", username); - jsonObject.put("exists", RepositoryController.this.checkRepositoryUrl(htmlUrl, request)); - return jsonObject; - }).collect(Collectors.toList()); - - PageResultDto pageResultDto = new PageResultDto<>(page.getPageNumber(), page.getPageSize(), (int) gitLabRepos.get("total")); - pageResultDto.setResult(objects); - return pageResultDto; - } - - /** - * github 仓库 - * - * @param token 个人令牌 - * @param page 分页 - * @return page - */ - private PageResultDto githubRepos(String token, Page page, HttpServletRequest request) { - GitHubUtil.GitHubUserInfo gitHubUserInfo = GitHubUtil.getGitHubUserInfo(token); - JSONArray gitHubUserReposArray = GitHubUtil.getGitHubUserRepos(token, page); - - List objects = gitHubUserReposArray.stream().map(o -> { - JSONObject repo = (JSONObject) o; - JSONObject jsonObject = new JSONObject(); - jsonObject.put("name", repo.getString("name")); - String cloneUrl = repo.getString("clone_url"); - jsonObject.put("url", cloneUrl); - jsonObject.put("full_name", repo.getString("full_name")); - jsonObject.put("description", repo.getString("description")); - jsonObject.put("private", repo.getBooleanValue("private")); - // - jsonObject.put("username", gitHubUserInfo.getLogin()); - jsonObject.put("exists", RepositoryController.this.checkRepositoryUrl(cloneUrl, request)); - return jsonObject; - }).collect(Collectors.toList()); - // - PageResultDto pageResultDto = new PageResultDto<>(page.getPageNumber(), page.getPageSize(), gitHubUserInfo.public_repos); - pageResultDto.setResult(objects); - return pageResultDto; - } - - /** - * gitee 仓库 - * - * @param token 个人令牌 - * @param page 分页 - * @return page - */ - private PageResultDto giteeRepos(String token, Page page, String condition, HttpServletRequest request) { - String giteeUsername = GiteeUtil.getGiteeUsername(token); - - Map giteeReposMap = GiteeUtil.getGiteeRepos(token, page, condition); - JSONArray jsonArray = (JSONArray) giteeReposMap.get("jsonArray"); - int totalCount = (int) giteeReposMap.get("totalCount"); - - List objects = jsonArray.stream().map(o -> { - JSONObject repo = (JSONObject) o; - JSONObject jsonObject = new JSONObject(); - // 项目名称,如:Jpom - jsonObject.put("name", repo.getString("name")); - - // 项目地址,如:https://gitee.com/dromara/Jpom.git - String htmlUrl = repo.getString("html_url"); - jsonObject.put("url", htmlUrl); - - // 所属者/项目名,如:dromara/Jpom - jsonObject.put("full_name", repo.getString("full_name")); - - // 是否为私有仓库,是私有仓库为 true,非私有仓库为 false - jsonObject.put("private", repo.getBooleanValue("private")); - - // 项目描述,如:简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件 - jsonObject.put("description", repo.getString("description")); - - jsonObject.put("username", giteeUsername); - jsonObject.put("exists", this.checkRepositoryUrl(htmlUrl, request)); - return jsonObject; - }).collect(Collectors.toList()); - - PageResultDto pageResultDto = new PageResultDto<>(page.getPageNumber(), page.getPageSize(), totalCount); - pageResultDto.setResult(objects); - return pageResultDto; - } - - /** - * gogo仓库 - * - * @param token 个人令牌 - * @param page 分页 - * @return page - */ - private PageResultDto gogsRepos(String token, Page page, String condition, String giteaAddress, HttpServletRequest request) { - Assert.hasText(giteaAddress, "请填写 gogs 地址"); - String gogsUsername = GogsUtil.getUsername(giteaAddress, token); - - Map giteaReposMap = GiteaUtil.getRepos(giteaAddress, token, page, condition); - JSONArray jsonArray = (JSONArray) giteaReposMap.get("jsonArray"); - int totalCount = (int) giteaReposMap.get("totalCount"); - - List objects = jsonArray.stream().map(o -> { - JSONObject repo = (JSONObject) o; - JSONObject jsonObject = new JSONObject(); - // 项目名称,如:Jpom - jsonObject.put("name", repo.getString("name")); - - // 项目地址,如:https://10.0.0.1:3000/dromara/Jpom.git - String htmlUrl = repo.getString("html_url"); - jsonObject.put("url", htmlUrl); - - // 所属者/项目名,如:dromara/Jpom - jsonObject.put("full_name", repo.getString("full_name")); - - // 是否为私有仓库,是私有仓库为 true,非私有仓库为 false - jsonObject.put("private", repo.getBooleanValue("private")); - - // 项目描述,如:简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件 - jsonObject.put("description", repo.getString("description")); - - jsonObject.put("username", gogsUsername); - jsonObject.put("exists", this.checkRepositoryUrl(htmlUrl, request)); - return jsonObject; - }).collect(Collectors.toList()); - - PageResultDto pageResultDto = new PageResultDto<>(page.getPageNumber(), page.getPageSize(), totalCount); - pageResultDto.setResult(objects); - return pageResultDto; - } - - /** - * gitea仓库 - * - * @param token 个人令牌 - * @param page 分页 - * @return page - */ - private PageResultDto giteaRepos(String token, Page page, String condition, String giteaAddress, HttpServletRequest request) { - Assert.hasText(giteaAddress, "请填写 gitea 地址"); - String giteaUsername = GiteaUtil.getUsername(giteaAddress, token); - - Map giteaReposMap = GiteaUtil.getRepos(giteaAddress, token, page, condition); - JSONArray jsonArray = (JSONArray) giteaReposMap.get("jsonArray"); - int totalCount = (int) giteaReposMap.get("totalCount"); - - List objects = jsonArray.stream().map(o -> { - JSONObject repo = (JSONObject) o; - JSONObject jsonObject = new JSONObject(); - // 项目名称,如:Jpom - jsonObject.put("name", repo.getString("name")); - - // 项目地址,如:https://10.0.0.1:3000/dromara/Jpom.git - String htmlUrl = repo.getString("html_url"); - jsonObject.put("url", htmlUrl); - - // 所属者/项目名,如:dromara/Jpom - jsonObject.put("full_name", repo.getString("full_name")); - - // 是否为私有仓库,是私有仓库为 true,非私有仓库为 false - jsonObject.put("private", repo.getBooleanValue("private")); - - // 项目描述,如:简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件 - jsonObject.put("description", repo.getString("description")); - - jsonObject.put("username", giteaUsername); - jsonObject.put("exists", this.checkRepositoryUrl(htmlUrl, request)); - return jsonObject; - }).collect(Collectors.toList()); - - PageResultDto pageResultDto = new PageResultDto<>(page.getPageNumber(), page.getPageSize(), totalCount); - pageResultDto.setResult(objects); - return pageResultDto; - } - /** * 检查信息 * diff --git a/modules/server/src/main/java/io/jpom/controller/build/repository/GitHubUtil.java b/modules/server/src/main/java/io/jpom/controller/build/repository/GitHubUtil.java deleted file mode 100644 index 22baf55f5..000000000 --- a/modules/server/src/main/java/io/jpom/controller/build/repository/GitHubUtil.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Code Technology Studio - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.jpom.controller.build.repository; - -import cn.hutool.db.Page; -import cn.hutool.http.Header; -import cn.hutool.http.HttpResponse; -import cn.hutool.http.HttpUtil; -import com.alibaba.fastjson2.JSONArray; -import com.alibaba.fastjson2.JSONObject; -import lombok.Data; -import org.springframework.util.Assert; - -/** - * GitHub 工具 - * - * @author sam - * @since 2023/3/9 - */ -public class GitHubUtil { - /** - * GitHub 用户信息实体类 - *

- * 参考:https://docs.github.com/en/rest/users/users#about-the-users-api - */ - @Data - public static class GitHubUserInfo { - // 只列出目前需要用到的字段 - - /** - * 用户名,如:octocat - */ - private String login; - - /** - * 公开仓库数量,如:2 - */ - public int public_repos; - - /** - * 私有的仓库总数,如:100 - */ - public int total_private_repos; - - /** - * 拥有的私有仓库,如:100 - */ - public int owned_private_repos; - } - - /** - * GitHub 头部 - */ - private static final String GITHUB_HEADER_ACCEPT = "application/vnd.github.v3+json"; - - /** - * GitHub 用户 token 前缀 - */ - private static final String GITHUB_TOKEN = "token "; - - /** - * GitHub API 前缀 - */ - private static final String GITHUB_API_PREFIX = "https://api.github.com"; - - /** - * 获取 GitHub 用户信息 - * - * @param token 用户 token - * @return GitHub 用户信息 - */ - public static GitHubUserInfo getGitHubUserInfo(String token) { - // 参考:https://docs.github.com/en/rest/users/users#about-the-users-api - HttpResponse response = HttpUtil - .createGet(GITHUB_API_PREFIX + "/user") - .header(Header.ACCEPT, GITHUB_HEADER_ACCEPT) - .header(Header.AUTHORIZATION, GITHUB_TOKEN + token) - .execute(); - String body = response.body(); - Assert.state(response.isOk(), "令牌信息错误:" + body); - return JSONObject.parseObject(body, GitHubUserInfo.class); - } - - /** - * 获取 GitHub 仓库信息 - * - * @param token - */ - public static JSONArray getGitHubUserRepos(String token, Page page) { - // 参考:https://docs.github.com/en/rest/repos/repos#list-repositories-for-the-authenticated-user - HttpResponse response = HttpUtil - .createGet(GITHUB_API_PREFIX + "/user/repos") - .header(Header.ACCEPT, GITHUB_HEADER_ACCEPT) - .header(Header.AUTHORIZATION, GITHUB_TOKEN + token) - .form("access_token", token) - .form("sort", "pushed") - .form("page", page.getPageNumber()) - .form("per_page", page.getPageSize()) - .execute(); - String body = response.body(); - Assert.state(response.isOk(), "拉取仓库信息错误:" + body); - return JSONArray.parseArray(body); - } - -} diff --git a/modules/server/src/main/java/io/jpom/controller/build/repository/GitLabUtil.java b/modules/server/src/main/java/io/jpom/controller/build/repository/GitLabUtil.java deleted file mode 100644 index ce8adda6d..000000000 --- a/modules/server/src/main/java/io/jpom/controller/build/repository/GitLabUtil.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Code Technology Studio - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.jpom.controller.build.repository; - -import cn.hutool.core.convert.Convert; -import cn.hutool.core.util.StrUtil; -import cn.hutool.db.Page; -import cn.hutool.http.HttpResponse; -import cn.hutool.http.HttpUtil; -import com.alibaba.fastjson2.JSON; -import lombok.Data; -import org.springframework.util.Assert; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * GitLab 工具 - * - * @author sam - * @since 2023/3/9 - */ -public class GitLabUtil { - /** - * 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 版本信息容器,key:GitLab 地址,value:GitLabVersionInfo - */ - public static final Map gitlabVersionMap = new ConcurrentHashMap<>(); - - /** - * 获取 GitLab 版本 - * - * @param gitlabAddress GitLab 地址 - * @param token 用户 token - * @return 请求结果 - */ - public 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), true) - .header("PRIVATE-TOKEN", token) - .execute(); - } - - /** - * 获取 GitLab 版本信息 - * - * @param url GitLab 地址 - * @param token 用户 token - */ - public 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 - */ - public static String getGitLabApiVersion(String url, String token) { - return getGitLabVersionInfo(url, token).getApiVersion(); - } - - /** - * 获取 GitLab 用户信息 - * - * @param gitlabAddress GitLab 地址 - * @param token 用户 token - * @return 请求结果 - */ - public 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) - ), true - ) - .form("access_token", token) - .timeout(5000) - .execute(); - } - - /** - * 获取 GitLab 仓库信息 - * - * @param gitlabAddress GitLab 地址 - * @param token 用户 token - * @return 响应结果 - */ - public static Map getGitLabRepos(String gitlabAddress, String token, Page page, String condition) { - // 参考:https://docs.gitlab.com/ee/api/projects.html - HttpResponse reposResponse = HttpUtil.createGet( - StrUtil.format( - "{}/api/{}/projects", - gitlabAddress, - getGitLabApiVersion(gitlabAddress, token) - ), true - ) - .form("private_token", token) - .form("membership", true) - // 当 simple=true 时,不返回项目的 visibility,无法判断是私有仓库、公开仓库还是内部仓库 -// .form("simple", true) - .form("order_by", "updated_at") - .form("page", page.getPageNumber()) - .form("per_page", page.getPageSize()) - .form("search", condition) - .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/modules/server/src/main/java/io/jpom/controller/build/repository/GiteaUtil.java b/modules/server/src/main/java/io/jpom/controller/build/repository/GiteaUtil.java deleted file mode 100644 index 92b3cfd7a..000000000 --- a/modules/server/src/main/java/io/jpom/controller/build/repository/GiteaUtil.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Code Technology Studio - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.jpom.controller.build.repository; - -import cn.hutool.core.convert.Convert; -import cn.hutool.db.Page; -import cn.hutool.http.HttpResponse; -import cn.hutool.http.HttpUtil; -import com.alibaba.fastjson2.JSON; -import com.alibaba.fastjson2.JSONArray; -import com.alibaba.fastjson2.JSONObject; -import org.springframework.util.Assert; - -import java.util.HashMap; -import java.util.Map; - -/** - * Gitea 工具 - * - * @author songxinqiang - * @since 2023/3/9 - */ -public class GiteaUtil { - - /** - * Gitea API 版本号 - */ - private static final String API_VERSION = "v1"; - - /** - * 用户授权码 - */ - private static final String ACCESS_TOKEN = "access_token"; - - /** - * 排序方式: 创建时间(created),更新时间(updated),最后推送时间(pushed),仓库所属与名称(full_name)。默认: full_name - */ - private static final String SORT = "sort"; - - /** - * 当前的页码 - */ - private static final String PAGE = "page"; - - /** - * 每页的数量,最大为 100 - */ - private static final String LIMIT = "limit"; - - /** - * 获取 Gitea 用户名 - * - * @param token 用户授权码 - * @return Gitea 用户名 - */ - public static String getUsername(String giteaAddress, String token) { - HttpResponse userResponse = HttpUtil.createGet(giteaAddress + "/api/v1/user", true) - .form(ACCESS_TOKEN, token) - .execute(); - Assert.state(userResponse.isOk(), "令牌不正确:" + userResponse.body()); - JSONObject userBody = JSONObject.parseObject(userResponse.body()); - return userBody.getString("login"); - } - - /** - * 获取 Gitea 用户仓库信息 - * - * @param giteaAddress Gitea 地址 - * @param token 用户授权码 - * @param page 分页参数 - * @return map - */ - public static Map getRepos(String giteaAddress, String token, Page page, String condition) { - if (condition == null) { - HttpResponse reposResponse = HttpUtil.createGet(giteaAddress + "/api/v1/user/repos", true) - .form(ACCESS_TOKEN, token) - //.form(SORT, "newest") - .form(PAGE, page.getPageNumber()) - .form(LIMIT, page.getPageSize()) - // 搜索关键字 - //.form("q", condition) - .execute(); - String body = reposResponse.body(); - Assert.state(reposResponse.isOk(), "获取仓库信息错误:" + body); - // 所有仓库总数,包括公开的和私有的 - String totalCountStr = reposResponse.header("x-total-count"); - int totalCount = Convert.toInt(totalCountStr, 0); - //String totalPage = reposResponse.header("total_page"); - - Map map = new HashMap<>(2); - map.put("jsonArray", JSONArray.parseArray(body)); - // 仓库总数 - map.put("totalCount", totalCount); - return map; - } else { - return getGiteaReposSearch(giteaAddress, token, page, condition); - } - } - - /** - * 获取 Gitea 用户仓库信息搜索 - * - * @param giteaAddress Gitea 地址 - * @param token 用户授权码 - * @param page 分页参数 - * @return - */ - public static Map getGiteaReposSearch(String giteaAddress, String token, Page page, String condition) { - HttpResponse reposResponse = HttpUtil.createGet(giteaAddress + "/api/v1/repos/search", true) - .form(ACCESS_TOKEN, token) - .form(SORT, "created") - .form(PAGE, page.getPageNumber()) - .form(LIMIT, page.getPageSize()) - // 搜索关键字 - .form("q", condition) - .execute(); - String body = reposResponse.body(); - JSONObject jsonObject = JSON.parseObject(body); - JSONArray data = jsonObject.getJSONArray("data"); - Assert.state(reposResponse.isOk(), "获取仓库信息错误:" + data.toString()); - // 所有仓库总数,包括公开的和私有的 - String totalCountStr = reposResponse.header("x-total-count"); - int totalCount = Convert.toInt(totalCountStr, 0); - - Map map = new HashMap<>(2); - map.put("jsonArray", JSONArray.parseArray(data.toString())); - // 仓库总数 - map.put("totalCount", totalCount); - return map; - - } - -} diff --git a/modules/server/src/main/java/io/jpom/controller/build/repository/GiteeUtil.java b/modules/server/src/main/java/io/jpom/controller/build/repository/GiteeUtil.java deleted file mode 100644 index fdedd7bfe..000000000 --- a/modules/server/src/main/java/io/jpom/controller/build/repository/GiteeUtil.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Code Technology Studio - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.jpom.controller.build.repository; - -import cn.hutool.core.convert.Convert; -import cn.hutool.db.Page; -import cn.hutool.http.HttpResponse; -import cn.hutool.http.HttpUtil; -import com.alibaba.fastjson2.JSONArray; -import com.alibaba.fastjson2.JSONObject; -import org.springframework.util.Assert; - -import java.util.HashMap; -import java.util.Map; - -/** - * Gitee 工具 - * - * @author sam - * @since 2023/3/9 - */ -public class GiteeUtil { - - /** - * Gitee API 前缀 - */ - private static final String GITEE_API_PREFIX = "https://gitee.com/api"; - - /** - * Gitee API 版本号 - */ - private static final String API_VERSION = "v5"; - - /** - * Gitee API 地址前缀 - */ - private static final String GITEE_API_URL_PREFIX = GITEE_API_PREFIX + "/" + API_VERSION; - - /** - * 用户授权码 - */ - private static final String ACCESS_TOKEN = "access_token"; - - /** - * 排序方式: 创建时间(created),更新时间(updated),最后推送时间(pushed),仓库所属与名称(full_name)。默认: full_name - */ - private static final String SORT = "sort"; - - /** - * 当前的页码 - */ - private static final String PAGE = "page"; - - /** - * 每页的数量,最大为 100 - */ - private static final String PER_PAGE = "per_page"; - - /** - * 获取 Gitee 用户名 - * - * @param token 用户授权码 - * @return Gitee 用户名 - */ - public static String getGiteeUsername(String token) { - // 参考:https://gitee.com/api/v5/swagger#/getV5User - HttpResponse userResponse = HttpUtil.createGet(GITEE_API_URL_PREFIX + "/user", true) - .form(ACCESS_TOKEN, token) - .execute(); - Assert.state(userResponse.isOk(), "令牌不正确:" + userResponse.body()); - JSONObject userBody = JSONObject.parseObject(userResponse.body()); - return userBody.getString("login"); - } - - /** - * 获取 Gitee 用户仓库信息 - * - * @param token 用户授权码 - * @param page 分页参数 - * @return - */ - public static Map getGiteeRepos(String token, Page page, String condition) { - // 参考:https://gitee.com/api/v5/swagger#/getV5UserRepos - HttpResponse reposResponse = HttpUtil.createGet(GITEE_API_URL_PREFIX + "/user/repos", true) - .form(ACCESS_TOKEN, token) - .form(SORT, "pushed") - .form(PAGE, page.getPageNumber()) - .form(PER_PAGE, page.getPageSize()) - // 搜索关键字 - .form("q", condition) - .execute(); - String body = reposResponse.body(); - Assert.state(reposResponse.isOk(), "获取仓库信息错误:" + body); - - // 所有仓库总数,包括公开的和私有的 - String totalCountStr = reposResponse.header("total_count"); - int totalCount = Convert.toInt(totalCountStr, 0); - //String totalPage = reposResponse.header("total_page"); - - Map map = new HashMap<>(2); - map.put("jsonArray", JSONArray.parseArray(body)); - // 仓库总数 - map.put("totalCount", totalCount); - return map; - } -} diff --git a/modules/server/src/main/java/io/jpom/controller/build/repository/GogsUtil.java b/modules/server/src/main/java/io/jpom/controller/build/repository/GogsUtil.java deleted file mode 100644 index c97ffb48f..000000000 --- a/modules/server/src/main/java/io/jpom/controller/build/repository/GogsUtil.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Code Technology Studio - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.jpom.controller.build.repository; - -/** - * @author bwcx_jzy - * @since 2023/3/19 - */ -public class GogsUtil extends GiteaUtil { -} diff --git a/modules/server/src/main/java/io/jpom/controller/build/repository/ImportRepoProviderConfig.java b/modules/server/src/main/java/io/jpom/controller/build/repository/ImportRepoProviderConfig.java new file mode 100644 index 000000000..8e9134ae8 --- /dev/null +++ b/modules/server/src/main/java/io/jpom/controller/build/repository/ImportRepoProviderConfig.java @@ -0,0 +1,94 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Code Technology Studio + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package io.jpom.controller.build.repository; + +import lombok.Data; + +import java.util.Map; + +/** + * 仓库提供商配置 + * + * @author WeiHongBin + */ +@Data +public class ImportRepoProviderConfig { + private String baseUrl; + /** + * 鉴权方式 1:header 2:from 3:body + */ + private Integer authType; + /** + * 鉴权key 例如:Authorization + */ + private String authKey; + /** + * 鉴权值 例如:Bearer ${token} + */ + private String authValue; + /** + * 扩展参数 + */ + private Map extraParams; + /** + * 扩展参数类型 1:header 2:from 3:body + */ + private Integer extraParamsType; + /** + * 获取用户信息的请求方式 + */ + private String currentUserMethod; + /** + * 获取用户信息的请求地址 + */ + private String currentUserUrl; + /** + * 获取用户名 path + */ + private String userNamePath; + /** + * 获取仓库列表的请求方式 + */ + private String repoListMethod; + /** + * 获取仓库列表的请求地址 + */ + private String repoListUrl; + /** + * 获取仓库列表的请求参数 + */ + private Map repoListParam; + /** + * 获取仓库列表数组 path + */ + private String repoListPath; + /** + * 仓库信息 转换 path + */ + private Map repoConvertPath; + /** + * 获取仓库总数 X-Total + */ + private String repoTotalHeader; + +} diff --git a/modules/server/src/main/java/io/jpom/controller/build/repository/ImportRepoUtil.java b/modules/server/src/main/java/io/jpom/controller/build/repository/ImportRepoUtil.java new file mode 100644 index 000000000..5f788ab7f --- /dev/null +++ b/modules/server/src/main/java/io/jpom/controller/build/repository/ImportRepoUtil.java @@ -0,0 +1,206 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Code Technology Studio + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package io.jpom.controller.build.repository; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.db.Page; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import cn.hutool.http.Method; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.hutool.setting.yaml.YamlUtil; +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.ClassPathResource; +import org.springframework.util.Assert; + +import java.io.InputStream; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +@Slf4j +@UtilityClass +public class ImportRepoUtil { + + private static final String IMPORT_REPO_PROVIDER_DIR = "/import-repo-provider"; + + + @SneakyThrows + public Map> getProviderList() { + String normalize = FileUtil.normalize(IMPORT_REPO_PROVIDER_DIR); + ClassPathResource classPathResource = new ClassPathResource(normalize); + if (!classPathResource.exists()) { + return null; + } + return Arrays.stream(Objects.requireNonNull(classPathResource.getFile() + .listFiles(pathname -> StrUtil.endWith(pathname.getName(), ".yml")))) + .map(file -> { + String name = file.getName().replace(".yml", ""); + ImportRepoProviderConfig providerConfig = getProviderConfig(name); + Map map = new HashMap<>(); + map.put("name", name); + map.put("baseUrl", providerConfig.getBaseUrl()); + // 是否支持查询 + map.put("query", providerConfig.getRepoListParam().values().stream().anyMatch(s -> s.contains("${query}"))); + return map; + }).collect(Collectors.toMap(map -> (String) map.get("name"), map -> map)); + } + + @SneakyThrows + public ImportRepoProviderConfig getProviderConfig(String platform) { + String normalize = FileUtil.normalize(String.format("%s/%s.yml", IMPORT_REPO_PROVIDER_DIR, platform)); + ClassPathResource classPathResource = new ClassPathResource(normalize); + Assert.state(classPathResource.exists(), "配置文件不存在"); + try (InputStream inputStream = classPathResource.getInputStream()) { + return YamlUtil.load(inputStream, ImportRepoProviderConfig.class); + } + } + + private void setCommonParams(String platform, HttpRequest request, String token) { + ImportRepoProviderConfig provider = getProviderConfig(platform); + String callToken = provider.getAuthValue().replace("${token}", token); + if (provider.getAuthType() == 1) { + request.header(provider.getAuthKey(), callToken); + } else if (provider.getAuthType() == 2) { + request.form(provider.getAuthKey(), callToken); + } else if (provider.getAuthType() == 3) { + request.body(JSONUtil.createObj().set(provider.getAuthKey(), callToken).toString()); + } + + Map extraParams = provider.getExtraParams(); + if (CollUtil.isNotEmpty(extraParams)) { + if (provider.getExtraParamsType() == 1) { + extraParams.forEach(request::header); + } else if (provider.getExtraParamsType() == 2) { + extraParams.forEach(request::form); + } else if (provider.getExtraParamsType() == 3) { + request.body(JSONUtil.toJsonStr(extraParams)); + } + } + } + + public JSONObject getRepoList(String platform, String query, Page page, String token, String username, String baseUrl) { + baseUrl = StrUtil.blankToDefault(baseUrl, getProviderConfig(platform).getBaseUrl()); + ImportRepoProviderConfig provider = getProviderConfig(platform); + HttpRequest request = HttpUtil.createRequest(Method.valueOf(provider.getRepoListMethod()), baseUrl + provider.getRepoListUrl()); + setCommonParams(platform, request, token); + query = StrUtil.blankToDefault(query, ""); + String finalQuery = query; + provider.getRepoListParam().forEach((k, v) -> { + if ("${query}".equals(v)) { + v = v.replace("${query}", finalQuery); + } + if ("${page}".equals(v)) { + v = v.replace("${page}", String.valueOf(page.getPageNumber())); + } + if ("${pageSize}".equals(v)) { + v = v.replace("${pageSize}", String.valueOf(page.getPageSize())); + } + request.form(k, v); + }); + String body; + int total; + request.getUrl(); + log.debug(String.format("url: %s headers: %s form: %s", request.getUrl(), request.headers(), request.form())); + try (HttpResponse execute = request.execute()) { + body = execute.body(); + int status = execute.getStatus(); + Map> headers = execute.headers(); + String totalHeader = execute.header(provider.getRepoTotalHeader()); + int totalCount = page.getPageSize() * page.getPageNumber(); + if ("Link".equals(provider.getRepoTotalHeader()) && StrUtil.isNotBlank(totalHeader)) { + // github 特殊处理 + Pattern pattern = Pattern.compile("page=(\\d+)&per_page=(\\d+)>; rel=\"last\""); + Matcher matcher = pattern.matcher(totalHeader); + if (matcher.find()) { + int linkPage = Integer.parseInt(matcher.group(2)); + int linkPerPage = Integer.parseInt(matcher.group(1)); + total = linkPage * linkPerPage; + } else { + total = totalCount; + } + } else { + total = StrUtil.isNotBlank(totalHeader) ? Integer.parseInt(totalHeader) : totalCount; + } + log.debug(String.format("status: %s body: %s headers: %s", status, body, headers)); + Assert.state(execute.isOk(), String.format("请求失败: status: %s body: %s headers: %s", status, body, headers)); + } + JSONArray jsonArray = JSONUtil.parse(body).getByPath(provider.getRepoListPath(), JSONArray.class); + List data = jsonArray.stream().map(o -> { + JSONObject obj = (JSONObject) o; + JSONObject entries = new JSONObject(); + provider.getRepoConvertPath().forEach((k, v) -> { + if (StrUtil.startWith(v, "$ ")) { + String[] expression = v.split(" "); + String value = obj.getStr(expression[1]); + // 对比方式 + String compare = expression[2]; + // 对比值 + String compareValue = expression[3]; + switch (compare) { + case "==": + entries.set(k, value.equals(compareValue)); + break; + case "!=": + entries.set(k, !value.equals(compareValue)); + break; + default: + throw new IllegalStateException("表达式目前仅支持 == 和 != 比较"); + } + } else { + entries.set(k, obj.get(v)); + } + }); + entries.set("username", username); + return entries; + }).collect(Collectors.toList()); + return JSONUtil.createObj().set("data", data).set("total", total); + } + + public String getCurrentUserName(String platform, String token, String baseUrl) { + baseUrl = StrUtil.blankToDefault(baseUrl, getProviderConfig(platform).getBaseUrl()); + Assert.state(StrUtil.isNotBlank(baseUrl), String.format("请填写 %s 的 地址", platform)); + ImportRepoProviderConfig provider = getProviderConfig(platform); + HttpRequest request = HttpUtil.createRequest(Method.valueOf(provider.getCurrentUserMethod()), baseUrl + provider.getCurrentUserUrl()); + setCommonParams(platform, request, token); + String body; + log.debug(String.format("url: %s headers: %s form: %s", request.getUrl(), request.headers(), request.form())); + try (HttpResponse execute = request.execute()) { + body = execute.body(); + int status = execute.getStatus(); + Map> headers = execute.headers(); + log.debug(String.format("status: %s body: %s headers: %s", status, body, headers)); + Assert.state(execute.isOk(), String.format("请求失败: status: %s body: %s headers: %s", status, body, headers)); + } + return JSONUtil.parse(body).getByPath(provider.getUserNamePath(), String.class); + } + +} diff --git a/modules/server/src/main/resources/import-repo-provider/gitea.yml b/modules/server/src/main/resources/import-repo-provider/gitea.yml new file mode 100644 index 000000000..e4b4031b7 --- /dev/null +++ b/modules/server/src/main/resources/import-repo-provider/gitea.yml @@ -0,0 +1,20 @@ +baseUrl: https://try.gitea.io +authType: 1 +authKey: Authorization +authValue: 'Bearer ${token}' +currentUserMethod: GET +currentUserUrl: /api/v1/user +userNamePath: login +repoListMethod: GET +repoListUrl: /api/v1/user/repos +repoListParam: + page: '${page}' + limit: '${pageSize}' +repoListPath: '' +repoConvertPath: + name: name + full_name: full_name + description: description + url: clone_url + private: private +repoTotalHeader: X-Total-Count diff --git a/modules/server/src/main/resources/import-repo-provider/gitee.yml b/modules/server/src/main/resources/import-repo-provider/gitee.yml new file mode 100644 index 000000000..0228c6f4e --- /dev/null +++ b/modules/server/src/main/resources/import-repo-provider/gitee.yml @@ -0,0 +1,24 @@ +baseUrl: https://gitee.com +authType: 2 +authKey: access_token +authValue: '${token}' +currentUserMethod: GET +currentUserUrl: /api/v5/user +userNamePath: login +repoListMethod: GET +repoListUrl: /api/v5/user/repos +repoListParam: + type: all + sort: pushed + direction: desc + q: '${query}' + page: '${page}' + per_page: '${pageSize}' +repoListPath: '' +repoConvertPath: + name: name + full_name: full_name + description: description + url: html_url + private: private +repoTotalHeader: total_count diff --git a/modules/server/src/main/resources/import-repo-provider/github.yml b/modules/server/src/main/resources/import-repo-provider/github.yml new file mode 100644 index 000000000..2528f115d --- /dev/null +++ b/modules/server/src/main/resources/import-repo-provider/github.yml @@ -0,0 +1,27 @@ +baseUrl: https://api.github.com +authType: 1 +authKey: Authorization +authValue: 'Bearer ${token}' +extraParams: + Accept: application/vnd.github.v3+json + X-GitHub-Api-Version: 2022-11-28 +extraParamsType: 1 +currentUserMethod: GET +currentUserUrl: /user +userNamePath: login +repoListMethod: GET +repoListUrl: /user/repos +repoListParam: + type: all + sort: pushed + direction: desc + page: '${page}' + per_page: '${pageSize}' +repoListPath: '' +repoConvertPath: + name: name + full_name: full_name + description: description + url: clone_url + private: private +repoTotalHeader: Link diff --git a/modules/server/src/main/resources/import-repo-provider/gitlab.yml b/modules/server/src/main/resources/import-repo-provider/gitlab.yml new file mode 100644 index 000000000..534a330c7 --- /dev/null +++ b/modules/server/src/main/resources/import-repo-provider/gitlab.yml @@ -0,0 +1,23 @@ +baseUrl: https://gitlab.com +authType: 1 +authKey: Authorization +authValue: 'Bearer ${token}' +currentUserMethod: GET +currentUserUrl: /api/v4/user +userNamePath: username +repoListMethod: GET +repoListUrl: /api/v4/projects +repoListParam: + membership: true + order_by: updated_at + search: '${query}' + page: '${page}' + per_page: '${pageSize}' +repoListPath: '' +repoConvertPath: + name: name + full_name: path_with_namespace + description: description + url: http_url_to_repo + private: $ visibility != public +repoTotalHeader: X-Total diff --git a/modules/server/src/main/resources/import-repo-provider/gitlab_v3.yml b/modules/server/src/main/resources/import-repo-provider/gitlab_v3.yml new file mode 100644 index 000000000..3c1939223 --- /dev/null +++ b/modules/server/src/main/resources/import-repo-provider/gitlab_v3.yml @@ -0,0 +1,23 @@ +baseUrl: https://gitlab.com +authType: 1 +authKey: Authorization +authValue: 'Bearer ${token}' +currentUserMethod: GET +currentUserUrl: /api/v3/user +userNamePath: username +repoListMethod: GET +repoListUrl: /api/v3/projects +repoListParam: + membership: true + order_by: updated_at + search: '${query}' + page: '${page}' + per_page: '${pageSize}' +repoListPath: '' +repoConvertPath: + name: name + full_name: path_with_namespace + description: description + url: http_url_to_repo + private: $ visibility != public +repoTotalHeader: X-Total diff --git a/modules/server/src/main/resources/import-repo-provider/gogs.yml b/modules/server/src/main/resources/import-repo-provider/gogs.yml new file mode 100644 index 000000000..a69c50e5c --- /dev/null +++ b/modules/server/src/main/resources/import-repo-provider/gogs.yml @@ -0,0 +1,20 @@ +baseUrl: https://try.gogs.io +authType: 1 +authKey: Authorization +authValue: 'Bearer ${token}' +currentUserMethod: GET +currentUserUrl: /api/v1/user +userNamePath: login +repoListMethod: GET +repoListUrl: /api/v1/user/repos +repoListParam: + page: '${page}' + limit: '${pageSize}' +repoListPath: '' +repoConvertPath: + name: name + full_name: full_name + description: description + url: clone_url + private: private +repoTotalHeader: X-Total-Count diff --git a/web-vue/src/api/repository.js b/web-vue/src/api/repository.js index 2b086e8ea..dffb2c60c 100644 --- a/web-vue/src/api/repository.js +++ b/web-vue/src/api/repository.js @@ -92,6 +92,13 @@ export function authorizeRepos(param) { }); } +export function providerInfo() { + return axios({ + url: "/build/repository/provider_info", + method: "get", + }); +} + export function sortItem(params) { return axios({ url: "/build/repository/sort-item", diff --git a/web-vue/src/pages/repository/list.vue b/web-vue/src/pages/repository/list.vue index 6bfee88cd..483b7f725 100644 --- a/web-vue/src/pages/repository/list.vue +++ b/web-vue/src/pages/repository/list.vue @@ -168,8 +168,8 @@ - 全局 - 当前工作空间 + 全局 + 当前工作空间 安全设置-->私人令牌 中获取", @@ -343,13 +346,8 @@ export default { gitlab: "在 preferences-->Access Tokens 中获取", gitea: "在 设置 --> 应用 --> 生成令牌", gogs: "在 设置 --> 应用 --> 生成令牌", - other: "请输入私人令牌", - }, - importTypeAddressHelp: { - gitlab: "请输入 GitLab 的地址,支持自建 GitLab,不需要输入协议,如:gitlab.com、gitlab.jpom.io、10.1.2.3、10.1.2.3:8888 等", - gitea: "请输入 gitea 的地址,不需要输入协议,如:10.1.2.3、10.1.2.3:3000 等", - gogs: "请输入 gogs 的地址,不需要输入协议,如:10.1.2.3、10.1.2.3:3000 等", - }, + other: "请输入私人令牌" + } }; }, computed: { @@ -359,7 +357,7 @@ export default { }, reposPagination() { return COMPUTED_PAGINATION(this.giteeImportForm, this.PAGE_DEFAULT_SIZW_OPTIONS); - }, + } }, watch: {}, created() { @@ -367,7 +365,7 @@ export default { }, methods: { // 加载数据 - loadData(pointerEvent) { + async loadData(pointerEvent) { this.listQuery.page = pointerEvent?.altKey || pointerEvent?.ctrlKey ? 1 : this.listQuery.page; this.loading = true; getRepositoryList(this.listQuery).then((res) => { @@ -377,6 +375,13 @@ export default { } this.loading = false; }); + const response = await providerInfo(); + if (response.code === 200) { + this.providerData = response.data; + } + }, + importChange(value) { + this.giteeImportForm.address = this.providerData[value].baseUrl; }, // // 筛选 // handleFilter() { @@ -386,7 +391,7 @@ export default { handleAdd() { this.temp = { repoType: 0, - protocol: 0, + protocol: 0 }; this.editVisible = true; }, @@ -420,12 +425,12 @@ export default { userName: record.username, password: this.giteeImportForm.token, name: record.name, - gitUrl: record.url, + gitUrl: record.url }).then((res) => { if (res.code === 200) { // 成功 this.$notification.success({ - message: res.msg, + message: res.msg }); record.exists = true; this.loadData(); @@ -453,7 +458,7 @@ export default { if (res.code === 200) { // 成功 this.$notification.success({ - message: res.msg, + message: res.msg }); this.editVisible = false; this.loadData(); @@ -471,19 +476,19 @@ export default { cancelText: "取消", onOk: () => { const params = { - id: record.id, + id: record.id //isRealDel: this.isSystem, }; // 删除 deleteRepository(params).then((res) => { if (res.code === 200) { this.$notification.success({ - message: res.msg, + message: res.msg }); this.loadData(); } }); - }, + } }); }, @@ -499,12 +504,12 @@ export default { restHideField(record.id).then((res) => { if (res.code === 200) { this.$notification.success({ - message: res.msg, + message: res.msg }); this.loadData(); } }); - }, + } }); }, // 分页、排序、筛选变化时触发 @@ -518,7 +523,7 @@ export default { const msgData = { top: "确定要将此数据置顶吗?", up: "确定要将此数上移吗?", - down: "确定要将此数据下移吗?下移操作可能因为列表后续数据没有排序值操作无效!", + down: "确定要将此数据下移吗?下移操作可能因为列表后续数据没有排序值操作无效!" }; let msg = msgData[method] || "确定要操作吗?"; if (!record.sortValue) { @@ -536,21 +541,21 @@ export default { sortItem({ id: record.id, method: method, - compareId: compareId, + compareId: compareId }).then((res) => { if (res.code == 200) { this.$notification.success({ - message: res.msg, + message: res.msg }); this.loadData(); return false; } }); - }, + } }); - }, - }, + } + } };