diff --git a/.gitignore b/.gitignore index 6b4fd25c..19dfa07f 100644 --- a/.gitignore +++ b/.gitignore @@ -74,6 +74,7 @@ vendor/bundle/ /log /public/admin /mysql_data +/public/repo/ .generators @@ -85,3 +86,4 @@ redis_data/ Dockerfile dump.rdb .tags* +ceshi_user.xlsx \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7ef5879a..d3f9c193 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -775,7 +775,8 @@ class ApplicationController < ActionController::Base end def convert_image! - @image = params[:image] || user_params[:image] + @image = params[:image] + @image = @image.nil? && params[:user].present? ? params[:user][:image] : @image return unless @image.present? max_size = EduSetting.get('upload_avatar_max_size') || 2 * 1024 * 1024 # 2M if @image.class == ActionDispatch::Http::UploadedFile diff --git a/app/controllers/compare_controller.rb b/app/controllers/compare_controller.rb index b36ffde3..bc81da56 100644 --- a/app/controllers/compare_controller.rb +++ b/app/controllers/compare_controller.rb @@ -48,6 +48,6 @@ class CompareController < ApplicationController end def gitea_compare(base, head) - Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, base, head) + Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, base, head, current_user.gitea_token) end end diff --git a/app/controllers/issue_tags_controller.rb b/app/controllers/issue_tags_controller.rb index d21d8ed4..f3691f3d 100644 --- a/app/controllers/issue_tags_controller.rb +++ b/app/controllers/issue_tags_controller.rb @@ -7,7 +7,7 @@ class IssueTagsController < ApplicationController def index - issue_tags = @project.issue_tags.order("#{order_name} #{order_type}") + issue_tags = @project.issue_tags.reorder("#{order_name} #{order_type}") @user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user)) @page = params[:page] || 1 @limit = params[:limit] || 15 diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 3af2f653..ca74247d 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -219,7 +219,7 @@ class IssuesController < ApplicationController change_type = change_token > 0 ? "add" : "minus" post_to_chain(change_type, change_token.abs, current_user.try(:login)) end - @issue.create_journal_detail(change_files, issue_files, issue_file_ids, current_user&.id) + @issue.create_journal_detail(change_files, issue_files, issue_file_ids, current_user&.id) if @issue.previous_changes.present? normal_status(0, "更新成功") else normal_status(-1, "更新失败") @@ -302,9 +302,11 @@ class IssuesController < ApplicationController # update_hash = params[:issue] issue_ids = params[:ids] if issue_ids.present? + issues = Issue.where(id: issue_ids) if update_hash.blank? normal_status(-1, "请选择批量更新内容") - elsif Issue.where(id: issue_ids)&.update(update_hash) + elsif issues&.update(update_hash) + issues.map{|i| i.create_journal_detail(false, [], [], current_user&.id) if i.previous_changes.present?} normal_status(0, "批量更新成功") else normal_status(-1, "批量更新失败") diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb index 94e6c865..a337bd23 100644 --- a/app/controllers/members_controller.rb +++ b/app/controllers/members_controller.rb @@ -56,7 +56,7 @@ class MembersController < ApplicationController end def member_exists? - @project.members.exists?(params[:user_id]) + @project.members.exists?(user_id: params[:user_id]) end def operate! @@ -64,10 +64,10 @@ class MembersController < ApplicationController end def check_member_exists! - return render_result(1, "user_id为#{params[:user_id]}的用户已经是项目成员") if member_exists? + return render_error("user_id为#{params[:user_id]}的用户已经是项目成员") if member_exists? end def check_member_not_exists! - return render_result(1, "user_id为#{params[:user_id]}的用户还不是项目成员") unless @project.member?(params[:user_id]) + return render_error("user_id为#{params[:user_id]}的用户还不是项目成员") unless member_exists? end end diff --git a/app/controllers/organizations/team_users_controller.rb b/app/controllers/organizations/team_users_controller.rb index 752e1f0e..0c27e070 100644 --- a/app/controllers/organizations/team_users_controller.rb +++ b/app/controllers/organizations/team_users_controller.rb @@ -29,6 +29,11 @@ class Organizations::TeamUsersController < Organizations::BaseController ActiveRecord::Base.transaction do @team_user.destroy! Gitea::Organization::TeamUser::DeleteService.call(@organization.gitea_token, @team.gtid, @operate_user.login) + org_team_users = @organization.team_users.where(user_id: @operate_user.id) + unless org_team_users.present? + @organization.organization_users.find_by(user_id: @operate_user.id).destroy! + Gitea::Organization::OrganizationUser::DeleteService.call(@organization.gitea_token, @organization.login, @operate_user.login) + end render_ok end rescue Exception => e @@ -43,6 +48,11 @@ class Organizations::TeamUsersController < Organizations::BaseController ActiveRecord::Base.transaction do @team_user.destroy! Gitea::Organization::TeamUser::DeleteService.call(@organization.gitea_token, @team.gtid, current_user.login) + org_team_users = @organization.team_users.where(user_id: current_user.id) + unless org_team_users.present? + @organization.organization_users.find_by(user_id: current_user.id).destroy! + Gitea::Organization::OrganizationUser::DeleteService.call(@organization.gitea_token, @organization.login, current_user.login) + end render_ok end rescue Exception => e diff --git a/app/controllers/projects/base_controller.rb b/app/controllers/projects/base_controller.rb index 9811a213..240bc91f 100644 --- a/app/controllers/projects/base_controller.rb +++ b/app/controllers/projects/base_controller.rb @@ -4,4 +4,7 @@ class Projects::BaseController < ApplicationController before_action :load_project before_action :load_repository + def require_manager! + return render_forbidden('你没有权限操作') unless current_user.admin? || @project.manager?(current_user) + end end diff --git a/app/controllers/projects/teams_controller.rb b/app/controllers/projects/teams_controller.rb index b6ea3218..3ffc7800 100644 --- a/app/controllers/projects/teams_controller.rb +++ b/app/controllers/projects/teams_controller.rb @@ -14,7 +14,7 @@ class Projects::TeamsController < Projects::BaseController def create ActiveRecord::Base.transaction do @team_project = TeamProject.build(@owner.id, @operate_team.id, @project.id) - Gitea::Organization::TeamProject::CreateService.call(@owner.gitea_token, @operate_team.gtid, @owner.login, @project.identifier) + Gitea::Organization::TeamProject::CreateService.call(current_user.gitea_token, @operate_team.gtid, @owner.login, @project.identifier) render_ok end rescue Exception => e @@ -25,7 +25,7 @@ class Projects::TeamsController < Projects::BaseController def destroy ActiveRecord::Base.transaction do @team_project.destroy! - Gitea::Organization::TeamProject::DeleteService.call(@owner.gitea_token, @operate_team.gtid, @owner.login, @project.identifier) + Gitea::Organization::TeamProject::DeleteService.call(current_user.gitea_token, @operate_team.gtid, @owner.login, @project.identifier) render_ok end rescue Exception => e diff --git a/app/controllers/projects/webhooks_controller.rb b/app/controllers/projects/webhooks_controller.rb new file mode 100644 index 00000000..9f36da20 --- /dev/null +++ b/app/controllers/projects/webhooks_controller.rb @@ -0,0 +1,116 @@ +class Projects::WebhooksController < Projects::BaseController + before_action :require_manager! + before_action :find_webhook, only:[:edit, :update, :destroy, :tasks, :test] + + def index + @webhooks = @project.webhooks + @webhooks = kaminari_paginate(@webhooks) + end + + def create + ActiveRecord::Base.transaction do + return render_error("webhooks数量已到上限!请删除暂不使用的webhooks以进行添加操作") if @project.webhooks.size > 19 + return render_error("参数错误.") unless webhook_params.present? + form = Projects::Webhooks::CreateForm.new(webhook_params) + return render json: {status: -1, message: form.errors} unless form.validate! + response = Gitea::Repository::Webhooks::CreateService.new(operating_token, @project&.owner&.login, @project&.identifier, gitea_webhooks_params).call + if response[0] == 201 + @webhook = response[2] + else + render_error("创建失败.") + end + end + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + def edit + + end + + def update + return render_error("参数错误.") unless webhook_params.present? + form = Projects::Webhooks::CreateForm.new(webhook_params) + return render json: {status: -1, message: form.errors} unless form.validate! + response = Gitea::Repository::Webhooks::UpdateService.call(operating_token, @project&.owner&.login, @project&.identifier, @webhook.id, gitea_webhooks_params) + if response[0] == 200 + @webhook = response[2] + render_ok + else + render_error("更新失败.") + end + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + def destroy + response = Gitea::Repository::Webhooks::DeleteService.call(operating_token, @project&.owner&.login, @project&.identifier, @webhook.id) + if response[0] == 204 + @webhook = response[2] + render_ok + else + render_error("删除失败.") + end + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + def tasks + @tasks = @webhook.tasks.where(is_delivered: true).order("delivered desc") + @tasks = kaminari_paginate(@tasks) + end + + def test + ActiveRecord::Base.transaction do + response = Gitea::Repository::Webhooks::TestService.call(operating_token, @project&.owner&.login, @project&.identifier, @webhook.id) + if response[0] == 204 + render_ok + else + render_error("测试推送失败.") + end + end + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + private + def find_webhook + @webhook = @project.webhooks.find_by_id(params[:id]) + return render_not_found if @webhook.nil? + end + + def webhook_params + params.require(:webhook).permit(:url, :type, :http_method, :content_type, :secret, :active, :branch_filter, events: []) + end + + def webhook_type + webhook_params.fetch(:type, "gitea") + end + + def webhook_branch_filter + webhook_params.fetch(:branch_filter, "*") + end + + def gitea_webhooks_params + { + active: webhook_params[:active], + branch_filter: webhook_branch_filter, + config: { + content_type: webhook_params[:content_type], + url: webhook_params[:url], + http_method: webhook_params[:http_method], + secret: webhook_params[:secret] + }, + events: webhook_params[:events], + type: webhook_type, + } + end + + def operating_token + @project.member?(current_user) ? current_user.gitea_token : @project&.owner&.gitea_token + end +end \ No newline at end of file diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 74a463b9..d23cfdad 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -16,6 +16,7 @@ class ProjectsController < ApplicationController menu.append(menu_hash_by_name("code")) if @project.has_menu_permission("code") menu.append(menu_hash_by_name("issues")) if @project.has_menu_permission("issues") menu.append(menu_hash_by_name("pulls")) if @project.has_menu_permission("pulls") + menu.append(menu_hash_by_name("wiki")) if @project.has_menu_permission("wiki") menu.append(menu_hash_by_name("devops")) if @project.has_menu_permission("devops") menu.append(menu_hash_by_name("versions")) if @project.has_menu_permission("versions") menu.append(menu_hash_by_name("resources")) if @project.has_menu_permission("resources") @@ -117,8 +118,14 @@ class ProjectsController < ApplicationController ActiveRecord::Base.transaction do # TODO: # 临时特殊处理修改website、lesson_url操作方法 - if project_params.has_key?("website") + if project_params.has_key?("website") @project.update(project_params) + elsif project_params.has_key?("default_branch") + @project.update(project_params) + gitea_params = { + default_branch: @project.default_branch + } + Gitea::Repository::UpdateService.call(@owner, @project.identifier, gitea_params) else validate_params = project_params.slice(:name, :description, :project_category_id, :project_language_id, :private) @@ -222,7 +229,7 @@ class ProjectsController < ApplicationController private def project_params - params.permit(:user_id, :name, :description, :repository_name, :website, :lesson_url, + params.permit(:user_id, :name, :description, :repository_name, :website, :lesson_url, :default_branch, :project_category_id, :project_language_id, :license_id, :ignore_id, :private) end diff --git a/app/controllers/public_keys_controller.rb b/app/controllers/public_keys_controller.rb new file mode 100644 index 00000000..327e719c --- /dev/null +++ b/app/controllers/public_keys_controller.rb @@ -0,0 +1,64 @@ +class PublicKeysController < ApplicationController + before_action :require_login + before_action :find_public_key, only: [:destroy] + + def index + @public_keys = current_user.public_keys + @public_keys = kaminari_paginate(@public_keys) + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + def create + return render_error("参数错误") if public_key_params.blank? + return render_ok({status: 10002, message: "请输入密钥"}) if public_key_params[:key].blank? + return render_ok({status: 10001, message: "请输入标题"}) if public_key_params[:title].blank? + @gitea_response = Gitea::User::Keys::CreateService.call(current_user.gitea_token, public_key_params) + if @gitea_response[0] == 201 + @public_key = @gitea_response[2] + else + return render_error("创建ssh key失败") if @gitea_response[2].blank? + return render_ok({status: 10002, message: "密钥格式不正确"}) if @gitea_response[2]["message"].starts_with?("Invalid key content") + exist_public_key = Gitea::PublicKey.find_by(content: public_key_params[:key]) + return render_ok({status: 10002, message: "密钥已被占用"}) if @gitea_response[2]["message"].starts_with?("Key content has been used as non-deploy key") && exist_public_key.present? && exist_public_key&.owner_id != current_user.gitea_uid + return render_ok({status: 10002, message: "密钥已存在,请勿重复添加"}) if @gitea_response[2]["message"].starts_with?("Key content has been used as non-deploy key") + @public_key = nil + end + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + def destroy + return render_not_found unless @public_key.present? + result = Gitea::User::Keys::DeleteService.call(current_user.gitea_token, @public_key.id) + if result[0] == 204 + render_ok + else + render_error + end + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + private + + def page + params[:page].to_i.zero? ? 1 : params[:page].to_i + end + + def limit + limit = params[:limit] || params[:per_page] + limit = (limit.to_i.zero? || limit.to_i > 15) ? 15 : limit.to_i + end + + def public_key_params + params.require(:public_key).permit(:key, :title) + end + + def find_public_key + @public_key = current_user.public_keys.find_by_id(params[:id]) + end +end \ No newline at end of file diff --git a/app/controllers/pull_requests_controller.rb b/app/controllers/pull_requests_controller.rb index 4270cb38..35051222 100644 --- a/app/controllers/pull_requests_controller.rb +++ b/app/controllers/pull_requests_controller.rb @@ -19,6 +19,7 @@ class PullRequestsController < ApplicationController @close_issues = @filter_issues.joins(:pull_request).where(pull_requests: {status: PullRequest::CLOSED}) @merged_issues = @filter_issues.joins(:pull_request).where(pull_requests: {status: PullRequest::MERGED}) @user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user)) + @user_admin_or_developer = current_user.present? && (current_user.admin || @project.all_developers.include?(current_user)) scopes = Issues::ListQueryService.call(issues,params.delete_if{|k,v| v.blank?}, "PullRequest") @issues_size = scopes.size diff --git a/app/controllers/users/base_controller.rb b/app/controllers/users/base_controller.rb index 128dc539..21442779 100644 --- a/app/controllers/users/base_controller.rb +++ b/app/controllers/users/base_controller.rb @@ -5,7 +5,7 @@ class Users::BaseController < ApplicationController helper_method :observed_logged_user?, :observed_user def observed_user - @_observed_user ||= (User.find_by_id(params[:user_id]) || User.find_by_login(params[:user_id])) + @_observed_user ||= (User.find_by_login(params[:user_id]) || User.find_by_id(params[:user_id])) end def observed_logged_user? diff --git a/app/docs/slate/source/api.html.md b/app/docs/slate/source/api.html.md index a08823a4..a846317c 100644 --- a/app/docs/slate/source/api.html.md +++ b/app/docs/slate/source/api.html.md @@ -12,6 +12,7 @@ toc_footers: includes: - licenses - gitignores + - public_keys - users - projects - repositories diff --git a/app/docs/slate/source/includes/_public_keys.md b/app/docs/slate/source/includes/_public_keys.md new file mode 100644 index 00000000..60d73c01 --- /dev/null +++ b/app/docs/slate/source/includes/_public_keys.md @@ -0,0 +1,158 @@ + +# PublicKeys + +## public_keys列表 +获取public_keys列表,支持分页 + +> 示例: + +```shell +curl -X GET \ +http://localhost:3000/api/public_keys.json +``` + +```javascript +await octokit.request('GET /api/public_keys.json') +``` + +### HTTP 请求 +`GET api/public_keys.json` + +### 请求参数 +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +page |否| 1 | int | 页码 | +limit |否| 15 | int | 每页数量 | + +### 返回字段说明 +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +total_count |int |总数 | +public_keys.id |int |ID| +public_keys.name |string|密钥标题| +public_keys.content |string|密钥内容| +public_keys.fingerprint |string|密钥标识| +public_keys.created_time |string|密钥创建时间| + + +> 返回的JSON示例: + +```json +{ + "total_count": 1, + "public_keys": [ + { + "id": 16, + "name": "xxx", + "content": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDe5ETOTB5PcmcYJkIhfF7+mxmJQDCLg7/LnMoKHpKoo/jYUnFU9OjfsxVo3FTNUvh2475WXMAur5KsFoNKjK9+JHxvoXyJKmyVPWgXU/NRxQyaWPnPLPK8qPRF5ksJE6feBOqtsdxsvBiHs2r1NX/U26Ecnpr6avudD0cmyrEfbYMWbupLrhsd39dswPT73f3W5jc7B9Y47Ioiv8UOju3ABt1+kpuAjaaVC6VtUQoEFiZb1y33yBnyePya7dvFyApyD4ILyyIG2rtZWK7l53YFnwZDuFsTWjEEEQD0U4FBSFdH5wtwx0WQLMSNyTtaFBSG0kJ+uiQQIrxlvikcm63df7zbC3/rWLPsKgW122Zt966dcpFqiCiJNDKZPPw3qpg8TBL6X+qIZ+FxVEk/16/zScpyEfoxQp0GvgxI7hPLErmfkC5tMsib8MAXYBNyvJXna0vg/wOaNNIaI4SAH9Ksh3f/TtalYVjp6WxIwVBfnbq51WnmlnEXePtX6XjAGL+GbF2VQ1nv/IzrY09tNbTV6wQsrSIP3VDzYQxdJ1rdsVNMoJB0H2Pu0NdcSz53Wx45N+myD0QnE05ss+zDp5StY90OYsx2aCo6qAA8Qn2jUjdta7MQWwkPfKrta4tTQ0XbWMjx4/E1+l3J5liwZkl2XOGOwhfXdRsBjaEziZ18kQ== yystopf@163.com", + "fingerprint": "SHA256:cU8AK/+roqUUyiaYXIdS2Nj4+Rb2p6rqWSeRDc+aqKM", + "created_unix": 1626246596, + "created_time": "2021/07/14 15:09" + } + ] +} +``` + + +## 创建public_key +创建public_key + +> 示例: + +```shell +curl -X POST \ +http://localhost:3000/api/public_keys.json +``` + +```javascript +await octokit.request('POST /api/public_keys.json') +``` + +### HTTP 请求 +`POST api/public_keys.json` + +### 请求参数 +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +key |是 | 否 | string | 密钥 | +title |是 | 否 | string | 密钥标题 | + +> 请求的JSON示例: +```json +{ + "public_key": { + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDe5ETOTB5PcmcYJkIhfF7+mxmJQDCLg7/LnMoKHpKoo/jYUnFU9OjfsxVo3FTNUvh2475WXMAur5KsFoNKjK9+JHxvoXyJKmyVPWgXU/NRxQyaWPnPLPK8qPRF5ksJE6feBOqtsdxsvBiHs2r1NX/U26Ecnpr6avudD0cmyrEfbYMWbupLrhsd39dswPT73f3W5jc7B9Y47Ioiv8UOju3ABt1+kpuAjaaVC6VtUQoEFiZb1y33yBnyePya7dvFyApyD4ILyyIG2rtZWK7l53YFnwZDuFsTWjEEEQD0U4FBSFdH5wtwx0WQLMSNyTtaFBSG0kJ+uiQQIrxlvikcm63df7zbC3/rWLPsKgW122Zt966dcpFqiCiJNDKZPPw3qpg8TBL6X+qIZ+FxVEk/16/zScpyEfoxQp0GvgxI7hPLErmfkC5tMsib8MAXYBNyvJXna0vg/wOaNNIaI4SAH9Ksh3f/TtalYVjp6WxIwVBfnbq51WnmlnEXePtX6XjAGL+GbF2VQ1nv/IzrY09tNbTV6wQsrSIP3VDzYQxdJ1rdsVNMoJB0H2Pu0NdcSz53Wx45N+myD0QnE05ss+zDp5StY90OYsx2aCo6qAA8Qn2jUjdta7MQWwkPfKrta4tTQ0XbWMjx4/E1+l3J5liwZkl2XOGOwhfXdRsBjaEziZ18kQ== yystopf@163.com", + "title": "xxx" + } +} +``` + +### 返回字段说明 +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +total_count |int |总数 | +id |int |ID| +name |string|密钥标题| +content |string|密钥内容| +fingerprint |string|密钥标识| +created_time |string|密钥创建时间| + + +> 返回的JSON示例: + +```json +{ + "id": 17, + "name": "xxx", + "content": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDe5ETOTB5PcmcYJkIhfF7+mxmJQDCLg7/LnMoKHpKoo/jYUnFU9OjfsxVo3FTNUvh2475WXMAur5KsFoNKjK9+JHxvoXyJKmyVPWgXU/NRxQyaWPnPLPK8qPRF5ksJE6feBOqtsdxsvBiHs2r1NX/U26Ecnpr6avudD0cmyrEfbYMWbupLrhsd39dswPT73f3W5jc7B9Y47Ioiv8UOju3ABt1+kpuAjaaVC6VtUQoEFiZb1y33yBnyePya7dvFyApyD4ILyyIG2rtZWK7l53YFnwZDuFsTWjEEEQD0U4FBSFdH5wtwx0WQLMSNyTtaFBSG0kJ+uiQQIrxlvikcm63df7zbC3/rWLPsKgW122Zt966dcpFqiCiJNDKZPPw3qpg8TBL6X+qIZ+FxVEk/16/zScpyEfoxQp0GvgxI7hPLErmfkC5tMsib8MAXYBNyvJXna0vg/wOaNNIaI4SAH9Ksh3f/TtalYVjp6WxIwVBfnbq51WnmlnEXePtX6XjAGL+GbF2VQ1nv/IzrY09tNbTV6wQsrSIP3VDzYQxdJ1rdsVNMoJB0H2Pu0NdcSz53Wx45N+myD0QnE05ss+zDp5StY90OYsx2aCo6qAA8Qn2jUjdta7MQWwkPfKrta4tTQ0XbWMjx4/E1+l3J5liwZkl2XOGOwhfXdRsBjaEziZ18kQ== yystopf@163.com", + "fingerprint": "SHA256:cU8AK/+roqUUyiaYXIdS2Nj4+Rb2p6rqWSeRDc+aqKM", + "created_time": "2021/07/14 15:26" +} +``` + + + +## 删除public_key +删除public_key + +> 示例: + +```shell +curl -X DELETE \ +http://localhost:3000/api/public_keys/:id.json +``` + +```javascript +await octokit.request('DELETE /api/public_keys/:id.json') +``` + +### HTTP 请求 +`DELETE api/public_keys/:id.json` + +### 请求参数 +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +id |是 | 否 | int | 密钥ID | + + +> 返回的JSON示例: + +```json +{ + "status": 0, + "message": "success" +} +``` + + diff --git a/app/docs/slate/source/includes/_repositories.md b/app/docs/slate/source/includes/_repositories.md index 40d3ba2f..1552655e 100644 --- a/app/docs/slate/source/includes/_repositories.md +++ b/app/docs/slate/source/includes/_repositories.md @@ -867,3 +867,674 @@ await octokit.request('GET /api/jasder/jasder_test/sub_entries.json') + +## 获取仓库webhooks列表 +获取仓库webhooks列表 + +> 示例: + +```shell +curl -X GET \ +http://localhost:3000/api/yystopf/ceshi/webhooks.json +``` + +```javascript +await octokit.request('GET /api/yystopf/ceshi/webhooks.json') +``` + +### HTTP 请求 +`GET /api/:owner/:repo/webhooks.json` + +### 请求参数: +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|owner |是| |string |用户登录名 | +|repo |是| |string |项目标识identifier | + + +### 返回字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|id |int |id | +|url |string|地址| +|http_method |string|请求方式| +|is_active |bool |是否激活| +|type |string|类型| +|last_status |string|最后一次推送的状态| +|create_time |string|创建时间| + + +> 返回的JSON示例: + +```json +{ + "total_count": 4, + "webhooks": [ + { + "id": 2, + "url": "https://oapi.dingtalk.com/robot/send?access_token=7e1e19d0eddb6a5e33c5c2c4e66f4c88f9437184b9ed2c2653194c6374c7d513", + "http_method": "", + "is_active": true, + "type": "dingtalk", + "last_status": "succeed", + "create_time": "2021-07-12 10:50:07" + }, + { + "id": 3, + "url": "http://localhost:3000", + "http_method": "GET", + "is_active": true, + "type": "gitea", + "last_status": "succeed", + "create_time": "2021-07-26 10:03:45" + }, + { + "id": 4, + "url": "http://localhost:10081", + "http_method": "POST", + "is_active": true, + "type": "gitea", + "last_status": "waiting", + "create_time": "2021-07-26 16:56:53" + }, + { + "id": 5, + "url": "http://localhost:3001", + "http_method": "POST", + "is_active": true, + "type": "gitea", + "last_status": "fail", + "create_time": "2021-07-26 16:58:23" + } + ] +} +``` + + +## 获取仓库单个webhook +获取仓库单个webhook + +> 示例: + +```shell +curl -X GET \ +http://localhost:3000/api/yystopf/ceshi/webhooks/3/edit.json +``` + +```javascript +await octokit.request('GET /api/yystopf/ceshi/webhooks/3/edit.json') +``` + +### HTTP 请求 +`GET /api/:owner/:repo/webhooks/:id/edit.json` + +### 请求参数: +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|owner |是| |string |用户登录名 | +|repo |是| |string |项目标识identifier | +|id |是||integer|webhook ID| + + +### 返回字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|id |int |id | +|url |string|地址| +|content_type |string|POST Content Type| +|http_method |string|请求方式| +|secret| |string|密钥| +|is_active |bool |是否激活| +|type |string|类型| +|last_status |string|最后一次推送的状态, waiting 等待,fail 失败,succeed 成功| +|branch_filter |string|分支过滤| +|events |string|触发条件| +|create_time |string|创建时间| + + +参数| 含义| +--------- | ------- | ------- | +|create|创建分支或标签| +|delete|分支或标签删除| +|fork|仓库被fork| +|push|git仓库推送| +|issue|易修已打开、已关闭、已重新打开或编辑| +|issue_assign|易修被指派| +|issue_label|易修标签被更新或删除| +|issue_milestone|易修被收入里程碑| +|issue_comment|易修评论| +|pull_request|合并请求| +|pull_request_assign|合并请求被指派| +|pull_request_label|合并请求被贴上标签| +|pull_request_milestone|合并请求被记录于里程碑中| +|pull_request_comment|合并请求被评论| +|pull_request_review_approved|合并请求被批准| +|pull_request_review_rejected|合并请求被拒绝| +|pull_request_review_comment|合并请求被提出审查意见| +|pull_request_sync|合并请求被同步| +|repository|创建或删除仓库| +|release|版本发布| + + +> 返回的JSON示例: + +```json +{ + "id": 3, + "http_method": "GET", + "content_type": "form", + "url": "http://localhost:3000", + "secret": "123456", + "last_status": "succeed", + "is_active": true, + "type": "gitea", + "create_time": "2021-07-26 10:03:45", + "branch_filter": "*", + "events": [ + "create", + "delete", + "fork", + "issues", + "issue_assign", + "issue_label", + "issue_milestone", + "issue_comment", + "push", + "pull_request", + "pull_request_assign", + "pull_request_label", + "pull_request_milestone", + "pull_request_comment", + "pull_request_review", + "pull_request_sync", + "repository", + "release" + ] +} +``` + + +## 添加仓库webhook +添加仓库webhook + +> 示例: + +```shell +curl -X POST \ +http://localhost:3000/api/yystopf/ceshi/webhooks.json +``` + +```javascript +await octokit.request('POST /api/yystopf/ceshi/webhooks.json') +``` + +### HTTP 请求 +`POST /api/:owner/:repo/webhooks.json` + +### 请求参数: +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|owner |是| | string |用户登录名 | +|repo |是| | string |项目标识identifier | +|webhook.url |是| | string |目标url | +|webhook.type |否| | string |类型| +|webhook.http_method |是| | string | http方法, POST和GET | +|webhook.content_type |是| | string | POST Content Type | +|webhook.secret |否| | string |密钥文本| +|webhook.active |是| | bool | 是否激活| +|webhook.branch_filter|否| |string|分支过滤| +|webhook.events |否| |array|触发事件| + +触发事件字段说明 + +参数| 含义| +--------- | ------- | ------- | +|create|创建分支或标签| +|delete|分支或标签删除| +|fork|仓库被fork| +|push|git仓库推送| +|issue|易修已打开、已关闭、已重新打开或编辑| +|issue_assign|易修被指派| +|issue_label|易修标签被更新或删除| +|issue_milestone|易修被收入里程碑| +|issue_comment|易修评论| +|pull_request|合并请求| +|pull_request_assign|合并请求被指派| +|pull_request_label|合并请求被贴上标签| +|pull_request_milestone|合并请求被记录于里程碑中| +|pull_request_comment|合并请求被评论| +|pull_request_review_approved|合并请求被批准| +|pull_request_review_rejected|合并请求被拒绝| +|pull_request_review_comment|合并请求被提出审查意见| +|pull_request_sync|合并请求被同步| +|repository|创建或删除仓库| +|release|版本发布| + + +> 请求的JSON示例: + +```json +{ + "active": true, + "content_type": "json", + "http_method": "GET", + "secret": "123456", + "url": "http://localhost:10000", + "branch_filter": "*", + "events": ["push"] +} +``` + +### 返回字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|id |int |id | +|url |string|地址| +|content_type |string|POST Content Type| +|is_active |bool |是否激活| +|type |string|类型| +|events | array|触发事件 | +|create_time |string|创建时间| + + +> 返回的JSON示例: + +```json +{ + "id": 18, + "type": "gitea", + "content_type": "json", + "url": "http://localhost:10000", + "events": [ + "push" + ], + "active": true, + "create_time": "2021-07-26 18:53:43" +} +``` + + +## 更新仓库webhook +更新仓库webhook + +> 示例: + +```shell +curl -X PATCH \ +http://localhost:3000/api/yystopf/ceshi/webhooks/7.json +``` + +```javascript +await octokit.request('PATCH /api/yystopf/ceshi/webhooks/7.json') +``` + +### HTTP 请求 +`PATCH /api/:owner/:repo/webhooks/:id.json` + +### 请求参数: +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|owner |是| | string |用户登录名 | +|repo |是| | string |项目标识identifier | +|id |是| | string |webhook id | +|webhook.url |是| | string |目标url | +|webhook.type |否| | string |类型| +|webhook.http_method |是| | string | http方法, POST和GET | +|webhook.content_type |是| | string | POST Content Type | +|webhook.secret |否| | string |密钥文本| +|webhook.active |是| | bool | 是否激活| +|webhook.branch_filter|否| |string|分支过滤| +|webhook.events |否| |array|触发事件| + +触发事件字段说明 + +参数| 含义| +--------- | ------- | ------- | +|create|创建分支或标签| +|delete|分支或标签删除| +|fork|仓库被fork| +|push|git仓库推送| +|issue|易修已打开、已关闭、已重新打开或编辑| +|issue_assign|易修被指派| +|issue_label|易修标签被更新或删除| +|issue_milestone|易修被收入里程碑| +|issue_comment|易修评论| +|pull_request|合并请求| +|pull_request_assign|合并请求被指派| +|pull_request_label|合并请求被贴上标签| +|pull_request_milestone|合并请求被记录于里程碑中| +|pull_request_comment|合并请求被评论| +|pull_request_review_approved|合并请求被批准| +|pull_request_review_rejected|合并请求被拒绝| +|pull_request_review_comment|合并请求被提出审查意见| +|pull_request_sync|合并请求被同步| +|repository|创建或删除仓库| +|release|版本发布| + + +> 请求的JSON示例: + +```json +{ + "active": true, + "content_type": "json", + "http_method": "GET", + "secret": "123456", + "url": "http://localhost:10000", + "branch_filter": "*", + "events": ["push"] +} +``` + +### 返回字段说明: + +> 返回的JSON示例: + +```json +{ + "status": 0, + "message": "success" +} +``` + + + +## 删除仓库webhook +删除仓库webhook + +> 示例: + +```shell +curl -X DELETE \ +http://localhost:3000/api/yystopf/ceshi/webhooks/7.json +``` + +```javascript +await octokit.request('DELETE /api/yystopf/ceshi/webhooks/7.json') +``` + +### HTTP 请求 +`DELETE /api/:owner/:repo/webhooks/:id.json` + +### 请求参数: +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|owner |是| | string |用户登录名 | +|repo |是| | string |项目标识identifier | +|id |是| | string |webhook id | + +### 返回字段说明: + +> 返回的JSON示例: + +```json +{ + "status": 0, + "message": "success" +} +``` + + +## 获取仓库webhook的历史推送列表 +获取仓库webhook的历史推送列表 + +> 示例: + +```shell +curl -X GET \ +http://localhost:3000/api/yystopf/ceshi/webhooks/3/tasks.json +``` + +```javascript +await octokit.request('GET /api/yystopf/ceshi/webhooks/3/tasks.json') +``` + +### HTTP 请求 +`GET /api/:owner/:repo/webhooks/:id/tasks.json` + +### 请求参数: +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|owner |是| |string |用户登录名 | +|repo |是| |string |项目标识identifier | +|id |是| |integer |webhook ID| + +### 返回字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|id |int |id | +|uuid |string|推送uuid| +|type |string|类型| +|is_succeed |bool|是否推送成功| +|is_delivered |bool|是否完成推送| +|payload_content |json|请求主体内容| +|request_content |json|请求内容,头部等等| +|reponse_content |json|响应内容,状态,头部,主体等等| +|delivered_time |string|推送时间| + + +> 返回的JSON示例: + +```json +{ + "total_count": 6, + "tasks": [ + { + "id": 20, + "type": "gitea", + "uuid": "99aa2c23-6884-4c44-9020-5469320aa408", + "is_succeed": true, + "is_delivered": true, + "payload_content": { + "secret": "123456", + "ref": "refs/heads/master", + "before": "feb48e31362787a7620b53d4df3c4effddbb6f0b", + "after": "feb48e31362787a7620b53d4df3c4effddbb6f0b", + "compare_url": "", + "commits": [ + { + "id": "feb48e31362787a7620b53d4df3c4effddbb6f0b", + "message": "fix\n", + "url": "http://localhost:10081/yystopf/ceshi/commit/feb48e31362787a7620b53d4df3c4effddbb6f0b", + "author": { + "name": "viletyy", + "email": "yystopf@163.com", + "username": "root" + }, + "committer": { + "name": "viletyy", + "email": "yystopf@163.com", + "username": "root" + }, + "verification": { + "verified": false, + "reason": "gpg.error.not_signed_commit", + "signature": "", + "signer": null, + "payload": "" + }, + "timestamp": "2021-07-26T13:52:13+08:00", + "added": null, + "removed": null, + "modified": null + } + ], + "head_commit": null, + "repository": { + "id": 2, + "owner": { + "id": 3, + "login": "yystopf", + "full_name": "", + "email": "yystopf@forge.com", + "avatar_url": "http://localhost:10081/user/avatar/yystopf/-1", + "language": "zh-CN", + "is_admin": true, + "last_login": "2021-07-21T18:38:21+08:00", + "created": "2021-06-03T14:50:25+08:00", + "username": "yystopf" + }, + "name": "ceshi", + "full_name": "yystopf/ceshi", + "description": "", + "empty": false, + "private": false, + "fork": false, + "template": false, + "parent": null, + "mirror": false, + "size": 3846, + "html_url": "http://localhost:10081/yystopf/ceshi", + "ssh_url": "virus@localhost:10081:yystopf/ceshi.git", + "clone_url": "http://localhost:10081/yystopf/ceshi.git", + "original_url": "", + "website": "", + "stars_count": 0, + "forks_count": 1, + "watchers_count": 1, + "open_issues_count": 0, + "open_pr_counter": 0, + "release_counter": 0, + "default_branch": "master", + "archived": false, + "created_at": "2021-06-03T15:15:30+08:00", + "updated_at": "2021-07-26T13:52:16+08:00", + "permissions": { + "admin": false, + "push": false, + "pull": false + }, + "has_issues": true, + "internal_tracker": { + "enable_time_tracker": true, + "allow_only_contributors_to_track_time": true, + "enable_issue_dependencies": true + }, + "has_wiki": true, + "has_pull_requests": true, + "ignore_whitespace_conflicts": false, + "allow_merge_commits": true, + "allow_rebase": true, + "allow_rebase_explicit": true, + "allow_squash_merge": true, + "avatar_url": "", + "internal": false + }, + "pusher": { + "id": 0, + "login": "yystopf", + "full_name": "", + "email": "yystopf@forge.com", + "avatar_url": "http://localhost:10081/user/avatar/yystopf/-1", + "language": "", + "is_admin": false, + "last_login": "0001-01-01T00:00:00Z", + "created": "2021-06-03T14:50:25+08:00", + "username": "yystopf" + }, + "sender": { + "id": 0, + "login": "yystopf", + "full_name": "", + "email": "yystopf@forge.com", + "avatar_url": "http://localhost:10081/user/avatar/yystopf/-1", + "language": "", + "is_admin": false, + "last_login": "0001-01-01T00:00:00Z", + "created": "2021-06-03T14:50:25+08:00", + "username": "yystopf" + } + }, + "request_content": { + "headers": { + "X-GitHub-Delivery": "99aa2c23-6884-4c44-9020-5469320aa408", + "X-GitHub-Event": "push", + "X-Gitea-Delivery": "99aa2c23-6884-4c44-9020-5469320aa408", + "X-Gitea-Event": "push", + "X-Gitea-Signature": "34a01edcd952ff6410ff6ebc946471161bde74aff86171f21621d2c2c4130f66", + "X-Gogs-Delivery": "99aa2c23-6884-4c44-9020-5469320aa408", + "X-Gogs-Event": "push", + "X-Gogs-Signature": "34a01edcd952ff6410ff6ebc946471161bde74aff86171f21621d2c2c4130f66" + } + }, + "response_content": { + "status": 200, + "headers": { + "Cache-Control": "no-store, must-revalidate, private, max-age=0", + "Content-Length": "2556", + "Content-Type": "text/html; charset=utf-8", + "Referrer-Policy": "strict-origin-when-cross-origin", + "Set-Cookie": "__profilin=p%3Dt; path=/; HttpOnly", + "Vary": "Origin", + "X-Content-Type-Options": "nosniff", + "X-Download-Options": "noopen", + "X-Frame-Options": "SAMEORIGIN", + "X-Miniprofiler-Ids": "9ynvpncz5xm0rpgorb5y,hgggd9mv6lr4a9drcrlr,j7zqlx2vy5aji2vtgoba,f1ktsmh3jxvq0z2hf612,mih3dvgvlqhi3zy8lf2x,5k1qbkvbnru8mye9cest,tj6ern8w6awqf2zsimbr,9isaehvubivd52wo5p9v,1rzfhtq1nhuwbgy9p76g,z0xzidzyywna0y7a69m0,hzoklky92ycjqt42gi0s,y0ai7y0t28mcn8x0py2x,322il7nadinp51mw2r5m,m6dukftfsh6tjcxzp1gq,667wlqbytfwbrirnmma1,jcehj3dl8lkw8gk510cr", + "X-Miniprofiler-Original-Cache-Control": "max-age=0, private, must-revalidate", + "X-Permitted-Cross-Domain-Policies": "none", + "X-Request-Id": "08bff080-bbb5-4183-b845-81de3d47120a", + "X-Runtime": "0.394766", + "X-Xss-Protection": "1; mode=block" + }, + "body": "
\n" + }, + "delivered_time": "2021-07-28 11:47:29" + } + ] +} +``` + + +## 仓库webhook测试推送 +仓库webhook测试推送 + +> 示例: + +```shell +curl -X POST \ +http://localhost:3000/api/yystopf/ceshi/webhooks/3/test.json +``` + +```javascript +await octokit.request('POST /api/yystopf/ceshi/webhooks/3/test.json') +``` + +### HTTP 请求 +`POST /api/:owner/:repo/webhooks/:id/test.json` + +### 请求参数: +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|owner |是| | string |用户登录名 | +|repo |是| | string |项目标识identifier | +|id |是| | integer|webhook ID| + + + + +### 返回字段说明: + + +> 返回的JSON示例: + +```json +{ + "status": 0, + "message": "success" +} +``` + \ No newline at end of file diff --git a/app/forms/projects/webhooks/create_form.rb b/app/forms/projects/webhooks/create_form.rb new file mode 100644 index 00000000..75b07b1b --- /dev/null +++ b/app/forms/projects/webhooks/create_form.rb @@ -0,0 +1,8 @@ +class Projects::Webhooks::CreateForm < BaseForm + attr_accessor :type, :url, :http_method, :content_type, :secret, :events, :active, :branch_filter + + validates :url, format: { with: URI::regexp(%w[http https]), message: "请输入正确的地址" } + validates :active, inclusion: {in: [true, false]} + validates :http_method, inclusion: { in: %w(POST GET), message: "请输入正确的请求方式"} + validates :content_type, inclusion: { in: %w(json form), message: "请输入正确的Content Type"} +end \ No newline at end of file diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 12a0097b..8af54219 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -34,11 +34,12 @@ module ProjectsHelper end def json_response(project, user) - repo = Repository.includes(:mirror).select(:id, :mirror_url, :source_clone_url).find_by(project: project) + repo = Repository.includes(:mirror).select(:id, :is_mirror, :mirror_url, :source_clone_url).find_by(project: project) tmp_json = {} unless project.common? tmp_json = tmp_json.merge({ + is_mirror: repo.is_mirror ? true : false, mirror_status: repo.mirror_status, mirror_num: repo.mirror_num, mirror_url: repo.remote_mirror_url, @@ -54,7 +55,11 @@ module ProjectsHelper repo_id: repo.id, open_devops: (user.blank? || user.is_a?(AnonymousUser)) ? false : project.open_devops?, type: project.numerical_for_project_type, - author: render_owner(project) + author: render_owner(project), + project_category_id: project.project_category_id, + project_language_id: project.project_language_id, + license_id: project.license_id, + ignore_id: project.ignore_id }).compact render json: tmp_json diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index 95c669b7..a7218810 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -10,7 +10,7 @@ module RepositoriesHelper end def download_type(str) - default_type = %w(xlsx xls ppt pptx pdf zip 7z rar exe pdb obj idb png jpg gif tif psd svg RData rdata doc docx mpp vsdx dot) + default_type = %w(xlsx xls ppt pptx pdf zip 7z rar exe pdb obj idb png jpg gif tif psd svg RData rdata doc docx mpp vsdx dot otf eot ttf woff woff2) default_type.include?(str&.downcase) end @@ -81,8 +81,10 @@ module RepositoriesHelper content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, entry['path'], ref: ref)['content'] readme_render_decode64_content(content, path) else - file_type = entry['name'].to_s.split(".").last - return entry['content'] if download_type(file_type) + file_type = File.extname(entry['name'].to_s)[1..-1] + if download_type(file_type) + return entry['content'].nil? ? Gitea::Repository::Entries::GetService.call(owner, repo.identifier, entry['path'], ref: ref)['content'] : entry['content'] + end render_decode64_content(entry['content']) end end @@ -94,15 +96,16 @@ module RepositoriesHelper end def render_download_image_url(dir_path, file_path, content) + full_path = file_path.starts_with?("/") ? [dir_path, file_path].join("") : [dir_path, file_path].join("/") + file_name = full_path.split("/")[-1] # 用户名/项目标识/文件路径 - dir_path = generate_dir_path(dir_path) + dir_path = generate_dir_path(full_path.split("/"+file_name)[0]) - file_path = [dir_path, file_path].join('/') + file_path = [dir_path, file_name].join('/') puts "##### render_download_image_url file_path: #{file_path}" base64_to_image(file_path, content) - - file_path = file_path.split('public')[1] + file_path = file_path[6..-1] File.join(base_url, file_path) end diff --git a/app/helpers/tag_chosen_helper.rb b/app/helpers/tag_chosen_helper.rb index 5193c9ab..593e412d 100644 --- a/app/helpers/tag_chosen_helper.rb +++ b/app/helpers/tag_chosen_helper.rb @@ -20,10 +20,16 @@ module TagChosenHelper "done_ratio": render_complete_percentage, "issue_tag": render_issue_tags(project), "issue_type": render_issue_species, - "all_issues": all_issues + "all_issues": all_issues, + "branches": render_branches(project) } end + def render_branches(project) + branches = Gitea::Repository::Branches::ListService.call(project&.owner, project.identifier) + branches.collect{|i| i["name"] if i.is_a?(Hash)} + end + def render_cache_trackers cache_key = "all_trackers/#{Tracker.maximum('id')}" @@ -176,7 +182,7 @@ module TagChosenHelper real_name = user.try(:show_real_name) user_id = user.id is_chosen = ((user.id.to_s == issue_info[0].to_s) ? "1" : "0") - member_info = {id: user_id, name: real_name,avatar_url: url_to_avatar(user),is_chosen: is_chosen} + member_info = {id: user_id, name: real_name,avatar_url: url_to_avatar(user), permission: project.get_premission(user), is_chosen: is_chosen} project_members_info.push(member_info) end end diff --git a/app/jobs/migrate_remote_repository_job.rb b/app/jobs/migrate_remote_repository_job.rb index 03727908..340e006e 100644 --- a/app/jobs/migrate_remote_repository_job.rb +++ b/app/jobs/migrate_remote_repository_job.rb @@ -8,10 +8,13 @@ class MigrateRemoteRepositoryJob < ApplicationJob puts "############ MigrateRemoteRepositoryJob starting ... ############" gitea_repository = Gitea::Repository::MigrateService.new(token, params).call - if gitea_repository - repo&.project&.update_columns(gpid: gitea_repository["id"]) + puts "#gitea_repository#{gitea_repository}" + if gitea_repository[0]==201 + repo&.project&.update_columns(gpid: gitea_repository[2]["id"]) repo&.mirror&.succeeded! puts "############ mirror status: #{repo.mirror.status} ############" + else + repo&.mirror&.failed! end end end diff --git a/app/models/gitea/public_key.rb b/app/models/gitea/public_key.rb new file mode 100644 index 00000000..bb219235 --- /dev/null +++ b/app/models/gitea/public_key.rb @@ -0,0 +1,9 @@ +class Gitea::PublicKey < Gitea::Base + self.inheritance_column = nil # FIX The single-table inheritance mechanism failed + # establish_connection :gitea_db + + self.table_name = "public_key" + + belongs_to :user, class_name: '::User', primary_key: :gitea_uid, foreign_key: :owner_id, optional: true + +end diff --git a/app/models/gitea/webhook.rb b/app/models/gitea/webhook.rb new file mode 100644 index 00000000..f60f5678 --- /dev/null +++ b/app/models/gitea/webhook.rb @@ -0,0 +1,13 @@ +class Gitea::Webhook < Gitea::Base + serialize :events, JSON + self.inheritance_column = nil + + self.table_name = 'webhook' + + has_many :tasks, class_name: "Gitea::WebhookTask", foreign_key: :hook_id + belongs_to :project, class_name: "::Project", primary_key: :gpid, foreign_key: :repo_id, optional: true + + enum hook_task_type: {gogs: 1, slack: 2, gitea: 3, discord: 4, dingtalk: 5, telegram: 6, msteams: 7, feishu: 8, matrix: 9} + enum last_status: {waiting: 0, succeed: 1, fail: 2} + enum content_type: {json: 1, form: 2} +end \ No newline at end of file diff --git a/app/models/gitea/webhook_task.rb b/app/models/gitea/webhook_task.rb new file mode 100644 index 00000000..d19a163a --- /dev/null +++ b/app/models/gitea/webhook_task.rb @@ -0,0 +1,13 @@ +class Gitea::WebhookTask < Gitea::Base + serialize :payload_content, JSON + serialize :request_content, JSON + serialize :response_content, JSON + + self.inheritance_column = nil + + self.table_name = 'hook_task' + + belongs_to :webhook, class_name: "Gitea::Webhook", foreign_key: :hook_id + + enum type: {gogs: 1, slack: 2, gitea: 3, discord: 4, dingtalk: 5, telegram: 6, msteams: 7, feishu: 8, matrix: 9} +end \ No newline at end of file diff --git a/app/models/project.rb b/app/models/project.rb index 8e517c5d..141648f3 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -123,6 +123,7 @@ class Project < ApplicationRecord has_one :applied_transfer_project,-> { order created_at: :desc }, dependent: :destroy has_many :pinned_projects, dependent: :destroy has_many :has_pinned_users, through: :pinned_projects, source: :user + has_many :webhooks, class_name: "Gitea::Webhook", primary_key: :gpid, foreign_key: :repo_id after_save :check_project_members, :reset_cache_data before_save :set_invite_code diff --git a/app/models/team.rb b/app/models/team.rb index c2596390..72df0509 100644 --- a/app/models/team.rb +++ b/app/models/team.rb @@ -31,7 +31,7 @@ class Team < ApplicationRecord validates :name, uniqueness: {scope: :organization_id} - enum authorize: {common: 0, read: 1, write: 2, admin: 3, owner: 4} + enum authorize: {read: 1, write: 2, admin: 3, owner: 4} def self.build(organization_id, name, nickname, description, authorize, includes_all_project, can_create_org_project) self.create!(organization_id: organization_id, diff --git a/app/models/team_unit.rb b/app/models/team_unit.rb index c757cc68..fdd1b975 100644 --- a/app/models/team_unit.rb +++ b/app/models/team_unit.rb @@ -20,7 +20,7 @@ class TeamUnit < ApplicationRecord belongs_to :organization belongs_to :team - enum unit_type: {code: 1, issues: 2, pulls: 3, releases: 4} + enum unit_type: {code: 1, issues: 2, pulls: 3, wiki: 4, releases: 5} validates :unit_type, uniqueness: { scope: [:organization_id, :team_id]} diff --git a/app/models/user.rb b/app/models/user.rb index c7067853..cbf0c66f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -169,6 +169,7 @@ class User < Owner accepts_nested_attributes_for :is_pinned_projects has_many :issues, dependent: :destroy, foreign_key: :author_id has_many :pull_requests, dependent: :destroy + has_many :public_keys, class_name: "Gitea::PublicKey",primary_key: :gitea_uid, foreign_key: :owner_id, dependent: :destroy # Groups and active users scope :active, lambda { where(status: STATUS_ACTIVE) } diff --git a/app/services/gitea/accelerator/migrate_service.rb b/app/services/gitea/accelerator/migrate_service.rb index 26541b48..86b8b3db 100644 --- a/app/services/gitea/accelerator/migrate_service.rb +++ b/app/services/gitea/accelerator/migrate_service.rb @@ -44,7 +44,7 @@ class Gitea::Accelerator::MigrateService < Gitea::Accelerator::BaseService clone_addr: params[:clone_addr], repo_name: params[:repository_name], auth_username: params[:auth_username], - auth_password: params[:auth_password], + auth_password: Base64.decode64(params[:auth_password]), mirror: ActiveModel::Type::Boolean.new.cast(params[:is_mirror]) } end diff --git a/app/services/gitea/client_service.rb b/app/services/gitea/client_service.rb index b88d8382..868a704c 100644 --- a/app/services/gitea/client_service.rb +++ b/app/services/gitea/client_service.rb @@ -108,6 +108,7 @@ class Gitea::ClientService < ApplicationService def full_url(api_rest, action='post') url = [api_url, api_rest].join('').freeze url = action === 'get' ? url : URI.escape(url) + url = URI.escape(url) unless url.ascii_only? puts "[gitea] request url: #{url}" return url end diff --git a/app/services/gitea/repository/migrate_service.rb b/app/services/gitea/repository/migrate_service.rb index 50881208..ab60b42f 100644 --- a/app/services/gitea/repository/migrate_service.rb +++ b/app/services/gitea/repository/migrate_service.rb @@ -33,7 +33,7 @@ class Gitea::Repository::MigrateService < Gitea::ClientService def call response = post(url, request_params) - render_201_response(response) + render_response(response) end private diff --git a/app/services/gitea/repository/webhooks/create_service.rb b/app/services/gitea/repository/webhooks/create_service.rb new file mode 100644 index 00000000..33c9a9b0 --- /dev/null +++ b/app/services/gitea/repository/webhooks/create_service.rb @@ -0,0 +1,23 @@ +class Gitea::Repository::Webhooks::CreateService < Gitea::ClientService + attr_reader :token, :owner, :repo, :params + def initialize(token, owner, repo, params) + @token = token + @owner = owner + @repo = repo + @params = params + end + + def call + response = post(url, request_params) + render_response(response) + end + + private + def request_params + Hash.new.merge({token: token, data: params}) + end + + def url + "/repos/#{owner}/#{repo}/hooks".freeze + end +end \ No newline at end of file diff --git a/app/services/gitea/repository/webhooks/delete_service.rb b/app/services/gitea/repository/webhooks/delete_service.rb new file mode 100644 index 00000000..997e9006 --- /dev/null +++ b/app/services/gitea/repository/webhooks/delete_service.rb @@ -0,0 +1,24 @@ +class Gitea::Repository::Webhooks::DeleteService < Gitea::ClientService + attr_reader :token, :owner, :repo, :id + + def initialize(token, owner, repo, id) + @token = token + @owner = owner + @repo = repo + @id = id + end + + def call + response = delete(url, params) + render_response(response) + end + + private + def params + Hash.new.merge(token: token) + end + + def url + "/repos/#{owner}/#{repo}/hooks/#{id}".freeze + end +end \ No newline at end of file diff --git a/app/services/gitea/repository/webhooks/tasks_service.rb b/app/services/gitea/repository/webhooks/tasks_service.rb new file mode 100644 index 00000000..e4c62edb --- /dev/null +++ b/app/services/gitea/repository/webhooks/tasks_service.rb @@ -0,0 +1,27 @@ +class Gitea::Repository::Webhooks::TasksService < Gitea::ClientService + attr_reader :token, :owner, :repo, :webhook_id + + # ref: The name of the commit/branch/tag. Default the repository’s default branch (usually master) + # repo_name: the name of repository + def initialize(token, owner, repo, webhook_id) + @token = token + @owner = owner + @repo = repo + @webhook_id = webhook_id + end + + def call + response = get(url, params) + render_response(response) + end + + private + def params + Hash.new.merge(token: user.gitea_token) + end + + def url + "/repos/#{owner}/#{repo}/hooks/#{webhook_id}/hook_tasks".freeze + end + +end diff --git a/app/services/gitea/repository/webhooks/test_service.rb b/app/services/gitea/repository/webhooks/test_service.rb new file mode 100644 index 00000000..7f183761 --- /dev/null +++ b/app/services/gitea/repository/webhooks/test_service.rb @@ -0,0 +1,24 @@ +class Gitea::Repository::Webhooks::TestService < Gitea::ClientService + attr_reader :token, :owner, :repo, :webhook_id + + def initialize(token, owner, repo, webhook_id) + @token = token + @owner = owner + @repo = repo + @webhook_id = webhook_id + end + + def call + response = post(url, request_params) + render_response(response) + end + + private + def request_params + Hash.new.merge({token: token}) + end + + def url + "/repos/#{owner}/#{repo}/hooks/#{webhook_id}/tests".freeze + end +end \ No newline at end of file diff --git a/app/services/gitea/repository/webhooks/update_service.rb b/app/services/gitea/repository/webhooks/update_service.rb new file mode 100644 index 00000000..6094c6c5 --- /dev/null +++ b/app/services/gitea/repository/webhooks/update_service.rb @@ -0,0 +1,24 @@ +class Gitea::Repository::Webhooks::UpdateService < Gitea::ClientService + attr_reader :token, :owner, :repo, :id, :params + def initialize(token, owner, repo, id, params) + @token = token + @owner = owner + @repo = repo + @id = id + @params = params + end + + def call + response = patch(url, data_params) + render_response(response) + end + + private + def url + "/repos/#{owner}/#{repo}/hooks/#{id}" + end + + def data_params + Hash.new.merge(token: token, data: params).compact + end +end \ No newline at end of file diff --git a/app/services/gitea/user/keys/create_service.rb b/app/services/gitea/user/keys/create_service.rb new file mode 100644 index 00000000..2b720c48 --- /dev/null +++ b/app/services/gitea/user/keys/create_service.rb @@ -0,0 +1,21 @@ +class Gitea::User::Keys::CreateService < Gitea::ClientService + attr_reader :token, :params + def initialize(token, params) + @token = token + @params = params + end + + def call + response = post(url, request_params) + render_response(response) + end + + private + def request_params + Hash.new.merge({token: token, data: params}) + end + + def url + '/user/keys'.freeze + end +end \ No newline at end of file diff --git a/app/services/gitea/user/keys/delete_service.rb b/app/services/gitea/user/keys/delete_service.rb new file mode 100644 index 00000000..3ae263e3 --- /dev/null +++ b/app/services/gitea/user/keys/delete_service.rb @@ -0,0 +1,23 @@ +class Gitea::User::Keys::DeleteService < Gitea::ClientService + attr_reader :token, :key_id + + def initialize(token, key_id) + @token = token + @key_id = key_id + end + + def call + response = delete(url, params) + render_response(response) + end + + private + + def params + Hash.new.merge(token: token) + end + + def url + "/user/keys/#{key_id}".freeze + end +end diff --git a/app/services/gitea/user/keys/get_service.rb b/app/services/gitea/user/keys/get_service.rb new file mode 100644 index 00000000..fbcf1c86 --- /dev/null +++ b/app/services/gitea/user/keys/get_service.rb @@ -0,0 +1,22 @@ +class Gitea::User::Keys::GetService < Gitea::ClientService + attr_reader :token, :key_id + + def initialize(token, key_id) + @token = token + @key_id = key_id + end + + def call + response = get(url, params) + render_response(response) + end + + private + def params + Hash.new.merge({token: token}) + end + + def url + "/user/keys/#{key_id}".freeze + end +end \ No newline at end of file diff --git a/app/services/gitea/user/keys/list_service.rb b/app/services/gitea/user/keys/list_service.rb new file mode 100644 index 00000000..1ddfde41 --- /dev/null +++ b/app/services/gitea/user/keys/list_service.rb @@ -0,0 +1,26 @@ +class Gitea::User::Keys::ListService < Gitea::ClientService + attr_reader :token, :page, :limit, :fingerprint + + def initialize(token, page, limit, fingerprint="") + @token = token + @page = page + @limit = limit + @fingerprint = fingerprint + end + + def call + response = get(url, params) + render_response(response) + end + + private + + def params + Hash.new.merge({token: token, fingerprint: fingerprint, page: page, limit: limit}) + end + + def url + '/user/keys'.freeze + end + +end \ No newline at end of file diff --git a/app/services/organizations/teams/create_service.rb b/app/services/organizations/teams/create_service.rb index c3fc5f59..17173229 100644 --- a/app/services/organizations/teams/create_service.rb +++ b/app/services/organizations/teams/create_service.rb @@ -37,7 +37,7 @@ class Organizations::Teams::CreateService < ApplicationService end def authorize - params[:authorize].present? ? params[:authorize] : "common" + params[:authorize].present? ? params[:authorize] : "read" end def includes_all_project @@ -54,7 +54,7 @@ class Organizations::Teams::CreateService < ApplicationService end def units_params - %w(admin owner).include?(authorize) ? %w(code issues pulls releases) : params[:unit_types] + %w(code issues pulls wiki releases) end def create_units diff --git a/app/services/organizations/teams/update_service.rb b/app/services/organizations/teams/update_service.rb index b5b273d8..275dad88 100644 --- a/app/services/organizations/teams/update_service.rb +++ b/app/services/organizations/teams/update_service.rb @@ -33,7 +33,7 @@ class Organizations::Teams::UpdateService < ApplicationService end def units_params - %w(admin owner).include?(team.authorize) ? %w(code issues pulls releases) : params[:unit_types] + %w(code issues pulls wiki releases) end def update_team(update_params) diff --git a/app/services/projects/apply_transfer_service.rb b/app/services/projects/apply_transfer_service.rb index 28097bed..ca26f86c 100644 --- a/app/services/projects/apply_transfer_service.rb +++ b/app/services/projects/apply_transfer_service.rb @@ -30,7 +30,7 @@ class Projects::ApplyTransferService < ApplicationService def is_permit_owner return true unless @owner.is_a?(Organization) - return @owner.is_owner?(@user) + return @owner.is_admin?(@user) end def create_apply diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index 8ae787c5..84b80f50 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -15,8 +15,11 @@ class Projects::ForkService < ApplicationService :rep_identifier, :project_category_id, :project_language_id, :license_id, :ignore_id, {repository: [:identifier, :hidden]}] + result = Gitea::Repository::ForkService.new(@project.owner, @target_owner, @project.identifier, @organization).call + clone_project.owner = @target_owner clone_project.forked_from_project_id = @project.id + clone_project.gpid = result['id'] clone_project.save! new_repository = clone_project.repository @@ -26,8 +29,6 @@ class Projects::ForkService < ApplicationService ProjectUnit.init_types(clone_project.id) - result = Gitea::Repository::ForkService.new(@project.owner, @target_owner, @project.identifier, @organization).call - @project.update_column('forked_count', @project&.forked_count.to_i + 1) new_repository.update_column('url', result['clone_url']) if result diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index 0b4f1d99..71c6ff68 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -23,12 +23,12 @@ class Projects::TransferService < ApplicationService private def update_owner - project.members.find_by(user_id: owner.id).destroy! if owner.is_a?(User) + project.members.map{|m| m.destroy! if m.user_id == owner.id || (new_owner.is_a?(Organization) && new_owner.is_member?(m.user_id)) } project.update!(user_id: new_owner.id) end def update_repo_url - project.repository.update!(url: @gitea_repo["clone_url"]) + project.repository.update!(user_id: new_owner.id, url: @gitea_repo["clone_url"]) end def update_visit_teams diff --git a/app/services/repositories/migrate_service.rb b/app/services/repositories/migrate_service.rb index 374115bf..2870faba 100644 --- a/app/services/repositories/migrate_service.rb +++ b/app/services/repositories/migrate_service.rb @@ -32,7 +32,7 @@ class Repositories::MigrateService < ApplicationService private: params[:hidden], mirror: wrapper_mirror || false, auth_username: params[:login], - auth_password: params[:password] + auth_password: Base64.decode64(params[:password]) } end diff --git a/app/views/owners/index.json.jbuilder b/app/views/owners/index.json.jbuilder index 6b7e5222..80b6b590 100644 --- a/app/views/owners/index.json.jbuilder +++ b/app/views/owners/index.json.jbuilder @@ -2,6 +2,7 @@ json.total_count @owners.size json.owners @owners.each do |owner| json.id owner.id json.type owner.type + json.login owner.login json.name owner&.show_real_name json.avatar_url url_to_avatar(owner) end \ No newline at end of file diff --git a/app/views/projects/create.json.jbuilder b/app/views/projects/create.json.jbuilder index 0b914b32..c7793a71 100644 --- a/app/views/projects/create.json.jbuilder +++ b/app/views/projects/create.json.jbuilder @@ -1,2 +1,2 @@ json.extract! @project, :id, :name,:identifier -json.login @project&.owner&.login +json.login @project&.owner&.login \ No newline at end of file diff --git a/app/views/projects/migrate.json.jbuilder b/app/views/projects/migrate.json.jbuilder index c9ae044e..1e8ddc4d 100644 --- a/app/views/projects/migrate.json.jbuilder +++ b/app/views/projects/migrate.json.jbuilder @@ -1 +1,2 @@ json.extract! @project, :id, :name, :identifier +json.login @project&.owner&.login \ No newline at end of file diff --git a/app/views/projects/teams/index.json.jbuilder b/app/views/projects/teams/index.json.jbuilder index 0a48e884..0b74a37a 100644 --- a/app/views/projects/teams/index.json.jbuilder +++ b/app/views/projects/teams/index.json.jbuilder @@ -1,5 +1,6 @@ json.total_count @teams.total_count +json.can_add @owner.is_owner?(current_user.id) || @owner&.repo_admin_change_team_access json.teams @teams.each do |team| json.(team, :id, :name, :authorize) - json.can_remove !team.includes_all_project && team&.organization&.repo_admin_change_team_access + json.can_remove !team.includes_all_project && (@owner.is_owner?(current_user.id) || team&.organization&.repo_admin_change_team_access) end \ No newline at end of file diff --git a/app/views/projects/webhooks/_detail.json.jbuilder b/app/views/projects/webhooks/_detail.json.jbuilder new file mode 100644 index 00000000..2497e5c6 --- /dev/null +++ b/app/views/projects/webhooks/_detail.json.jbuilder @@ -0,0 +1,4 @@ +json.(webhook, :id, :url, :http_method, :is_active) +json.type webhook.hook_task_type +json.last_status webhook.last_status +json.create_time Time.at(webhook.created_unix).strftime("%Y-%m-%d %H:%M:%S") \ No newline at end of file diff --git a/app/views/projects/webhooks/create.json.jbuilder b/app/views/projects/webhooks/create.json.jbuilder new file mode 100644 index 00000000..6d6dde31 --- /dev/null +++ b/app/views/projects/webhooks/create.json.jbuilder @@ -0,0 +1,7 @@ +json.id @webhook["id"] +json.type @webhook["type"] +json.content_type @webhook["config"]["content_type"] +json.url @webhook["config"]["url"] +json.events @webhook["events"] +json.active @webhook["active"] +json.create_time @webhook["created_at"].to_time.strftime("%Y-%m-%d %H:%M:%S") \ No newline at end of file diff --git a/app/views/projects/webhooks/edit.json.jbuilder b/app/views/projects/webhooks/edit.json.jbuilder new file mode 100644 index 00000000..2ee6d24e --- /dev/null +++ b/app/views/projects/webhooks/edit.json.jbuilder @@ -0,0 +1,11 @@ +json.id @webhook.id +json.(@webhook, :id, :http_method, :content_type, :url, :secret, :last_status, :is_active) +json.type @webhook.hook_task_type +json.create_time Time.at(@webhook.created_unix).strftime("%Y-%m-%d %H:%M:%S") +event = @webhook.events +json.branch_filter event["branch_filter"] +if event["send_everything"] + json.events event["events"].keys.collect{|i| i == "pull_request" ? i + "_only" : i} +else + json.events event["events"].select{|k, v| v}.keys.collect{|i| i == "pull_request" ? i + "_only" : i} +end diff --git a/app/views/projects/webhooks/index.json.jbuilder b/app/views/projects/webhooks/index.json.jbuilder new file mode 100644 index 00000000..62722acb --- /dev/null +++ b/app/views/projects/webhooks/index.json.jbuilder @@ -0,0 +1,4 @@ +json.total_count @webhooks.total_count +json.webhooks @webhooks.each do |webhook| + json.partial! 'detail', webhook: webhook +end \ No newline at end of file diff --git a/app/views/projects/webhooks/tasks.json.jbuilder b/app/views/projects/webhooks/tasks.json.jbuilder new file mode 100644 index 00000000..b8aef99f --- /dev/null +++ b/app/views/projects/webhooks/tasks.json.jbuilder @@ -0,0 +1,5 @@ +json.total_count @tasks.total_count +json.tasks @tasks.each do |task| + json.(task, :id, :type, :uuid, :is_succeed, :is_delivered, :payload_content, :request_content, :response_content) + json.delivered_time Time.at(task.delivered*10**-9).strftime("%Y-%m-%d %H:%M:%S") +end \ No newline at end of file diff --git a/app/views/public_keys/create.json.jbuilder b/app/views/public_keys/create.json.jbuilder new file mode 100644 index 00000000..9296413a --- /dev/null +++ b/app/views/public_keys/create.json.jbuilder @@ -0,0 +1,8 @@ +if @public_key.present? + json.status 0 + json.id @public_key["id"] + json.name @public_key["title"] + json.content @public_key["key"] + json.fingerprint @public_key["fingerprint"] + json.created_time @public_key["created_at"].to_time.strftime("%Y/%m/%d %H:%M") +end \ No newline at end of file diff --git a/app/views/public_keys/index.json.jbuilder b/app/views/public_keys/index.json.jbuilder new file mode 100644 index 00000000..6710529b --- /dev/null +++ b/app/views/public_keys/index.json.jbuilder @@ -0,0 +1,5 @@ +json.total_count @public_keys.total_count +json.public_keys @public_keys do |public_key| + json.(public_key, :id, :name, :content, :fingerprint, :created_unix) + json.created_time Time.at(public_key.created_unix).strftime("%Y/%m/%d %H:%M") +end \ No newline at end of file diff --git a/app/views/pull_requests/index.json.jbuilder b/app/views/pull_requests/index.json.jbuilder index e13f1005..e13f1935 100644 --- a/app/views/pull_requests/index.json.jbuilder +++ b/app/views/pull_requests/index.json.jbuilder @@ -5,6 +5,7 @@ json.merged_issues_size @merged_issues.size json.search_count @issues_size json.limit @limit json.user_admin_or_member @user_admin_or_member +json.user_admin_or_developer @user_admin_or_developer json.project_name @project.name json.project_author_name @project.owner.try(:login) diff --git a/app/views/repositories/_simple_entry.json.jbuilder b/app/views/repositories/_simple_entry.json.jbuilder index 7d41bbd2..6c208875 100644 --- a/app/views/repositories/_simple_entry.json.jbuilder +++ b/app/views/repositories/_simple_entry.json.jbuilder @@ -1,6 +1,6 @@ if @project.forge? file_name = entry['name'] - file_type = file_name.to_s.split(".").last + file_type = File.extname(file_name.to_s)[1..-1] direct_download = download_type(file_type) image_type = image_type?(file_type) json.name file_name diff --git a/app/views/versions/show.json.jbuilder b/app/views/versions/show.json.jbuilder index fe8606b2..4dda3095 100644 --- a/app/views/versions/show.json.jbuilder +++ b/app/views/versions/show.json.jbuilder @@ -8,7 +8,7 @@ json.user_login @version.version_user.try(:login) json.created_at format_time(@version.created_on) json.updated_at format_time(@version.updated_on) json.search_count @version_issues_size -json.percent @version.percent*100 +json.percent @version_close_issues_size.to_f/@version_issues_size*100#@version.percent*100 json.extract! @version, :id,:name,:project_id,:description, :effective_date, :status, :sharing,:wiki_page_title json.issues do diff --git a/config/routes.rb b/config/routes.rb index fc5c6c8a..48e9abe1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -71,6 +71,8 @@ Rails.application.routes.draw do # end end + resources :public_keys, only: [:index, :create, :destroy] + resources :statistic, only: [:index] do collection do get :platform_profile @@ -348,6 +350,7 @@ Rails.application.routes.draw do get '/auth/qq/callback', to: 'oauth/qq#create' get '/auth/wechat/callback', to: 'oauth/wechat#create' + get '/auth/educoder/callback', to: 'oauth/educoder#create' resource :bind_user, only: [:create] resources :hot_keywords, only: [:index] @@ -571,6 +574,12 @@ Rails.application.routes.draw do post :cancel end end + resources :webhooks, except: [:show, :new] do + member do + get :tasks + post :test + end + end scope do get( '/blob/*id/diff', diff --git a/lib/tasks/fix_some_error_data.rake b/lib/tasks/fix_some_error_data.rake new file mode 100644 index 00000000..872cf429 --- /dev/null +++ b/lib/tasks/fix_some_error_data.rake @@ -0,0 +1,61 @@ +desc "Fix Some Unstep Data" + +namespace :fix_some_error_data do + task org_member_and_project_member: :environment do + puts "======Begin: fix organization memberr======" + fix_org_count = 0 + OrganizationUser.find_each do |org_user| + org = org_user.organization + if org.team_users.where(user_id: org_user.user_id).blank? + Gitea::Organization::OrganizationUser::DeleteService.call(org_user.organization.gitea_token, org_user.organization.login, org_user&.user&.login) + org_user.destroy + fix_org_count += 1 + end + end + puts "======Count: #{fix_org_count}======" + puts "======End: fix organization member and project member======" + puts "======Begin: fix project member======" + fix_pro_count = 0 + Member.joins(project: :owner).where(users: {type: 'Organization'}).find_each do |member| + if member.project.owner.team_users.where(user_id: member.user_id).blank? + next + else + member.destroy + fix_pro_count += 1 + end + end + puts "======Count: #{fix_pro_count}======" + puts "======End: fix project member======" + end + + task open_full_gitea_team_authorize: :environment do + puts "======Begin: fix open full team authorize======" + team_count = 0 + Team.find_each do |team| + team.team_units.destroy_all + %w(code issues pulls wiki releases).each do |unit| + TeamUnit.build(team.organization_id, team.id, unit) + end + Gitea::Organization::Team::UpdateService.call(team&.organization&.gitea_token, team) + team_count += 1 + end + puts "======Count: #{team_count}======" + puts "======End: fix open full team authorize======" + end + + task transfer_repository_user_id: :environment do + puts "======Begin: fix open full team authorize======" + repo_count = 0 + AppliedTransferProject.find_each do |transfer| + next unless transfer.project.present? + next unless transfer.project.repository.present? + if transfer.project.user_id != transfer.project.repository.user_id + transfer.project.repository.update(user_id: transfer.project.user_id) + repo_count += 1 + end + end + puts "======Count: #{repo_count}======" + puts "======End: fix open full team authorize======" + + end +end \ No newline at end of file diff --git a/lib/tasks/produce_and_export_ceshi_user.rake b/lib/tasks/produce_and_export_ceshi_user.rake new file mode 100644 index 00000000..3163e736 --- /dev/null +++ b/lib/tasks/produce_and_export_ceshi_user.rake @@ -0,0 +1,61 @@ +namespace :produce_and_export_ceshi_user do + desc "Produce ceshi user and Export to excel" + task call: :environment do + puts "=======Begin=======" + DCODES = %W(1 2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) + username = DCODES.sample(8).join + email = username + '@forge.com' + password = SecureRandom.base64[8..-1] + p = Axlsx::Package.new + p.workbook.add_worksheet(:name => '测试用户') do |sheet| + sheet.add_row ["用户名", "邮箱", "密码", "GiteaToken", "测试仓库", "测试仓库克隆地址"] + (1..100).to_a.each do |i| + while (User.find_by(login: username).present? || User.find_by(mail: email).present?) do + username = DCODES.sample(8).join + email = username + '@forge.com' + password = SecureRandom.base64[8..-1] + end + puts "=======Generate:[#{i}] Username: #{username}, Password: #{password}, Email: #{email}=======" + puts "=======Create User Begin====== " + + user = User.new(admin: false, login: username, mail: email, type: "User") + user.password = password + user.platform = 'forge' + user.activate + + next unless user.valid? + + interactor = Gitea::RegisterInteractor.call({username: username, email: email, password: password}) + if interactor.success? + gitea_user = interactor.result + result = Gitea::User::GenerateTokenService.call(username, password) + user.gitea_token = result['sha1'] + user.gitea_uid = gitea_user[:body]['id'] + if user.save! + UserExtension.create!(user_id: user.id) + end + end + + puts "=======Create User End====== " + code = DCODES.sample(8).join + project_params = { + user_id: user.id, + name: code, + repository_name: code, + project_category_id: ProjectCategory.pluck(:id).sample, + project_language_id: ProjectLanguage.pluck(:id).sample, + license_id: License.pluck(:id).sample, + ignore_id: Ignore.pluck(:id).sample, + private: true + } + project = Projects::CreateService.new(user, project_params).call + puts project.as_json + sheet.add_row [username, email, password, user.gitea_token, "#{username}/#{code}", project&.repository.url] + end + end + p.use_shared_strings = true + p.serialize('ceshi_user.xlsx') + + puts "=======END=======" + end +end \ No newline at end of file diff --git a/public/docs/api.html b/public/docs/api.html index 5061a1ac..35099a4e 100644 --- a/public/docs/api.html +++ b/public/docs/api.html @@ -325,6 +325,20 @@ +获取public_keys列表,支持分页
+ +++示例:
+
curl -X GET \
+http://localhost:3000/api/public_keys.json
+
await octokit.request('GET /api/public_keys.json')
+
GET api/public_keys.json
参数 | +必选 | +默认 | +类型 | +字段说明 | +
---|---|---|---|---|
page | +否 | +1 | +int | +页码 | +
limit | +否 | +15 | +int | +每页数量 | +
参数 | +类型 | +字段说明 | +
---|---|---|
total_count | +int | +总数 | +
public_keys.id | +int | +ID | +
public_keys.name | +string | +密钥标题 | +
public_keys.content | +string | +密钥内容 | +
public_keys.fingerprint | +string | +密钥标识 | +
public_keys.created_time | +string | +密钥创建时间 | +
++返回的JSON示例:
+
{
+ "total_count": 1,
+ "public_keys": [
+ {
+ "id": 16,
+ "name": "xxx",
+ "content": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDe5ETOTB5PcmcYJkIhfF7+mxmJQDCLg7/LnMoKHpKoo/jYUnFU9OjfsxVo3FTNUvh2475WXMAur5KsFoNKjK9+JHxvoXyJKmyVPWgXU/NRxQyaWPnPLPK8qPRF5ksJE6feBOqtsdxsvBiHs2r1NX/U26Ecnpr6avudD0cmyrEfbYMWbupLrhsd39dswPT73f3W5jc7B9Y47Ioiv8UOju3ABt1+kpuAjaaVC6VtUQoEFiZb1y33yBnyePya7dvFyApyD4ILyyIG2rtZWK7l53YFnwZDuFsTWjEEEQD0U4FBSFdH5wtwx0WQLMSNyTtaFBSG0kJ+uiQQIrxlvikcm63df7zbC3/rWLPsKgW122Zt966dcpFqiCiJNDKZPPw3qpg8TBL6X+qIZ+FxVEk/16/zScpyEfoxQp0GvgxI7hPLErmfkC5tMsib8MAXYBNyvJXna0vg/wOaNNIaI4SAH9Ksh3f/TtalYVjp6WxIwVBfnbq51WnmlnEXePtX6XjAGL+GbF2VQ1nv/IzrY09tNbTV6wQsrSIP3VDzYQxdJ1rdsVNMoJB0H2Pu0NdcSz53Wx45N+myD0QnE05ss+zDp5StY90OYsx2aCo6qAA8Qn2jUjdta7MQWwkPfKrta4tTQ0XbWMjx4/E1+l3J5liwZkl2XOGOwhfXdRsBjaEziZ18kQ== yystopf@163.com",
+ "fingerprint": "SHA256:cU8AK/+roqUUyiaYXIdS2Nj4+Rb2p6rqWSeRDc+aqKM",
+ "created_unix": 1626246596,
+ "created_time": "2021/07/14 15:09"
+ }
+ ]
+}
+
创建public_key
+ +++示例:
+
curl -X POST \
+http://localhost:3000/api/public_keys.json
+
await octokit.request('POST /api/public_keys.json')
+
POST api/public_keys.json
参数 | +必选 | +默认 | +类型 | +字段说明 | +
---|---|---|---|---|
key | +是 | +否 | +string | +密钥 | +
title | +是 | +否 | +string | +密钥标题 | +
++请求的JSON示例: +
+json +{ + "public_key": { + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDe5ETOTB5PcmcYJkIhfF7+mxmJQDCLg7/LnMoKHpKoo/jYUnFU9OjfsxVo3FTNUvh2475WXMAur5KsFoNKjK9+JHxvoXyJKmyVPWgXU/NRxQyaWPnPLPK8qPRF5ksJE6feBOqtsdxsvBiHs2r1NX/U26Ecnpr6avudD0cmyrEfbYMWbupLrhsd39dswPT73f3W5jc7B9Y47Ioiv8UOju3ABt1+kpuAjaaVC6VtUQoEFiZb1y33yBnyePya7dvFyApyD4ILyyIG2rtZWK7l53YFnwZDuFsTWjEEEQD0U4FBSFdH5wtwx0WQLMSNyTtaFBSG0kJ+uiQQIrxlvikcm63df7zbC3/rWLPsKgW122Zt966dcpFqiCiJNDKZPPw3qpg8TBL6X+qIZ+FxVEk/16/zScpyEfoxQp0GvgxI7hPLErmfkC5tMsib8MAXYBNyvJXna0vg/wOaNNIaI4SAH9Ksh3f/TtalYVjp6WxIwVBfnbq51WnmlnEXePtX6XjAGL+GbF2VQ1nv/IzrY09tNbTV6wQsrSIP3VDzYQxdJ1rdsVNMoJB0H2Pu0NdcSz53Wx45N+myD0QnE05ss+zDp5StY90OYsx2aCo6qAA8Qn2jUjdta7MQWwkPfKrta4tTQ0XbWMjx4/E1+l3J5liwZkl2XOGOwhfXdRsBjaEziZ18kQ== yystopf@163.com", + "title": "xxx" + } +} +
参数 | +类型 | +字段说明 | +
---|---|---|
total_count | +int | +总数 | +
id | +int | +ID | +
name | +string | +密钥标题 | +
content | +string | +密钥内容 | +
fingerprint | +string | +密钥标识 | +
created_time | +string | +密钥创建时间 | +
++返回的JSON示例:
+
{
+ "id": 17,
+ "name": "xxx",
+ "content": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDe5ETOTB5PcmcYJkIhfF7+mxmJQDCLg7/LnMoKHpKoo/jYUnFU9OjfsxVo3FTNUvh2475WXMAur5KsFoNKjK9+JHxvoXyJKmyVPWgXU/NRxQyaWPnPLPK8qPRF5ksJE6feBOqtsdxsvBiHs2r1NX/U26Ecnpr6avudD0cmyrEfbYMWbupLrhsd39dswPT73f3W5jc7B9Y47Ioiv8UOju3ABt1+kpuAjaaVC6VtUQoEFiZb1y33yBnyePya7dvFyApyD4ILyyIG2rtZWK7l53YFnwZDuFsTWjEEEQD0U4FBSFdH5wtwx0WQLMSNyTtaFBSG0kJ+uiQQIrxlvikcm63df7zbC3/rWLPsKgW122Zt966dcpFqiCiJNDKZPPw3qpg8TBL6X+qIZ+FxVEk/16/zScpyEfoxQp0GvgxI7hPLErmfkC5tMsib8MAXYBNyvJXna0vg/wOaNNIaI4SAH9Ksh3f/TtalYVjp6WxIwVBfnbq51WnmlnEXePtX6XjAGL+GbF2VQ1nv/IzrY09tNbTV6wQsrSIP3VDzYQxdJ1rdsVNMoJB0H2Pu0NdcSz53Wx45N+myD0QnE05ss+zDp5StY90OYsx2aCo6qAA8Qn2jUjdta7MQWwkPfKrta4tTQ0XbWMjx4/E1+l3J5liwZkl2XOGOwhfXdRsBjaEziZ18kQ== yystopf@163.com",
+ "fingerprint": "SHA256:cU8AK/+roqUUyiaYXIdS2Nj4+Rb2p6rqWSeRDc+aqKM",
+ "created_time": "2021/07/14 15:26"
+}
+
删除public_key
+ +++示例:
+
curl -X DELETE \
+http://localhost:3000/api/public_keys/:id.json
+
await octokit.request('DELETE /api/public_keys/:id.json')
+
DELETE api/public_keys/:id.json
参数 | +必选 | +默认 | +类型 | +字段说明 | +
---|---|---|---|---|
id | +是 | +否 | +int | +密钥ID | +
++返回的JSON示例:
+
{
+ "status": 0,
+ "message": "success"
+}
+