diff --git a/CHANGELOG.md b/CHANGELOG.md index a5250040e..ecb743b91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,35 @@ # Changelog +## [v3.2.0](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-06-09 + +### ENHANCEMENTS + * ADD 集成邮件和平台站内信等通知系统 + * Fix 代码库二级页面-优化文件子目录浏览功能(#50388) + * Fix 代码库二级页面-优化commit提交详情页页面排版及数据显示(#50372) + * Fix 代码库二级页面-优化commit提交信息列表页加载方式和数据排序功能(#50348) + * Fix 代码库二级页面-优化创建发行版功能(#50346) + * Fix 代码库二级页面-优化标签列表页功能(#50344) + * Fix 代码库二级页面-优化发行版本列表页功能(#50345) + * Fix 代码库二级页面-优化分支列表页功能(#50343) + * Fix 其他问题优化(#51581) (#51343) (#51108) + +--- + +### BUGFIXES + * Fix 发行版—标签跳转链接错误(#51666) + * Fix 文件预览报错(#51660) + * Fix 标签创建时间显示错误(#51658) + * Fix 分支列表中头像显示问题(#51656) + * Fix 文本信息过长(#51630) (#51626) + * Fix 版本库中附件下载400(#51625) + * Fix loading页面优化(#51588) + * Fix 提交详情页面优化(#51577) + * Fix 修复易修复制功能(#51569) + * Fix 修复新建发行版用户信息显示错误的问题(#51665) + * Fix 修复查看文件详细信息报错的问题(#51561) + * Fix 修复提交记录中时间显示格式问题(#51526) + * Fix 组织下项目更加更新时间倒序排序(#50833) + + ## [v3.1.0](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-06-09 * ENHANCEMENTS diff --git a/app/controllers/admins/message_templates_controller.rb b/app/controllers/admins/message_templates_controller.rb new file mode 100644 index 000000000..23c94e784 --- /dev/null +++ b/app/controllers/admins/message_templates_controller.rb @@ -0,0 +1,44 @@ +class Admins::MessageTemplatesController < Admins::BaseController + before_action :get_template, only: [:edit, :update, :destroy] + + def index + message_templates = MessageTemplate.group(:type).count.keys + @message_templates = kaminari_array_paginate(message_templates) + end + + def edit + end + + def update + if @message_template.update_attributes(message_template_params) + redirect_to admins_message_templates_path + flash[:success] = '消息模版更新成功' + else + redirect_to admins_message_templates_path + flash[:danger] = @message_template.errors.full_messages.join(",") + end + end + + def init_data + if MessageTemplate.build_init_data + redirect_to admins_message_templates_path + flash[:success] = '消息模版初始化成功' + else + redirect_to admins_message_templates_path + flash[:danger] = '消息模版初始化失败' + end + end + + private + def message_template_params + params.require(@message_template.type.split("::").join("_").underscore.to_sym).permit! + end + + def get_template + @message_template = MessageTemplate.find_by(id: params[:id]) + unless @message_template.present? + redirect_to admins_message_templates_path + flash[:danger] = "消息模版不存在" + end + end +end \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 92c00fc6e..8ff81a465 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -247,11 +247,11 @@ class ApplicationController < ActionController::Base end def require_profile_completed - tip_exception(411, "请完善资料后再操作") unless User.current.profile_completed + tip_exception(411, "请完善资料后再操作") unless User.current.profile_is_completed? end def require_user_profile_completed(user) - tip_exception(412, "请用户完善资料后再操作") unless user.profile_completed + tip_exception(412, "请用户完善资料后再操作") unless user.profile_is_completed? end # 异常提醒 @@ -280,7 +280,7 @@ class ApplicationController < ActionController::Base # 资料是否完善 def check_account - if !current_user.profile_completed? + if !current_user. profile_is_completed? #info_url = '/account/profile' tip_exception(402, nil) end diff --git a/app/controllers/compare_controller.rb b/app/controllers/compare_controller.rb index bc81da563..6dc9baade 100644 --- a/app/controllers/compare_controller.rb +++ b/app/controllers/compare_controller.rb @@ -24,7 +24,7 @@ class CompareController < ApplicationController @exist_pullrequest = @project.pull_requests.where(is_original: false, head: @base, base: @head, status: 0).take end if @exist_pullrequest.present? - return -2, "在这些分支之间的合并请求已存在:#{@exist_pullrequest.try(:title)}" + return -2, "在这些分支之间的合并请求已存在:#{@exist_pullrequest.try(:title)}" else if @compare_result["Commits"].blank? && @compare_result["Diff"].blank? return -2, "分支内容相同,无需创建合并请求" diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 266d746d7..bfa15aa50 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -15,7 +15,7 @@ class IssuesController < ApplicationController include TagChosenHelper def index - @user_admin_or_member = current_user.present? && current_user.logged? && (current_user.admin || @project.member?(current_user)) + @user_admin_or_member = current_user.present? && current_user.logged? && (current_user.admin || @project.member?(current_user) || @project.is_public?) issues = @project.issues.issue_issue.issue_index_includes issues = issues.where(is_private: false) unless @user_admin_or_member @@ -111,6 +111,8 @@ class IssuesController < ApplicationController Issues::CreateForm.new({subject:issue_params[:subject]}).validate! @issue = Issue.new(issue_params) if @issue.save! + SendTemplateMessageJob.perform_later('IssueAssigned', current_user.id, @issue&.id) + SendTemplateMessageJob.perform_later('ProjectIssue', current_user.id, @issue&.id) if params[:attachment_ids].present? params[:attachment_ids].each do |id| attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id) @@ -158,6 +160,7 @@ class IssuesController < ApplicationController def update last_token = @issue.token last_status_id = @issue.status_id + @issue&.issue_tags_relates&.destroy_all if params[:issue_tag_ids].blank? if params[:issue_tag_ids].present? && !@issue&.issue_tags_relates.where(issue_tag_id: params[:issue_tag_ids]).exists? @issue&.issue_tags_relates&.destroy_all params[:issue_tag_ids].each do |tag| @@ -202,6 +205,20 @@ class IssuesController < ApplicationController issue_params = issue_send_params(params).except(:issue_classify, :author_id, :project_id) Issues::UpdateForm.new({subject:issue_params[:subject]}).validate! if @issue.update_attributes(issue_params) + if @issue&.pull_request.present? + SendTemplateMessageJob.perform_later('PullRequestChanged', current_user.id, @issue&.pull_request&.id, @issue.previous_changes.slice(:assigned_to_id, :priority_id, :fixed_version_id, :issue_tags_value)) + SendTemplateMessageJob.perform_later('PullRequestAssigned', current_user.id, @issue&.pull_request&.id ) if @issue.previous_changes[:assigned_to_id].present? + else + previous_changes = @issue.previous_changes.slice(:status_id, :assigned_to_id, :tracker_id, :priority_id, :fixed_version_id, :done_ratio, :issue_tags_value, :branch_name) + if @issue.previous_changes[:start_date].present? + previous_changes.merge!(start_date: [@issue.previous_changes[:start_date][0].to_s, @issue.previous_changes[:start_date][1].to_s]) + end + if @issue.previous_changes[:due_date].present? + previous_changes.merge!(due_date: [@issue.previous_changes[:due_date][0].to_s, @issue.previous_changes[:due_date][1].to_s]) + end + SendTemplateMessageJob.perform_later('IssueChanged', current_user.id, @issue&.id, previous_changes) + SendTemplateMessageJob.perform_later('IssueAssigned', current_user.id, @issue&.id) if @issue.previous_changes[:assigned_to_id].present? + end if params[:status_id].to_i == 5 #任务由非关闭状态到关闭状态时 @issue.issue_times.update_all(end_time: Time.now) @issue.update_closed_issues_count_in_project! @@ -253,6 +270,7 @@ class IssuesController < ApplicationController status_id = @issue.status_id token = @issue.token login = @issue.user.try(:login) + SendTemplateMessageJob.perform_later('IssueDeleted', current_user.id, @issue&.subject, @issue.assigned_to_id, @issue.author_id) if @issue.destroy if issue_type == "2" && status_id != 5 post_to_chain("add", token, login) @@ -272,8 +290,12 @@ class IssuesController < ApplicationController def clean #批量删除,暂时只能删除未悬赏的 issue_ids = params[:ids] - if issue_ids.present? - if Issue.where(id: issue_ids, issue_type: "1").destroy_all + issues = Issue.where(id: issue_ids, issue_type: "1") + if issues.present? + issues.find_each do |i| + SendTemplateMessageJob.perform_later('IssueDeleted', current_user.id, i&.subject, i.assigned_to_id, i.author_id) + end + if issues.destroy_all normal_status(0, "删除成功") else normal_status(-1, "删除失败") @@ -307,7 +329,18 @@ class IssuesController < ApplicationController if update_hash.blank? normal_status(-1, "请选择批量更新内容") elsif issues&.update(update_hash) - issues.map{|i| i.create_journal_detail(false, [], [], current_user&.id) if i.previous_changes.present?} + issues.each do |i| + i.create_journal_detail(false, [], [], current_user&.id) if i.previous_changes.present? + previous_changes = i.previous_changes.slice(:status_id, :assigned_to_id, :tracker_id, :priority_id, :fixed_version_id, :done_ratio, :issue_tags_value, :branch_name) + if i.previous_changes[:start_date].present? + previous_changes.merge!(start_date: [i.previous_changes[:start_date][0].to_s, i.previous_changes[:start_date][1].to_s]) + end + if i.previous_changes[:due_date].present? + previous_changes.merge!(due_date: [i.previous_changes[:due_date][0].to_s, i.previous_changes[:due_date][1].to_s]) + end + SendTemplateMessageJob.perform_later('IssueChanged', current_user.id, i&.id, previous_changes) + SendTemplateMessageJob.perform_later('IssueAssigned', current_user.id, i&.id) if i.previous_changes[:assigned_to_id].present? + end normal_status(0, "批量更新成功") else normal_status(-1, "批量更新失败") @@ -321,6 +354,8 @@ class IssuesController < ApplicationController @new_issue = @issue.dup @new_issue.author_id = current_user.id if @new_issue.save + SendTemplateMessageJob.perform_later('IssueAssigned', current_user.id, @new_issue&.id) + SendTemplateMessageJob.perform_later('ProjectIssue', current_user.id, @new_issue&.id) issue_tags = @issue.issue_tags.pluck(:id) if issue_tags.present? issue_tags.each do |tag| @@ -398,27 +433,27 @@ class IssuesController < ApplicationController def check_project_public unless @project.is_public || @project.member?(current_user) || current_user.admin? || (@project.user_id == current_user.id) - normal_status(-1, "您没有权限") + return render_forbidden end end def set_issue @issue = Issue.find_by_id(params[:id]) if @issue.blank? - normal_status(-1, "标签不存在") - elsif @issue.is_lock &&!(@project.member?(current_user) || current_user.admin?) - normal_status(-1, "您没有权限") + return render_not_found + elsif !(@project.is_public || (current_user.present? && (@project.member?(current_user) || current_user&.admin? || (@project.user_id == current_user&.id)))) + return render_forbidden end end def check_issue_permission unless @project.is_public || (current_user.present? && (@project.member?(current_user) || current_user&.admin? || (@project.user_id == current_user&.id))) - normal_status(-1, "您没有权限") + return render_forbidden end end def operate_issue_permission - return render_forbidden("您没有权限进行此操作.") unless current_user.admin? || @project.member?(current_user) + return render_forbidden("您没有权限进行此操作.") unless current_user.present? && current_user.logged? && (current_user.admin? || @project.member?(current_user) || @project.is_public?) end def export_issues(issues) diff --git a/app/controllers/main_controller.rb b/app/controllers/main_controller.rb index 7b7468f93..afe767897 100644 --- a/app/controllers/main_controller.rb +++ b/app/controllers/main_controller.rb @@ -23,9 +23,9 @@ class MainController < ApplicationController # TODO: 这块之后需要整合,者架构重新变化,统一跳转到index后再路由分发 if params[:path] && params[:path]&.include?("h5educoderbuild") && params[:path].split("/").first == "h5educoderbuild" - render file: 'public/h5educoderbuild/index.html', :layout => false + render file: 'public/h5educoderbuild/index.html', :layout => false, :content_type=> 'text/html' else - render file: 'public/react/build/index.html', :layout => false + render file: 'public/react/build/index.html', :layout => false, :content_type=> 'text/html' end end diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb index 7fb211e38..0af4898fc 100644 --- a/app/controllers/members_controller.rb +++ b/app/controllers/members_controller.rb @@ -9,6 +9,8 @@ class MembersController < ApplicationController def create interactor = Projects::AddMemberInteractor.call(@project.owner, @project, @user) + SendTemplateMessageJob.perform_later('ProjectJoined', current_user.id, @user.id, @project.id) + SendTemplateMessageJob.perform_later('ProjectMemberJoined', current_user.id, @user.id, @project.id) render_response(interactor) rescue Exception => e uid_logger_error(e.message) @@ -28,6 +30,8 @@ class MembersController < ApplicationController def remove interactor = Projects::DeleteMemberInteractor.call(@project.owner, @project, @user) + SendTemplateMessageJob.perform_later('ProjectLeft', current_user.id, @user.id, @project.id) + SendTemplateMessageJob.perform_later('ProjectMemberLeft', current_user.id, @user.id, @project.id) render_response(interactor) rescue Exception => e uid_logger_error(e.message) @@ -36,6 +40,7 @@ class MembersController < ApplicationController def change_role interactor = Projects::ChangeMemberRoleInteractor.call(@project.owner, @project, @user, params[:role]) + SendTemplateMessageJob.perform_later('ProjectRole', current_user.id, @user.id, @project.id, message_role_name) render_response(interactor) rescue Exception => e uid_logger_error(e.message) @@ -66,4 +71,14 @@ class MembersController < ApplicationController def check_user_profile_completed require_user_profile_completed(@user) end + + def message_role_name + case params[:role] + when 'Manager' then '管理员' + when 'Developer' then '开发者' + when 'Reporter' then '报告者' + else + '' + end + end end diff --git a/app/controllers/organizations/team_users_controller.rb b/app/controllers/organizations/team_users_controller.rb index 43ed10708..c63005260 100644 --- a/app/controllers/organizations/team_users_controller.rb +++ b/app/controllers/organizations/team_users_controller.rb @@ -18,6 +18,7 @@ class Organizations::TeamUsersController < Organizations::BaseController ActiveRecord::Base.transaction do @team_user = TeamUser.build(@organization.id, @operate_user.id, @team.id) @organization_user = OrganizationUser.build(@organization.id, @operate_user.id) + SendTemplateMessageJob.perform_later('OrganizationRole', @operate_user.id, @organization.id, @team.authorize_name) Gitea::Organization::TeamUser::CreateService.call(@organization.gitea_token, @team.gtid, @operate_user.login) end rescue Exception => e diff --git a/app/controllers/projects/project_units_controller.rb b/app/controllers/projects/project_units_controller.rb index e21fa388f..e8b8f67a1 100644 --- a/app/controllers/projects/project_units_controller.rb +++ b/app/controllers/projects/project_units_controller.rb @@ -6,7 +6,8 @@ class Projects::ProjectUnitsController < Projects::BaseController def create if current_user.admin? || @project.manager?(current_user) ActiveRecord::Base.transaction do - ProjectUnit.update_by_unit_types!(@project, unit_types) + before_units, after_units = ProjectUnit.update_by_unit_types!(@project, unit_types) + SendTemplateMessageJob.perform_later('ProjectSettingChanged', current_user.id, @project&.id, {navbar: true}) unless before_units.eql?(after_units) render_ok end else diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 325d76ccb..bea2b429c 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -4,7 +4,7 @@ class ProjectsController < ApplicationController include ProjectsHelper include Acceleratorable - before_action :require_login, except: %i[index branches group_type_list simple show fork_users praise_users watch_users recommend about menu_list] + before_action :require_login, except: %i[index branches branches_slice group_type_list simple show fork_users praise_users watch_users recommend about menu_list] before_action :require_profile_completed, only: [:create, :migrate] before_action :load_repository, except: %i[index group_type_list migrate create recommend] before_action :authorizate_user_can_edit_project!, only: %i[update] @@ -86,6 +86,13 @@ class ProjectsController < ApplicationController @branches = result.is_a?(Hash) && result.key?(:status) ? [] : result end + def branches_slice + return @branches = [] unless @project.forge? + + slice_result = Gitea::Repository::Branches::ListSliceService.call(@owner, @project.identifier) + @branches_slice = slice_result.is_a?(Hash) && slice_result.key?(:status) ? [] : slice_result + end + def group_type_list project_statics = ProjectStatistic.first @@ -139,6 +146,7 @@ class ProjectsController < ApplicationController @project.repository.update_column(:hidden, private) end end + SendTemplateMessageJob.perform_later('ProjectSettingChanged', current_user.id, @project&.id, @project.previous_changes.slice(:name, :description, :project_category_id, :project_language_id, :is_public)) end rescue Exception => e uid_logger_error(e.message) diff --git a/app/controllers/pull_requests_controller.rb b/app/controllers/pull_requests_controller.rb index 161810474..fe04dd5e5 100644 --- a/app/controllers/pull_requests_controller.rb +++ b/app/controllers/pull_requests_controller.rb @@ -58,8 +58,9 @@ class PullRequestsController < ApplicationController ActiveRecord::Base.transaction do @pull_request, @gitea_pull_request = PullRequests::CreateService.call(current_user, @owner, @project, params) if @gitea_pull_request[:status] == :success - @pull_request.bind_gitea_pull_request!(@gitea_pull_request[:body]["number"], @gitea_pull_request[:body]["id"]) - render_ok + @pull_request.bind_gitea_pull_request!(@gitea_pull_request[:body]["number"]) + SendTemplateMessageJob.perform_later('PullRequestAssigned', current_user.id, @pull_request&.id) + SendTemplateMessageJob.perform_later('ProjectPullRequest', current_user.id, @pull_request&.id) else render_error("create pull request error: #{@gitea_pull_request[:status]}") raise ActiveRecord::Rollback @@ -77,12 +78,13 @@ class PullRequestsController < ApplicationController if params[:title].nil? normal_status(-1, "名称不能为空") elsif params[:issue_tag_ids].nil? - normal_status(-1, "标签不能为空") + normal_status(-1, "标记不能为空") else ActiveRecord::Base.transaction do begin merge_params + @issue&.issue_tags_relates&.destroy_all if params[:issue_tag_ids].blank? if params[:issue_tag_ids].present? && !@issue&.issue_tags_relates.where(issue_tag_id: params[:issue_tag_ids]).exists? @issue&.issue_tags_relates&.destroy_all params[:issue_tag_ids].each do |tag| @@ -116,6 +118,8 @@ class PullRequestsController < ApplicationController normal_status(-1, e.message) raise ActiveRecord::Rollback end + SendTemplateMessageJob.perform_later('PullRequestChanged', current_user.id, @pull_request&.id, @issue.previous_changes.slice(:assigned_to_id, :priority_id, :fixed_version_id, :issue_tags_value)) + SendTemplateMessageJob.perform_later('PullRequestAssigned', current_user.id, @pull_request&.id ) if @issue.previous_changes[:assigned_to_id].present? end end @@ -125,7 +129,12 @@ class PullRequestsController < ApplicationController ActiveRecord::Base.transaction do begin colsed = PullRequests::CloseService.call(@owner, @repository, @pull_request, current_user) - colsed === true ? normal_status(1, "已拒绝") : normal_status(-1, '合并失败') + if colsed === true + SendTemplateMessageJob.perform_later('PullRequestClosed', current_user.id, @pull_request.id) + normal_status(1, "已拒绝") + else + normal_status(-1, '合并失败') + end rescue => e normal_status(-1, e.message) raise ActiveRecord::Rollback @@ -164,6 +173,7 @@ class PullRequestsController < ApplicationController if success_condition && @pull_request.merge! @pull_request.project_trend_status! @issue&.custom_journal_detail("merge", "", "该合并请求已被合并", current_user&.id) + SendTemplateMessageJob.perform_later('PullRequestMerged', current_user.id, @pull_request.id) normal_status(1, "合并成功") else normal_status(-1, result.message) @@ -190,7 +200,7 @@ class PullRequestsController < ApplicationController if can_merge.present? render json: { status: -2, - message: "在这些分支之间的合并请求已存在:#{can_merge.first.try(:title)}", + message: "在这些分支之间的合并请求已存在:#{can_merge.first.try(:title)}", } else normal_status(0, "可以合并") diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index d9fba6ef4..f9d949db5 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -1,4 +1,5 @@ class RepositoriesController < ApplicationController + include RepositoriesHelper include ApplicationHelper include OperateProjectAbilityAble include Repository::LanguagesPercentagable @@ -54,16 +55,6 @@ class RepositoriesController < ApplicationController @entries = Gitea::Repository::Entries::ListService.new(@owner, @project.identifier, ref: @ref).call @entries = @entries.present? ? @entries.sort_by{ |hash| hash['type'] } : [] @path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/" - - # TODO - # 临时处理readme文件问题 - result = Gitea::Repository::Readme::GetService.call(@owner.login, @project.identifier, @ref, @owner&.gitea_token) - @readme = - if result[:status] == :success - result[:body] - else - {} - end end end @@ -73,6 +64,7 @@ class RepositoriesController < ApplicationController def sub_entries file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip)) + @path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/" if @project.educoder? if params[:type] === 'file' @@ -103,10 +95,21 @@ class RepositoriesController < ApplicationController end def commits - @hash_commit = Gitea::Repository::Commits::ListService.new(@owner.login, @project.identifier, - sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call + if params[:filepath].present? + file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip)) + @hash_commit = Gitea::Repository::Commits::FileListService.new(@owner.login, @project.identifier, file_path_uri, + sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call + else + @hash_commit = Gitea::Repository::Commits::ListService.new(@owner.login, @project.identifier, + sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call + end end + def commits_slice + @hash_commit = Gitea::Repository::Commits::ListSliceService.call(@owner.login, @project.identifier, + sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token) + end + def commit @sha = params[:sha] @commit = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token) @@ -120,7 +123,11 @@ class RepositoriesController < ApplicationController end def contributors - @contributors = Gitea::Repository::Contributors::GetService.call(@owner, @repository.identifier) + if params[:filepath].present? + @contributors = [] + else + @contributors = Gitea::Repository::Contributors::GetService.call(@owner, @repository.identifier) + end end def edit @@ -183,10 +190,16 @@ class RepositoriesController < ApplicationController end def readme - result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token) - + if params[:filepath].present? + result = Gitea::Repository::Readme::DirService.call(@owner.login, @repository.identifier, params[:filepath], params[:ref], current_user&.gitea_token) + else + result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token) + end @readme = result[:status] === :success ? result[:body] : nil - render json: @readme + @readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref]) + render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha") + rescue + render json: nil end def languages @@ -214,7 +227,7 @@ class RepositoriesController < ApplicationController file_path = [domain, api_url, url].join file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("&") if @repository.hidden? - redirect_to file_path + redirect_to URI.escape(file_path) end private @@ -237,8 +250,14 @@ class RepositoriesController < ApplicationController # TODO 获取最新commit信息 def project_commits - Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier, - sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call + if params[:filepath].present? + file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip)) + Gitea::Repository::Commits::FileListService.new(@project.owner.login, @project.identifier, file_path_uri, + sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call + else + Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier, + sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call + end end def get_statistics diff --git a/app/controllers/users/messages_controller.rb b/app/controllers/users/messages_controller.rb new file mode 100644 index 000000000..035441ccb --- /dev/null +++ b/app/controllers/users/messages_controller.rb @@ -0,0 +1,96 @@ +class Users::MessagesController < Users::BaseController + before_action :private_user_resources! + before_action :find_receivers, only: [:create] + + def index + limit = params[:limit] || params[:per_page] + limit = (limit.to_i.zero? || limit.to_i > 15) ? 15 : limit.to_i + page = params[:page].to_i.zero? ? 1 : params[:page].to_i + result = Notice::Read::ListService.call(observed_user.id, message_type, message_status, page, limit) + return render_error if result.nil? + @data = result[2] + end + + def create + return render_forbidden unless %w(atme).include?(params[:type]) + case params[:type] + when 'atme' + Notice::Write::CreateAtmeForm.new(atme_params).validate! + case atme_params[:atmeable_type] + when 'Issue' + SendTemplateMessageJob.perform_now('IssueAtme', @receivers, current_user.id, atme_params[:atmeable_id]) + when 'PullRequest' + SendTemplateMessageJob.perform_now('PullRequestAtme', @receivers, current_user.id, atme_params[:atmeable_id]) + when 'Journal' + journal = Journal.find_by_id(atme_params[:atmeable_id]) + if journal.present? + if journal&.issue&.pull_request.present? + SendTemplateMessageJob.perform_now('PullRequestAtme', @receivers, current_user.id, atme_params[:atmeable_id]) + else + SendTemplateMessageJob.perform_now('IssueAtme', @receivers, current_user.id, atme_params[:atmeable_id]) + end + end + end + end + render_ok + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + def delete + return render_forbidden unless %w(atme).include?(params[:type]) + result = Notice::Write::DeleteService.call(params[:ids], observed_user.id, message_type) + return render_error if result.nil? + + render_ok + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + def read + return render_forbidden unless %w(notification atme).include?(params[:type]) + result = Notice::Write::ChangeStatusService.call(params[:ids], observed_user.id, message_type) + if result.nil? + render_error + else + render_ok + end + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + private + def message_type + @message_type = begin + case params[:type] + when "notification" then 1 + when "atme" then 2 + else + -1 + end + end + end + + def message_status + @message_status = begin + case params[:status] + when "1" then 1 + when "2" then 2 + else + -1 + end + end + end + + def atme_params + params.permit(:atmeable_type, :atmeable_id, receivers_login: []) + end + + def find_receivers + @receivers = User.where(login: params[:receivers_login]) + return render_not_found if @receivers.size == 0 + end +end \ No newline at end of file diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index c9161a42a..7890ee864 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -91,6 +91,12 @@ class UsersController < ApplicationController def get_user_info begin @user = current_user + begin + result = Notice::Read::CountService.call(current_user.id) + @message_unread_total = result.nil? ? 0 : result[2]["unread_notification"] + rescue + @message_unread_total = 0 + end # TODO 等消息上线再打开注释 #@tidding_count = unviewed_tiddings(current_user) if current_user.present? rescue Exception => e diff --git a/app/controllers/version_releases_controller.rb b/app/controllers/version_releases_controller.rb index 618eb0735..2d7546a1c 100644 --- a/app/controllers/version_releases_controller.rb +++ b/app/controllers/version_releases_controller.rb @@ -1,14 +1,14 @@ class VersionReleasesController < ApplicationController before_action :load_repository before_action :set_user - before_action :require_login, except: [:index] - before_action :find_version , only: [:edit, :update, :destroy] + before_action :require_login, except: [:index, :show] + before_action :check_release_authorize, except: [:index, :show] + before_action :find_version , only: [:show, :edit, :update, :destroy] def index - version_releases = Gitea::Versions::ListService.new(@user.gitea_token, @user.try(:login), @repository.try(:identifier)).call - @version_releases = version_releases + @version_releases = kaminari_paginate(@repository.version_releases.order(created_at: :desc)) @user_permission = current_user.present? && (@repository.project.all_developers.include?(current_user) || current_user.admin?) - @forge_releases = @repository.version_releases.select(:id,:version_gid, :created_at).includes(:attachments) + @user_admin_permission = current_user.present? && (@repository.project.all_managers.include?(current_user) || current_user.admin?) end def new @@ -22,6 +22,10 @@ class VersionReleasesController < ApplicationController end end + def show + # @release = Gitea::Versions::GetService.call(current_user.gitea_token, @user&.login, @repository&.identifier, @version&.version_gid) + end + def create if params[:name].nil? normal_status(-1, "名称不能为空") @@ -37,13 +41,14 @@ class VersionReleasesController < ApplicationController version_params = releases_params version_release = VersionRelease.new(version_params.merge(user_id: current_user.id, repository_id: @repository.id)) if version_release.save! - git_version_release = Gitea::Versions::CreateService.new(@user.gitea_token, @user.try(:login), @repository.try(:identifier), version_params).call + git_version_release = Gitea::Versions::CreateService.new(current_user.gitea_token, @user.try(:login), @repository.try(:identifier), version_params).call if git_version_release update_params = { tarball_url: git_version_release["tarball_url"], zipball_url: git_version_release["zipball_url"], url: git_version_release["url"], version_gid: git_version_release["id"], + sha: git_version_release["sha"] } version_release.update_attributes!(update_params) version_release.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create") @@ -81,7 +86,7 @@ class VersionReleasesController < ApplicationController if @version.update_attributes!(version_params) create_attachments(params[:attachment_ids], @version) if params[:attachment_ids].present? - git_version_release = Gitea::Versions::UpdateService.new(@user.gitea_token, @user.try(:login), @repository.try(:identifier), version_params, @version.try(:version_gid)).call + git_version_release = Gitea::Versions::UpdateService.new(current_user.gitea_token, @user.try(:login), @repository.try(:identifier), version_params, @version.try(:version_gid)).call unless git_version_release raise Error, "更新失败" end @@ -102,7 +107,7 @@ class VersionReleasesController < ApplicationController ActiveRecord::Base.transaction do begin if @version.destroy - git_version_release = Gitea::Versions::DeleteService.new(@user.gitea_token, @user.try(:login), @repository.try(:identifier), @version.try(:version_gid)).call + git_version_release = Gitea::Versions::DeleteService.new(current_user.gitea_token, @user.try(:login), @repository.try(:identifier), @version.try(:version_gid)).call if git_version_release.status == 204 normal_status(0, "删除成功") @@ -157,4 +162,8 @@ class VersionReleasesController < ApplicationController end end + def check_release_authorize + return render_forbidden("您没有权限进行此操作.") unless current_user.admin? || @project.manager?(current_user) + end + end diff --git a/app/docs/slate/source/includes/_pulls.md b/app/docs/slate/source/includes/_pulls.md index 1df50f6bf..b9ad3b0ba 100644 --- a/app/docs/slate/source/includes/_pulls.md +++ b/app/docs/slate/source/includes/_pulls.md @@ -1 +1,502 @@ # Pulls + + +## Get a pull request +获取合并请求详情接口 + +> 示例: + +```shell +curl -X GET http://localhost:3000/api/Jasder/gitlink/pulls/88.json +``` + +```javascript +await octokit.request('GET /api/Jasder/gitlink/pulls/88.json') +``` + +### HTTP 请求 +`GET /api/:owner/:repo/pulls/:id.json` + +### 请求参数: +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|owner |是| |string |用户登录名 | +|repo |是| |string |项目标识identifier | +|id |是| | integer | pull id值 | + + + + +> 返回的JSON示例: + +```json +{ + "status": 0, + "message": "响应成功", + "project_name": "Gitlink", + "identifier": "forgeplus", + "project_identifier": "forgeplus", + "pr_time": "52分钟前", + "commits_count": 229, + "files_count": 328, + "comments_count": 0, + "comments_total_count": 0, + "pull_request": { + "id": 1189, + "base": "master", + "head": "develop", + "status": 0, + "fork_project_id": null, + "is_original": false, + "pull_request_staus": "open", + "fork_project_user": null, + "create_user": "jasder", + "mergeable": true, + "state": "open" + }, + "issue": { + "id": 51888, + "subject": "FIx release v3.2.0", + "description": null, + "is_private": false, + "branch_name": null, + "project_author_name": "Gitlink", + "closed_on": "", + "created_at": "2021-10-12 15:51", + "assign_user_name": "victor", + "assign_user_login": "moshenglv", + "author_name": "段甲生", + "author_login": "jasder", + "author_picture": "images/avatars/User/36480?t=1615520120", + "issue_status": "新增", + "priority": "正常", + "version": null, + "issue_tags": null + }, + "conflict_files": [] +} +``` + +## 获取pull request文件列表 +获取pull request文件列表 + +> 示例: + +```shell +curl -X GET \ +http://localhost:3000/api/Jason/gitlink/pulls/1/files.json +``` + +```javascript +await octokit.request('GET /api/jasder/gitlink/pulls/1/files.json') +``` + +### HTTP 请求 +`GET /api/:owner/:repo/pulls/:id/files.json` + +### 请求参数: +|参数名|必选|类型|说明| +|-|-|-|-| +|owner |是|string |用户登录名 | +|repo |是|string |project's identifier | +|id |是|int |pull request's id | + + +### 返回字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|-|-|-| +|files_count |int|文件更改的总数量| +|total_addition |int|添加代码总行数| +|total_deletion |int|删除代码总行数| +|files |array|| +|-- sha |string|commit's sha value| +|-- name |string|当前文件名| +|-- old_name |string| 修改之前的文件名称,与name相同的话,说明文件名未更改| +|-- addition |int|文件添加的行数| +|-- deletion |int|文件删除的行数| +|-- type |int|文件类型, 1: 表示该文件只添加了内容,2: 表示该文件内容有修改, 3: 表示文件被删除或者改文件只删除了内容| +|-- isCreated |boolean|当前文件是否为新增文件, true: 是, false: 否| +|-- isDeleted |boolean|当前文件是否被删除, true: 是,false: 否| +|-- isBin |boolean|当前文件是否为二进制文件,true: 是,false: 否| +|-- isLFSFile |boolean|当前文件是否为LFS文件,true: 是,false: 否| +|-- isRenamed |boolean|当前文件是否被重命名,true: 是,false: 否| +|-- sections |array|| +|---- fileName |string|文件名称| +|---- lines |array|| +|------ leftIdx |string|文件变动之前所在行数| +|------ rightIdx |string|文件更改后所在行数| +|------ type |string|文件变更类型,1: 新增,2: 修改, 3: 删除, 4: diff统计信息| +|------ content |string|文件变更的内容| +|------ sectionInfo |object|| +|-------- path |string|文件相对仓库的路径| +|-------- lastLeftIdx |int|| +|-------- lastRightIdx |int|| +|-------- leftHunkSize |int|文件变更之前的行数| +|-------- rightHunkSize |int|文件变更之后的行数(及当前页面编辑器显示的总行数)| +|-------- leftIdx |int|文件变更之前所在行数| +|-------- rightIdx |int|文件变更之后所在行数(即:页面编辑器开始显示的行数)| + + + +> 返回的JSON示例: + +```json +{ + "files_count": 6, + "total_addition": 447, + "total_deletion": 0, + "files": [ + { + "sha": "xefenisnii", + "name": "文件.txt", + "old_name": "文件.txt", + "index": 6, + "addition": 2, + "deletion": 0, + "type": 1, + "isCreated": true, + "isDeleted": false, + "isBin": false, + "isLFSFile": false, + "isRenamed": false, + "isSubmodule": false, + "sections": [ + { + "fileName": "文件.txt", + "name": "", + "lines": [ + { + "leftIdx": 0, + "rightIdx": 0, + "type": 4, + "content": "@@ -0,0 +1,2 @@", + "sectionInfo": { + "path": null, + "lastLeftIdx": null, + "lastRightIdx": null, + "leftIdx": 0, + "rightIdx": 0, + "leftHunkSize": null, + "rightHunkSize": null + } + }, + { + "leftIdx": 0, + "rightIdx": 1, + "type": 2, + "content": "+用例图一致性更新", + "sectionInfo": null + }, + { + "leftIdx": 0, + "rightIdx": 2, + "type": 2, + "content": "+工程文件直接上传会有文件缺失,现在压缩后上传", + "sectionInfo": null + } + ] + } + ] + } + ] +} +``` + + +## 获取pull request的commits列表 +获取pull request的commits列表 + +> 示例: + +```shell +curl -X GET http://localhost:3000/api/jasder/jasder_test/pulls/1/commits.json +``` + +```javascript +await octokit.request('GET /api/jasder/jasder_test/pulls/1/commits.json') +``` + +### HTTP 请求 +`GET /api/:owner/:repo/pulls/:id/commits.json` + +### 请求参数: +|参数名|必选|类型|说明| +|-|-|-|-| +|owner |是|string |用户登录名 | +|repo |是|string |project's identifier | +|id |是|int |pull request's id | + + +### 返回字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|commits_count |int|commits总数量| +|commits |array|| +|-- author |object|项目作者| +|---- login |string|用户login| +|---- name |string|用户姓名| +|---- image_url |string|用户头像| +|-- committer |object|commit提交用户| +|---- login |string|用户login| +|---- name |string|用户姓名| +|---- image_url |string|用户头像| +|-- timestamp |int|commit的unix时间戳| +|-- time_from_now|string|commit’s 提交时间距当前时间的时间值| +|-- message |string|commit说明信息| +|-- sha |string|commit’s sha值| + + +> 返回的JSON示例: + +```json +{ + "commits_count": 1, + "commits": [ + { + "author": { + "id": 36480, + "login": "jasder", + "name": "段甲生", + "image_url": "avatars/User/b" + }, + "committer": { + "id": 36480, + "login": "jasder", + "name": "段甲生", + "image_url": "avatars/User/b" + }, + "timestamp": 1604382982, + "time_from_now": "3小时前", + "message": "add some file\n* Add the tag list page to the release page\n* Apply suggestions from code review\n* Add the tags list view\n* Add the delete tag way on ui\n* Not delete tag and clear message when delete a release\n", + "sha": "8f5faee0d3b3be1b8063e84da0c79dd75327b968" + } + ] +} +``` + +## Compare two commits +Compare two commits + +> 示例: + +```shell +curl -X GET \ +http://localhost:3000/api/Jason/test-txt/compare/master...develop + +curl -X GET \ +http://localhost:3000/api/Jason/test-txt/compare/master...Jason/test-txt:develop +``` + +```javascript +await octokit.request('GET /api/Jason/test-txt/compare/master...Jason/test-txt:develop') +``` + +### HTTP 请求 +`GET /api/:owner/:repo/compare/{base}...{head}.json` + +### 请求参数: +|参数名|必选|类型|说明| +|-|-|-|-| +|owner |是|string |用户登录名 | +|repo |是|string |project's identifier | +|base |是|string |pull request's id | +|head |是|string |pull request's id | + + +### 返回字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|-|-|-| +|commits_count |int|commits总数量| +|commits |array|| +|-- author |object|项目作者| +|---- login |string|用户login| +|---- name |string|用户姓名| +|---- image_url |string|用户头像| +|-- committer |object|commit提交用户| +|---- login |string|用户login| +|---- name |string|用户姓名| +|---- image_url |string|用户头像| +|-- timestamp |int|commit的unix时间戳| +|-- time_from_now|string|commit’s 提交时间距当前时间的时间值| +|-- message |string|commit说明信息| +|-- sha |string|commit’s sha值| +|diff |object|| +|-- files_count |int|文件更改的总数量| +|-- total_addition |int|添加代码总行数| +|-- total_deletion |int|删除代码总行数| +|-- files |Array|| +|-- sha |string|commit's sha | +|-- name |string|当前文件名| +|-- old_name |string| 修改之前的文件名称,与name相同的话,说明文件名未更改| +|-- addition |int|文件添加的行数| +|-- deletion |int|文件删除的行数| +|-- type |int|文件类型, 1: 表示该文件只添加了内容,2: 表示该文件内容有修改, 3: 表示文件被删除或者改文件只删除了内容| +|-- isCreated |boolean|当前文件是否为新增文件, true: 是, false: 否| +|-- isDeleted |boolean|当前文件是否被删除, true: 是,false: 否| +|-- isBin |boolean|当前文件是否为二进制文件,true: 是,false: 否| +|-- isLFSFile |boolean|当前文件是否为LFS文件,true: 是,false: 否| +|-- isRenamed |boolean|当前文件是否被重命名,true: 是,false: 否| +|-- sections |array|| +|---- fileName |string|文件名称| +|---- lines |array|| +|------ leftIdx |string|文件变动之前所在行数| +|------ rightIdx |string|文件更改后所在行数| +|------ type |string|文件变更类型,1: 内容未改动,2: 添加, 3: 删除, 4: diff统计信息| +|------ content |string|文件变更的内容| +|------ sectionInfo |object|| +|-------- path |string|文件相对仓库的路径| +|-------- lastLeftIdx |int|| +|-------- lastRightIdx |int|| +|-------- leftHunkSize |int|文件变更之前的行数| +|-------- rightHunkSize |int|文件变更之后的行数(及当前页面编辑器显示的总行数)| +|-------- leftIdx |int|文件变更之前所在行数| +|-------- rightIdx |int|文件变更之后所在行数| + +> 返回的JSON示例: + +```json +{ + "commits_count": 1, + "commits": [ + { + "author": { + "id": 36480, + "login": "jasder", + "name": "段甲生", + "image_url": "avatars/User/b" + }, + "committer": { + "id": 36480, + "login": "jasder", + "name": "段甲生", + "image_url": "avatars/User/b" + }, + "timestamp": 1604382982, + "time_from_now": "4小时前", + "message": "add some file\n* Add the tag list page to the release page\n* Apply suggestions from code review\n* Add the tags list view\n* Add the delete tag way on ui\n* Not delete tag and clear message when delete a release\n", + "sha": "8f5faee0d3b3be1b8063e84da0c79dd75327b968" + } + ], + "diff": { + "files_count": 6, + "total_addition": 447, + "total_deletion": 0, + "files": [ + { + "name": "build.go", + "old_name": "build.go", + "index": 1, + "addition": 33, + "deletion": 0, + "type": 1, + "isCreated": true, + "isDeleted": false, + "isBin": false, + "isLFSFile": false, + "isRenamed": false, + "isSubmodule": false, + "sections": [ + { + "fileName": "build.go", + "name": "", + "lines": [ + { + "leftIdx": 0, + "rightIdx": 0, + "type": 4, + "content": "@@ -0,0 +1,33 @@", + "sectionInfo": { + "path": "build.go", + "lastLeftIdx": 0, + "lastRightIdx": 0, + "leftIdx": 0, + "rightIdx": 1, + "leftHunkSize": 0, + "rightHunkSize": 33 + } + }, + { + "leftIdx": 0, + "rightIdx": 1, + "type": 2, + "content": "+// Copyright 2020 The Gitea Authors. All rights reserved.", + "sectionInfo": null + } + ] + } + ] + } + ] + } +``` + + +## List pull requests +获取合并请求列表 + +> 示例: + +```shell +curl -X GET http://localhost:3000/api/Jasder/gitlink/pulls.json +``` + +```javascript +await octokit.request('GET /api/Jasder/gitlink/pulls.json') +``` + +### HTTP 请求 +`GET /api/:owner/:repo/pulls.json` + +### 请求参数: +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|owner |是| |string |用户登录名 | +|repo |是| |string |项目标识identifier | + + + + +> 返回的JSON示例: + +```json +{ + "status": 0, + "message": "响应成功", + "open_count": 4, + "close_count": 51, + "merged_issues_size": 123, + "search_count": 4, + "limit": null, + "user_admin_or_member": true, + "user_admin_or_developer": true, + "project_name": "Gitlink", + "project_author_name": "Gitlink", + "issues": [ + { + "pull_request_id": 1189, + "pull_request_status": 0, + "pull_request_head": "develop", + "pull_request_base": "master", + "pull_request_staus": "open", + "is_original": false, + "fork_project_id": null, + "fork_project_identifier": null, + "fork_project_user": null, + "id": 51888, + "name": "FIx release v3.2.0", + "pr_time": "59分钟前", + "assign_user_name": "victor", + "assign_user_login": "moshenglv", + "author_name": "段甲生", + "author_login": "jasder", + "avatar_url": "images/avatars/User/36480?t=1615520120", + "priority": "正常", + "version": null, + "journals_count": 0, + "issue_tags": null + } + ] +} +``` \ No newline at end of file diff --git a/app/docs/slate/source/includes/_repositories.md b/app/docs/slate/source/includes/_repositories.md index 1552655e4..3604689c8 100644 --- a/app/docs/slate/source/includes/_repositories.md +++ b/app/docs/slate/source/includes/_repositories.md @@ -274,6 +274,124 @@ await octokit.request('GET /api/yystopf/ceshi/detail.json') } ``` +## 仓库标签列表 +仓库标签列表 + +> 示例: + +```shell +curl -X GET http://localhost:3000/api/yystopf/csfjkkj/tags.json +``` + +```javascript +await octokit.request('GET /api/yystopf/csfjkkj/tags.json') +``` + +### HTTP 请求 +`GET /api/:owner/:repo/tags.json` + +### 请求参数: +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|owner |是| |string |用户登录名 | +|repo |是| |string |项目标识identifier | +|page |否| 1 | integer | 页码 | +|limit |否| 20| integer | 每页个数 | + + +### 返回字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|id |int |标签id | +|name |string|标签名称| +|zipball_url |string|标签zip包下载地址| +|tarball_url |string|标签tar包下载地址| +|tagger |object|打标签的人| +|time_ago |string|打标签的时间| +|created_at_unix|string|打标签的时间戳| +|message |string|标签信息| +|commit |object|标签最后一个commit| +|commit.sha |string|commit的id| +|commit.message |string|commit的提交信息| +|commit.time_ago|string|commit的提交时间| +|commit.created_at_unix|string|commit的提交时间戳| +|commit.committer|object|commit的提交者| +|commit.author|object|commit的作者| + + +> 返回的JSON示例: + +```json +[ + { + "name": "v2.0.0", + "id": "c7d0873ee41796d1a0e193063095ccf539a9bf31", + "zipball_url": "http://localhost:3000/api/yystopf/csfjkkj/archive/v2.0.0.zip", + "tarball_url": "http://localhost:3000/api/yystopf/csfjkkj/archive/v2.0.0.tar.gz", + "tagger": { + "id": 4, + "login": "testforge1", + "name": "testforge1", + "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png" + }, + "time_ago": "1天前", + "created_at_unix": 1632376903, + "message": "jdfkls", + "commit": { + "sha": "08fe383f1e5ebe2e2a384a8ea3ee890a758c7cd7", + "message": "add\n", + "time_ago": "1天前", + "created_at_unix": 1632376186, + "committer": { + "id": 4, + "login": "testforge1", + "name": "testforge1", + "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png" + }, + "author": { + "id": 4, + "login": "testforge1", + "name": "testforge1", + "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png" + } + } + }, + { + "name": "v1.0.0", + "id": "12168ad39c3ef201a445a2db181a3e43d50e40dd", + "zipball_url": "http://localhost:3000/api/yystopf/csfjkkj/archive/v1.0.0.zip", + "tarball_url": "http://localhost:3000/api/yystopf/csfjkkj/archive/v1.0.0.tar.gz", + "tagger": { + "id": null, + "login": "viletyy", + "name": "viletyy", + "image_url": "system/lets/letter_avatars/2/V/39_141_222/120.png" + }, + "time_ago": "10天前", + "created_at_unix": 1631588042, + "message": "dfks", + "commit": { + "sha": "5291b5e45a377c1f7710cc6647259887ed7aaccf", + "message": "ADD file via upload\n", + "time_ago": "21天前", + "created_at_unix": 1630648417, + "committer": { + "id": null, + "login": "yystopf", + "name": "yystopf", + "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png" + }, + "author": { + "id": null, + "login": "yystopf", + "name": "yystopf", + "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png" + } + } + } +] +``` + ## 编辑仓库信息 编辑仓库信息 @@ -868,6 +986,128 @@ await octokit.request('GET /api/jasder/jasder_test/sub_entries.json') Success Data. +## 获取仓库README文件 +获取仓库README文件 + +> 示例: + +```shell +curl -X GET \ +-d "ref=master" \ +-d "filepath=lib" \ +http://localhost:3000/api/yystopf/csfjkkj/readme.json +``` + +```javascript +await octokit.request('GET /api/yystopf/csfjkkj/readme.json') +``` + +### HTTP 请求 +`GET /api/:owner/:repo/readme.json` + +### 请求参数: +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|owner |是| |string |用户登录名 | +|repo |是| |string |项目标识identifier | +|ref |否| | string |分支名称、tag名称或是提交记录id,默认为默认分支 | +|filepath |否| | string |子目录名称,默认为空 | + +### 返回字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|type |string|文件类型, file:文件,dir:文件目录 +|encoding |string |编码 | +|size |int|文件夹或文件大小 单位B +|name |string|文件夹或文件名称| +|path |string|文件夹或文件相对路径| +|content |string|文件内容 +|sha |string|文件commitid + + +> 返回的JSON示例: + +```json +{ + "type": "file", + "encoding": "base64", + "size": 24, + "name": "README.md", + "path": "lib/README.md", + "content": "ZGZhc2RhZGpmIGRrZnNsCgpzZGZkZnMK", + "sha": "860962cd21c60b1a9e07d723080c87c32c18d44a" +} +``` + + +## 获取仓库贡献者 +获取仓库贡献者 + +> 示例: + +```shell +curl -X GET \ +-d "ref=master" \ +-d "filepath=lib" \ +http://localhost:3000/api/yystopf/csfjkkj/contributors.json +``` + +```javascript +await octokit.request('GET /api/yystopf/csfjkkj/contributors.json') +``` + +### HTTP 请求 +`GET /api/:owner/:repo/contributors.json` + +### 请求参数: +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|owner |是| |string |用户登录名 | +|repo |是| |string |项目标识identifier | +|ref |否| | string |分支名称、tag名称或是提交记录id,默认为整个仓库 | +|filepath |否| | string |子目录名称,默认为空 | + +### 返回字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|total_count |integer|贡献者数量| +|contributions |integer|贡献数量| +|login |string |用户登录名 | +|type |string|用户类型 | +|name |string|用户昵称| +|image_url |string|用户头像| + + +> 返回的JSON示例: + +```json +{ + "contributors": [ + { + "contributions": 5, + "login": "testforge2", + "type": "User", + "name": "testforge2", + "image_url": "system/lets/letter_avatars/2/T/236_177_85/120.png" + }, + { + "contributions": 79, + "login": "yystopf", + "type": "User", + "name": "yystopf", + "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png" + } + ], + "total_count": 2 +} +``` + + + ## 获取仓库webhooks列表 获取仓库webhooks列表 diff --git a/app/docs/slate/source/includes/_users.md b/app/docs/slate/source/includes/_users.md index bb4fd2b66..9d6b80927 100644 --- a/app/docs/slate/source/includes/_users.md +++ b/app/docs/slate/source/includes/_users.md @@ -1,7 +1,7 @@ # Users @@ -47,6 +47,275 @@ await octokit.request('GET /api/users/me.json') Success Data. +## 用户消息列表 +获取用户消息列表 + +> 示例: + +```shell +curl -X GET http://localhost:3000/api/users/:login/messages.json +``` + +```javascript +await octokit.request('GET /api/users/:login/messages.json') +``` + +### HTTP 请求 +`GET api/users/yystopf/messages.json` + +### 请求字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|type | string | 消息类型,不传为所有消息,notification为系统消息,atme为@我消息| +|status | integer | 是否已读,不传为所有消息,1为未读,2为已读 | +|limit | integer | 每页个数 | +|page | integer | 页码 | + +### 返回字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|total_count | integer | 消息总数 | +|type | string | 消息类型 | +|unread_notification | integer | 未读系统通知数量 | +|unread_atme | integer | 未读@我数量 | +|messages.id | integer | 消息id | +|messages.status | integer | 消息是否已读,1为未读,2为已读 | +|messages.content | string | 消息内容 | +|messages.notification_url | string | 消息跳转地址 | +|messages.source | string | 消息来源 | +|messages.timeago | string | 消息时间 | +|messages.type | string | 消息类型,notification为系统消息,atme为@我消息| +|sender | object | 消息发送者 | + +#### 消息来源source字段说明 +类型|说明 +--------- | ----------- +|IssueAssigned | 有新指派给我的易修 | +|IssueAssignerExpire | 我负责的易修截止日期到达最后一天 | +|IssueAtme | 在易修中@我 | +|IssueChanged | 我创建或负责的易修状态变更 | +|IssueCreatorExpire | 我创建的易修截止日期到达最后一天 | +|IssueDeleted | 我创建或负责的易修删除 | +|IssueJournal | 我创建或负责的易修有新的评论 | +|LoginIpTip | 登录异常提示 | +|OrganizationJoined | 账号被拉入组织 | +|OrganizationLeft | 账号被移出组织 | +|OrganizationRole | 账号组织权限变更 | +|ProjectDeleted | 我关注的仓库被删除 | +|ProjectFollowed | 我管理的仓库被关注 | +|ProjectForked | 我管理的仓库被复刻 | +|ProjectIssue | 我管理/关注的仓库有新的易修 | +|ProjectJoined | 账号被拉入项目 | +|ProjectLeft | 账号被移出项目 | +|ProjectMemberJoined | 我管理的仓库有成员加入 | +|ProjectMemberLeft | 我管理的仓库有成员移出 | +|ProjectMilestone | 我管理的仓库有新的里程碑 | +|ProjectPraised | 我管理的仓库被点赞 | +|ProjectPullRequest | 我管理/关注的仓库有新的合并请求 | +|ProjectRole | 账号仓库权限变更 | +|ProjectSettingChanged | 我管理的仓库项目设置被更改 | +|ProjectTransfer | 我关注的仓库被转移 | +|ProjectVersion | 我关注的仓库有新的发行版 | +|PullRequestAssigned | 有新指派给我的合并请求 | +|PullReuqestAtme | 在合并请求中@我 | +|PullRequestChanged | 我创建或负责的合并请求状态变更 | +|PullRequestClosed | 我创建或负责的合并请求被关闭 | +|PullRequestJournal | 我创建或负责的合并请求有新的评论 | +|PullRequestMerged | 我创建或负责的合并请求被合并 | + + +> 返回的JSON示例: + +```json +{ + "total_count": 5, + "type": "", + "unread_notification": 3, + "unread_atme": 2, + "messages": [ + { + "id": 1, + "status": 1, + "content": "Atme Message Content 1", + "notification_url": "http://www.baidu.com", + "source": "PullRequestAtme", + "time_ago": "1天前", + "type": "atme", + "sender": { + "id": 5, + "type": "User", + "name": "testforge2", + "login": "testforge2", + "image_url": "system/lets/letter_avatars/2/T/236_177_85/120.png" + } + }, + { + "id": 2, + "status": 0, + "content": "Atme Message Content 2", + "notification_url": "http://www.baidu.com", + "source": "IssueAtme", + "time_ago": "1天前", + "type": "atme", + "sender": { + "id": 4, + "type": "User", + "name": "testforge1", + "login": "testforge1", + "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png" + } + }, + { + "id": 3, + "status": 1, + "content": "Notification Message Content 1", + "notification_url": "http://www.baidu.com", + "source": "IssueDelete", + "time_ago": "1天前", + "type": "notification" + }, + { + "id": 4, + "status": 0, + "content": "Notification Message Content 2", + "notification_url": "http://www.baidu.com", + "source": "IssueChanged", + "time_ago": "1天前", + "type": "notification" + }, + { + "id": 5, + "status": 0, + "content": "Notification Message Content 3", + "notification_url": "http://www.baidu.com", + "source": "ProjectJoined", + "time_ago": "1天前", + "type": "notification" + } + ] +} +``` + + +## 发送消息 +发送消息, 目前只支持atme + +> 示例: + +```shell +curl -X POST http://localhost:3000/api/users/:login/messages.json +``` + +```javascript +await octokit.request('POST /api/users/:login/messages.json') +``` + +### HTTP 请求 +`POST api/users/yystopf/messages.json` + +### 请求字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|type | string | 消息类型 | +|receivers_login | array | 需要发送消息的用户名数组| +|atmeable_type | string | atme消息对象,是从哪里@我的,比如评论:Journal、易修:Issue、合并请求:PullRequest | +|atmeable_id | integer | atme消息对象id | + +> 请求的JSON示例: + +```json +{ + "type": "atme", + "receivers_login": ["yystopf", "testforge1"], + "atmeable_type": "Journal", + "atmeable_id": 67 +} +``` + + +> 返回的JSON示例: + +```json +{ + "status": 0, + "message": "success" +} +``` + + +## 阅读消息 +阅读消息 + +> 示例: + +```shell +curl -X POST http://localhost:3000/api/users/:login/messages/read.json +``` + +```javascript +await octokit.request('POST /api/users/:login/messages/read.json') +``` + +### HTTP 请求 +`POST api/users/yystopf/messages/read.json` + +### 请求字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|type | string | 消息类型,不传为所有消息,notification为系统消息,atme为@我消息| +|ids | array | 消息id数组,包含-1则把所有未读消息标记为已读| + +> 返回的JSON示例: + +```json +{ + "status": 0, + "message": "success" +} +``` + + +## 删除消息 +删除消息 + +> 示例: + +```shell +curl -X DELETE http://localhost:3000/api/users/:login/messages.json +``` + +```javascript +await octokit.request('DELETE /api/users/:login/messages.json') +``` + +### HTTP 请求 +`DELETE api/users/yystopf/messages.json` + +### 请求字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|type | string | 消息类型,atme为@我消息| +|ids | array | 消息id数组,包含-1则把所有消息删除| + +> 返回的JSON示例: + +```json +{ + "status": 0, + "message": "success" +} +``` + + + ## 更改用户信息 更改用户信息 diff --git a/app/forms/base_form.rb b/app/forms/base_form.rb index c41a12993..71eaee174 100644 --- a/app/forms/base_form.rb +++ b/app/forms/base_form.rb @@ -2,11 +2,15 @@ class BaseForm include ActiveModel::Model def check_project_category(project_category_id) - raise "project_category_id参数值无效." if project_category_id && !ProjectCategory.exists?(project_category_id) + unless project_category_id == '' + raise "project_category_id参数值无效." if project_category_id && !ProjectCategory.exists?(project_category_id) + end end def check_project_language(project_language_id) - raise "project_language_id参数值无效." if project_language_id && !ProjectLanguage.exists?(project_language_id) + unless project_language_id == '' + raise "project_language_id参数值无效." if project_language_id && !ProjectLanguage.exists?(project_language_id) + end end def check_repository_name(user_id, repository_name) diff --git a/app/forms/notice/write/create_atme_form.rb b/app/forms/notice/write/create_atme_form.rb new file mode 100644 index 000000000..5ac3acc25 --- /dev/null +++ b/app/forms/notice/write/create_atme_form.rb @@ -0,0 +1,23 @@ +class Notice::Write::CreateAtmeForm + include ActiveModel::Model + + attr_accessor :receivers_login, :atmeable_type, :atmeable_id + + validate :check_receivers + + def check_receivers + receivers_login.each do |login| + receiver = User.find_by(login: login) || User.find_by_id(login) + raise 'receivers_login值无效.' unless receiver.present? + end + end + + def check_atmeable + begin + raise 'atmeable对象无效.' unless atmeable_type.constantize.find_by_id(atmeable_id).present? + rescue => exception + raise 'atmeable对象无效.' + end + end + +end \ No newline at end of file diff --git a/app/forms/projects/update_form.rb b/app/forms/projects/update_form.rb index 65810a820..0cd2c9459 100644 --- a/app/forms/projects/update_form.rb +++ b/app/forms/projects/update_form.rb @@ -1,6 +1,6 @@ class Projects::UpdateForm < BaseForm attr_accessor :name, :description, :project_category_id, :project_language_id, :private - validates :name, :description, :project_category_id, :project_language_id, presence: true + validates :name, presence: true validates :name, length: { maximum: 50 } validates :description, length: { maximum: 200 } validate do diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 72f3f3415..c37fd59da 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -434,6 +434,10 @@ module ApplicationHelper User.find_by_login login end + def find_user_by_login_and_mail(login, mail) + User.find_by(login: login, mail: mail) + end + def find_user_by_gitea_uid(gitea_uid) User.find_by(gitea_uid: gitea_uid) end diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index 7057ac581..1096d1d21 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -6,16 +6,16 @@ module RepositoriesHelper def render_decode64_content(str) return nil if str.blank? - Base64.decode64(str).force_encoding("UTF-8") + Base64.decode64(str).force_encoding("UTF-8").encode("UTF-8", invalid: :replace) 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 otf eot ttf woff woff2) + default_type = %w(xlsx xls ppt pptx pdf zip 7z rar exe pdb obj idb RData rdata doc docx mpp vsdx dot otf eot ttf woff woff2) default_type.include?(str&.downcase) end def image_type?(str) - default_type = %w(png jpg gif tif psd svg gif bmp webp jpeg) + default_type = %w(png jpg gif tif psd svg bmp webp jpeg) default_type.include?(str&.downcase) end @@ -26,9 +26,13 @@ module RepositoriesHelper end def render_commit_author(author_json) - return nil if author_json.blank? || author_json["id"].blank? - # find_user_by_login author_json['name'] - find_user_by_gitea_uid author_json['id'] + return nil if author_json.blank? || (author_json["id"].blank? && author_json['name'].blank?) + if author_json["id"].present? + return find_user_by_gitea_uid author_json['id'] + end + if author_json["id"].nil? && (author_json["name"].present? && author_json["email"].present?) + return find_user_by_login_and_mail(author_json['name'], author_json["email"]) + end end def readme_render_decode64_content(str, path) @@ -79,12 +83,15 @@ module RepositoriesHelper def decode64_content(entry, owner, repo, ref, path=nil) if is_readme?(entry['type'], entry['name']) - content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, entry['path'], ref: ref)['content'] + content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content'] readme_render_decode64_content(content, path) else file_type = File.extname(entry['name'].to_s)[1..-1] + if image_type?(file_type) + return entry['content'].nil? ? Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content'] : entry['content'] + end if download_type(file_type) - return entry['content'].nil? ? Gitea::Repository::Entries::GetService.call(owner, repo.identifier, entry['path'], ref: ref)['content'] : entry['content'] + return entry['content'] end render_decode64_content(entry['content']) end diff --git a/app/jobs/broadcast_mirror_repo_msg_job.rb b/app/jobs/broadcast_mirror_repo_msg_job.rb index 6372ae2e3..4dee6894a 100644 --- a/app/jobs/broadcast_mirror_repo_msg_job.rb +++ b/app/jobs/broadcast_mirror_repo_msg_job.rb @@ -18,12 +18,30 @@ class BroadcastMirrorRepoMsgJob < ApplicationJob id: project.id, type: project.numerical_for_project_type } + # 新增失败重试机制, 重试三次 + result = broadcast(project, json_data) + + if result == 0 + count = 3 + while count > 0 + sleep 3.seconds + result = broadcast(project, json_data) + if result > 0 + break + end + count -= 1 + end + end + end + + def broadcast(project, json_data) puts "############ broadcast start.......... " puts "############ broadcast channel_name: channel_room_#{project.id}" puts "############ broadcast project data: #{json_data} " cable_result = ActionCable.server.broadcast "channel_room_#{project.id}", project: json_data - puts "############ broadcast result: #{cable_result == 1 ? 'successed' : 'failed'} " + puts "############ broadcast result: #{cable_result > 0 ? 'successed' : 'failed'} " + return cable_result end end diff --git a/app/jobs/delay_expired_issue_job.rb b/app/jobs/delay_expired_issue_job.rb new file mode 100644 index 000000000..4633a953d --- /dev/null +++ b/app/jobs/delay_expired_issue_job.rb @@ -0,0 +1,11 @@ +class DelayExpiredIssueJob < ApplicationJob + queue_as :message + + def perform + Issue.where(due_date: Date.today + 1.days).find_each do |issue| + SendTemplateMessageJob.perform_later('IssueAssignerExpire', issue.id) + SendTemplateMessageJob.perform_later('IssueCreatorExpire', issue.id) + end + end + +end \ No newline at end of file diff --git a/app/jobs/migrate_remote_repository_job.rb b/app/jobs/migrate_remote_repository_job.rb index 340e006ea..e54eaf2bc 100644 --- a/app/jobs/migrate_remote_repository_job.rb +++ b/app/jobs/migrate_remote_repository_job.rb @@ -16,5 +16,6 @@ class MigrateRemoteRepositoryJob < ApplicationJob else repo&.mirror&.failed! end + BroadcastMirrorRepoMsgJob.perform_later(repo.id) unless repo&.mirror.waiting? end end diff --git a/app/jobs/send_template_message_job.rb b/app/jobs/send_template_message_job.rb new file mode 100644 index 000000000..2c463d705 --- /dev/null +++ b/app/jobs/send_template_message_job.rb @@ -0,0 +1,259 @@ +class SendTemplateMessageJob < ApplicationJob + queue_as :message + + def perform(source, *args) + Rails.logger.info "SendTemplateMessageJob [args] #{args}" + case source + when 'FollowTip' + watcher_id = args[0] + watcher = Watcher.find_by_id(watcher_id) + return unless watcher.present? + receivers = User.where(id: watcher.watchable_id) + followeder = User.find_by_id(watcher.user_id) + receivers_string, content, notification_url = MessageTemplate::FollowedTip.get_message_content(receivers, followeder) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {watcher_id: watcher.id}) + when 'IssueAssigned' + operator_id, issue_id = args[0], args[1] + operator = User.find_by_id(operator_id) + issue = Issue.find_by_id(issue_id) + return unless operator.present? && issue.present? + receivers = User.where(id: issue&.assigned_to_id).where.not(id: operator&.id) + receivers_string, content, notification_url = MessageTemplate::IssueAssigned.get_message_content(receivers, operator, issue) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, issue_id: issue.id}) + receivers.find_each do |receiver| + receivers_email_string, email_title, email_content = MessageTemplate::IssueAssigned.get_email_message_content(receiver, operator, issue) + Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) + end + when 'IssueAssignerExpire' + issue_id = args[0] + issue = Issue.find_by_id(issue_id) + return unless issue.present? + receivers = User.where(id: issue&.assigned_to_id) + receivers_string, content, notification_url = MessageTemplate::IssueAssignerExpire.get_message_content(receivers, issue) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {issue_id: issue.id}) + when 'IssueAtme' + receivers, operator_id, issue_id = args[0], args[1], args[2] + operator = User.find_by_id(operator_id) + issue = Issue.find_by_id(issue_id) + return unless operator.present? && issue.present? + receivers = receivers.where.not(id: operator&.id) + receivers_string, content, notification_url = MessageTemplate::IssueAtme.get_message_content(receivers, operator, issue) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, issue_id: issue.id}, 2) + when 'IssueChanged' + operator_id, issue_id, change_params = args[0], args[1], args[2] + operator = User.find_by_id(operator_id) + issue = Issue.find_by_id(issue_id) + return unless operator.present? && issue.present? + receivers = User.where(id: [issue&.assigned_to_id, issue&.author_id]).where.not(id: operator&.id) + receivers_string, content, notification_url = MessageTemplate::IssueChanged.get_message_content(receivers, operator, issue, change_params) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, issue_id: issue.id, change_params: change_params.symbolize_keys}) + when 'IssueCreatorExpire' + issue_id = args[0] + issue = Issue.find_by_id(issue_id) + return unless issue.present? + receivers = User.where(id: issue&.author_id) + receivers_string, content, notification_url = MessageTemplate::IssueCreatorExpire.get_message_content(receivers, issue) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {issue_id: issue.id}) + when 'IssueDeleted' + operator_id, issue_title, issue_assigned_to_id, issue_author_id = args[0], args[1], args[2], args[3] + operator = User.find_by_id(operator_id) + return unless operator.present? + receivers = User.where(id: [issue_assigned_to_id, issue_author_id]).where.not(id: operator&.id) + receivers_string, content, notification_url = MessageTemplate::IssueDeleted.get_message_content(receivers, operator, issue_title) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, issue_title: issue_title}) + when 'OrganizationJoined' + user_id, organization_id = args[0], args[1] + user = User.find_by_id(user_id) + organization = Organization.find_by_id(organization_id) + return unless user.present? && organization.present? + receivers = User.where(id: user.id) + receivers_string, content, notification_url = MessageTemplate::OrganizationJoined.get_message_content(receivers, organization) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {user_id: user.id, organization_id: organization.id}) + receivers.find_each do |receiver| + receivers_email_string, email_title, email_content = MessageTemplate::OrganizationJoined.get_email_message_content(receiver, organization) + Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) + end + when 'OrganizationLeft' + user_id, organization_id = args[0], args[1] + user = User.find_by_id(user_id) + organization = Organization.find_by_id(organization_id) + return unless user.present? && organization.present? + receivers = User.where(id: user.id) + receivers_string, content, notification_url = MessageTemplate::OrganizationLeft.get_message_content(receivers, organization) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {user_id: user.id, organization_id: organization.id}) + receivers.find_each do |receiver| + receivers_email_string, email_title, email_content = MessageTemplate::OrganizationLeft.get_email_message_content(receiver, organization) + Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) + end + when 'OrganizationRole' + user_id, organization_id, role = args[0], args[1], args[2] + user = User.find_by_id(user_id) + organization = Organization.find_by_id(organization_id) + return unless user.present? && organization.present? + receivers = User.where(id: user.id) + receivers_string, content, notification_url = MessageTemplate::OrganizationRole.get_message_content(receivers, organization, role) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {user_id: user.id, organization_id: organization.id, role: role}) + receivers.find_each do |receiver| + receivers_email_string, email_title, email_content = MessageTemplate::OrganizationRole.get_email_message_content(receiver, organization, role) + Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) + end + when 'ProjectIssue' + operator_id, issue_id = args[0], args[1] + operator = User.find_by_id(operator_id) + issue = Issue.find_by_id(issue_id) + return unless operator.present? && issue.present? && issue&.project.present? + managers = issue&.project&.all_managers.where.not(id: operator&.id) + followers = User.none # TODO + receivers_string, content, notification_url = MessageTemplate::ProjectIssue.get_message_content(managers, followers, operator, issue) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, issue_id: issue.id}) + managers.find_each do |receiver| + receivers_email_string, email_title, email_content = MessageTemplate::ProjectIssue.get_email_message_content(receiver, true, operator, issue) + Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) + end + followers.find_each do |receiver| + receivers_email_string, email_title, email_content = MessageTemplate::ProjectIssue.get_email_message_content(receiver, false, operator, issue) + Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) + end + when 'ProjectJoined' + operator_id, user_id, project_id = args[0], args[1], args[2] + operator = User.find_by_id(operator_id) + user = User.find_by_id(user_id) + project = Project.find_by_id(project_id) + return unless operator.present? && user.present? && project.present? + receivers = User.where(id: user.id).where.not(id: operator&.id) + receivers_string, content, notification_url = MessageTemplate::ProjectJoined.get_message_content(receivers, project) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, user_id: user.id, project_id: project.id}) + receivers.find_each do |receiver| + receivers_email_string, email_title, email_content = MessageTemplate::ProjectJoined.get_email_message_content(receiver, project) + Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) + end + when 'ProjectLeft' + operator_id, user_id, project_id = args[0], args[1], args[2] + operator = User.find_by_id(operator_id) + user = User.find_by_id(user_id) + project = Project.find_by_id(project_id) + return unless operator.present? && user.present? && project.present? + receivers = User.where(id: user.id).where.not(id: operator&.id) + receivers_string, content, notification_url = MessageTemplate::ProjectLeft.get_message_content(receivers, project) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, user_id: user.id, project_id: project.id}) + receivers.find_each do |receiver| + receivers_email_string, email_title, email_content = MessageTemplate::ProjectLeft.get_email_message_content(receiver, project) + Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) + end + when 'ProjectMemberJoined' + operator_id, user_id, project_id = args[0], args[1], args[2] + operator = User.find_by_id(operator_id) + user = User.find_by_id(user_id) + project = Project.find_by_id(project_id) + return unless operator.present? && user.present? && project.present? + receivers = project&.all_managers.where.not(id: [operator&.id, user&.id]) + receivers_string, content, notification_url = MessageTemplate::ProjectMemberJoined.get_message_content(receivers, user, project) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, user_id: user.id, project_id: project.id}) + receivers.find_each do |receiver| + receivers_email_string, email_title, email_content = MessageTemplate::ProjectMemberJoined.get_email_message_content(receiver, user, project) + Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) + end + when 'ProjectMemberLeft' + operator_id, user_id, project_id = args[0], args[1], args[2] + operator = User.find_by_id(operator_id) + user = User.find_by_id(user_id) + project = Project.find_by_id(project_id) + return unless operator.present? && user.present? && project.present? + receivers = project&.all_managers.where.not(id: [operator&.id, user&.id]) + receivers_string, content, notification_url = MessageTemplate::ProjectMemberLeft.get_message_content(receivers, user, project) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, user_id: user.id, project_id: project.id}) + receivers.find_each do |receiver| + receivers_email_string, email_title, email_content = MessageTemplate::ProjectMemberLeft.get_email_message_content(receiver, user, project) + Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) + end + when 'ProjectPullRequest' + operator_id, pull_request_id = args[0], args[1] + operator = User.find_by_id(operator_id) + pull_request = PullRequest.find_by_id(pull_request_id) + return unless operator.present? && pull_request.present? && pull_request&.project.present? + managers = pull_request&.project&.all_managers.where.not(id: operator&.id) + followers = User.none # TODO + receivers_string, content, notification_url = MessageTemplate::ProjectPullRequest.get_message_content(managers, followers, operator, pull_request) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, pull_request_id: pull_request.id}) + managers.find_each do |receiver| + receivers_email_string, email_title, email_content = MessageTemplate::ProjectPullRequest.get_email_message_content(receiver, true, operator, pull_request) + Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) + end + followers.find_each do |receiver| + receivers_email_string, email_title, email_content = MessageTemplate::ProjectPullRequest.get_email_message_content(receiver, false, operator, pull_request) + Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) + end + when 'ProjectRole' + operator_id, user_id, project_id, role = args[0], args[1], args[2], args[3] + operator = User.find_by_id(operator_id) + user = User.find_by_id(user_id) + project = Project.find_by_id(project_id) + return unless operator.present? && user.present? && project.present? + receivers = User.where(id: user.id).where.not(id: operator&.id) + receivers_string, content, notification_url = MessageTemplate::ProjectRole.get_message_content(receivers, project, role) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, user_id: user.id, project_id: project.id, role: role}) + receivers.find_each do |receiver| + receivers_email_string, email_title, email_content = MessageTemplate::ProjectRole.get_email_message_content(receivers, project, role) + Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) + end + when 'ProjectSettingChanged' + operator_id, project_id, change_params = args[0], args[1], args[2] + operator = User.find_by_id(operator_id) + project = Project.find_by_id(project_id) + return unless operator.present? && project.present? + receivers = project.all_managers.where.not(id: operator&.id) + receivers_string, content, notification_url = MessageTemplate::ProjectSettingChanged.get_message_content(receivers, operator, project, change_params.symbolize_keys) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, project_id: project.id, change_params: change_params}) + receivers.find_each do |receiver| + receivers_email_string, email_title, email_content = MessageTemplate::ProjectSettingChanged.get_email_message_content(receiver, operator, project, change_params.symbolize_keys) + Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) + end + when 'PullRequestAssigned' + operator_id, pull_request_id = args[0], args[1] + operator = User.find_by_id(operator_id) + pull_request = PullRequest.find_by_id(pull_request_id) + issue = Issue.find_by_id(pull_request&.issue_id) + return unless operator.present? && pull_request.present? && issue.present? + receivers = User.where(id: issue&.assigned_to_id).where.not(id: operator&.id) + receivers_string, content, notification_url = MessageTemplate::PullRequestAssigned.get_message_content(receivers, operator, pull_request) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, pull_request_id: pull_request.id}) + receivers.find_each do |receiver| + receivers_email_string, email_title, email_content = MessageTemplate::PullRequestAssigned.get_email_message_content(receiver, operator, pull_request) + Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) + end + when 'PullRequestAtme' + receivers, operator_id, pull_request_id = args[0], args[1], args[2] + operator = User.find_by_id(operator_id) + pull_request = PullRequest.find_by_id(pull_request_id) + return unless operator.present? && pull_request.present? + receivers = receivers.where.not(id: operator&.id) + receivers_string, content, notification_url = MessageTemplate::PullRequestAtme.get_message_content(receivers, operator, pull_request) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, pull_request_id: pull_request.id}, 2) + when 'PullRequestChanged' + operator_id, pull_request_id, change_params = args[0], args[1], args[2] + operator = User.find_by_id(operator_id) + pull_request = PullRequest.find_by_id(pull_request_id) + issue = Issue.find_by_id(pull_request&.issue_id) + return unless operator.present? && pull_request.present? && issue.present? + receivers = User.where(id: [issue&.assigned_to_id, pull_request&.user_id]).where.not(id: operator&.id) + receivers_string, content, notification_url = MessageTemplate::PullRequestChanged.get_message_content(receivers, operator, pull_request, change_params.symbolize_keys) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, pull_request_id: pull_request.id, change_params: change_params}) + when 'PullRequestClosed' + operator_id, pull_request_id = args[0], args[1] + operator = User.find_by_id(operator_id) + pull_request = PullRequest.find_by_id(pull_request_id) + return unless operator.present? && pull_request.present? + receivers = User.where(id: [pull_request&.issue&.assigned_to_id, pull_request&.user_id]).where.not(id: operator&.id) + receivers_string, content, notification_url = MessageTemplate::PullRequestClosed.get_message_content(receivers, operator, pull_request) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, pull_request_id: pull_request.id}) + when 'PullRequestMerged' + operator_id, pull_request_id = args[0], args[1] + operator = User.find_by_id(operator_id) + pull_request = PullRequest.find_by_id(pull_request_id) + return unless operator.present? && pull_request.present? + receivers = User.where(id: [pull_request&.issue&.assigned_to_id, pull_request&.user_id]).where.not(id: operator&.id) + receivers_string, content, notification_url = MessageTemplate::PullRequestMerged.get_message_content(receivers, operator, pull_request) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, pull_request_id: pull_request.id}) + end + end +end \ No newline at end of file diff --git a/app/jobs/sync_mirrored_repository_job.rb b/app/jobs/sync_mirrored_repository_job.rb index a1408153f..63abbfd80 100644 --- a/app/jobs/sync_mirrored_repository_job.rb +++ b/app/jobs/sync_mirrored_repository_job.rb @@ -26,6 +26,7 @@ class SyncMirroredRepositoryJob < ApplicationJob result = Gitea::Repository::SyncMirroredService.call(repo.owner.login, repo.identifier, token: user.gitea_token) repo&.mirror.set_status! if result[:status] === 200 + BroadcastMirrorRepoMsgJob.perform_later(repo.id) unless repo&.mirror.waiting? end end diff --git a/app/libs/notice.rb b/app/libs/notice.rb new file mode 100644 index 000000000..93d5cb42d --- /dev/null +++ b/app/libs/notice.rb @@ -0,0 +1,21 @@ +module Notice + class << self + def notice_config + notice_config = {} + + begin + config = Rails.application.config_for(:configuration).symbolize_keys! + notice_config = config[:notice].symbolize_keys! + raise 'notice config missing' if notice_config.blank? + rescue => exception + raise ex if Rails.env.production? + + puts %Q{\033[33m [warning] gitea config or configuration.yml missing, + please add it or execute 'cp config/configuration.yml.example config/configuration.yml' \033[0m} + notice_config = {} + end + + notice_config + end + end +end \ No newline at end of file diff --git a/app/models/message_template.rb b/app/models/message_template.rb new file mode 100644 index 000000000..2c8c96869 --- /dev/null +++ b/app/models/message_template.rb @@ -0,0 +1,99 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +class MessageTemplate < ApplicationRecord + + def self.build_init_data + self.create(type: 'MessageTemplate::FollowedTip', sys_notice: '{nickname} 关注了你', notification_url: '{baseurl}/{login}') + email_html = File.read("#{email_template_html_dir}/issue_assigned.html") + self.create(type: 'MessageTemplate::IssueAssigned', sys_notice: '{nickname1}在 {nickname2}/{repository} 指派给你一个易修:{title}', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}', email: email_html, email_title: '{nickname1} 在 {nickname2}/{repository} 指派给你一个易修') + self.create(type: 'MessageTemplate::IssueAssignerExpire', sys_notice: '您负责的易修 {title} 已临近截止日期,请尽快处理', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}') + self.create(type: 'MessageTemplate::IssueAtme', sys_notice: '{nickname} 在易修 {title} 中@我', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}') + self.create(type: 'MessageTemplate::IssueChanged', sys_notice: '在项目 {nickname2}/{repository} 的易修 {title} 中:{ifassigner}{nickname1}将负责人从 {assigner1} 修改为 {assigner2} {endassigner}{ifstatus}{nickname1}将状态从 {status1} 修改为 {status2} {endstatus}{iftracker}{nickname1}将类型从 {tracker1} 修改为 {tracker2} {endtracker}{ifpriority}{nickname1}将优先级从 {priority1} 修改为 {priority2} {endpriority}{ifmilestone}{nickname1}将里程碑从 {milestone1} 修改为 {milestone2} {endmilestone}{iftag}{nickname1}将标记从 {tag1} 修改为 {tag2} {endtag}{ifdoneratio}{nickname1}将完成度从 {doneratio1} 修改为 {doneratio2} {enddoneratio}{ifbranch}{nickname1}将指定分支从 {branch1} 修改为 {branch2} {endbranch}{ifstartdate}{nickname1}将开始日期从 {startdate1} 修改为 {startdate2} {endstartdate}{ifduedate}{nickname1}将结束日期从 {duedate1} 修改为 {duedate2} {endduedate}', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}') + self.create(type: 'MessageTemplate::IssueCreatorExpire', sys_notice: '您发布的易修 {title} 已临近截止日期,请尽快处理', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}') + self.create(type: 'MessageTemplate::IssueDeleted', sys_notice: '{nickname}已将易修 {title} 删除', notification_url: '') + self.create(type: 'MessageTemplate::IssueJournal', sys_notice: '{nickname}评论易修{title}:{notes}', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}') + self.create(type: 'MessageTemplate::LoginIpTip', sys_notice: '您的账号{nickname}于{login_time)在非常用的IP地址{ip}登录,如非本人操作,请立即修改密码', notification_url: '') + email_html = File.read("#{email_template_html_dir}/organization_joined.html") + self.create(type: 'MessageTemplate::OrganizationJoined', sys_notice: '你已加入 {organization} 组织', notification_url: '{baseurl}/{login}', email: email_html, email_title: '你已加入 {organization} 组织') + email_html = File.read("#{email_template_html_dir}/organization_left.html") + self.create(type: 'MessageTemplate::OrganizationLeft', sys_notice: '你已被移出 {organization} 组织', notification_url: '', email: email_html, email_title: '你已被移出 {organization} 组织') + self.create(type: 'MessageTemplate::OrganizationRole', sys_notice: '组织 {organization} 已把你的角色改为 {role}', notification_url: '{baseurl}/{login}') + self.create(type: 'MessageTemplate::ProjectDeleted', sys_notice: '你关注的仓库{nickname}/{repository}已被删除', notification_url: '') + self.create(type: 'MessageTemplate::ProjectFollowed', sys_notice: '{nickname} 关注了你管理的仓库', notification_url: '{baseurl}/{login}') + self.create(type: 'MessageTemplate::ProjectForked', sys_notice: '{nickname1} 复刻了你管理的仓库{nickname1}/{repository1}到{nickname2}/{repository2}', notification_url: '{baseurl}/{owner}/{identifier}') + email_html = File.read("#{email_template_html_dir}/project_issue.html") + self.create(type: 'MessageTemplate::ProjectIssue', sys_notice: '{nickname1}在 {nickname2}/{repository} 新建易修:{title}', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}', email: email_html, email_title: '{nickname1} 在 {nickname2}/{repository} 新建了一个易修') + email_html = File.read("#{email_template_html_dir}/project_joined.html") + self.create(type: 'MessageTemplate::ProjectJoined', sys_notice: '你已加入 {repository} 项目', notification_url: '{baseurl}/{owner}/{identifier}', email: email_html, email_title: '你已加入 {repository} 项目') + email_html = File.read("#{email_template_html_dir}/project_left.html") + self.create(type: 'MessageTemplate::ProjectLeft', sys_notice: '你已被移出 {repository} 项目', notification_url: '', email: email_html, email_title: '你已被移出 {repository} 项目') + email_html = File.read("#{email_template_html_dir}/project_member_joined.html") + self.create(type: 'MessageTemplate::ProjectMemberJoined', sys_notice: '{nickname1} 已加入项目 {nickname2}/{repository}', notification_url: '{baseurl}/{owner}/{identifier}', email: email_html, email_title: '{nickname1} 已加入项目 {nickname2}/{repository}') + email_html = File.read("#{email_template_html_dir}/project_member_left.html") + self.create(type: 'MessageTemplate::ProjectMemberLeft', sys_notice: '{nickname1} 已被移出项目 {nickname2}/{repository}', notification_url: '{baseurl}/{owner}/{identifier}', email: email_html, email_title: '{nickname1} 已被移出项目 {nickname2}/{repository}') + self.create(type: 'MessageTemplate::ProjectMilestone', sys_notice: '{nickname1}在 {nickname2}/{repository} 创建了一个里程碑:{title}', notification_url: '{baseurl}/{owner}/{identifier}/milestones/{id}') + self.create(type: 'MessageTemplate::ProjectPraised', sys_notice: '{nickname} 点赞了你管理的仓库', notification_url: '{baseurl}/{login}') + email_html = File.read("#{email_template_html_dir}/project_pull_request.html") + self.create(type: 'MessageTemplate::ProjectPullRequest', sys_notice: '{nickname1}在 {nickname2}/{repository} 提交了一个合并请求:{title}', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}/Messagecount', email: email_html, email_title: '{nickname1} 在 {nickname2}/{repository} 提交了一个合并请求') + self.create(type: 'MessageTemplate::ProjectRole', sys_notice: '仓库 {repository} 已把你的角色改为 {role}', notification_url: '{baseurl}/{owner}/{identifier}') + email_html = File.read("#{email_template_html_dir}/project_setting_changed.html") + self.create(type: 'MessageTemplate::ProjectSettingChanged', sys_notice: '{nickname1}更改了 {nickname2}/{repository} 仓库设置:{ifname}更改项目名称为"{name}"{endname}{ifdescription}更改项目简介为"{description}"{enddescription}{ifcategory}更改项目类别为"{category}"{endcategory}{iflanguage}更改项目语言为"{language}"{endlanguage}{ifpermission}将仓库设为"{permission}"{endpermission}{ifnavbar}将项目导航更改为"{navbar}"{endnavbar}', notification_url: '{baseurl}/{owner}/{identifier}/settings', email: email_html, email_title: '您管理的仓库 {nickname2}/{repository} 仓库设置已被更改') + self.create(type: 'MessageTemplate::ProjectTransfer', sys_notice: '你关注的仓库{nickname1}/{repository1}已被转移至{nickname2}/{repository2}', notification_url: '{baseurl}/{owner}/{identifier}') + self.create(type: 'MessageTemplate::ProjectVersion', sys_notice: '{nickname1}在 {nickname2}/{repository} 创建了发行版:{title}', notification_url: '{baseurl}/{owner}/{identifier}/releases') + email_html = File.read("#{email_template_html_dir}/pull_request_assigned.html") + self.create(type: 'MessageTemplate::PullRequestAssigned', sys_notice: '{nickname1}在 {nickname2}/{repository} 指派给你一个合并请求:{title}', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}/Messagecount', email: email_html, email_title: '{nickname1} 在 {nickname2}/{repository} 指派给你一个合并请求') + self.create(type: 'MessageTemplate::PullRequestAtme', sys_notice: '{nickname} 在合并请求 {title} 中@我', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}/Messagecount') + self.create(type: 'MessageTemplate::PullRequestChanged', sys_notice: '在项目{nickname2}/{repository}的合并请求 {title} 中:{ifassigner}{nickname1}将审查成员从 {assigner1} 修改为 {assigner2} {endassigner}{ifmilestone}{nickname1}将里程碑从 {milestone1} 修改为 {milestone2} {endmilestone}{iftag}{nickname1}将标记从 {tag1} 修改为 {tag2} {endtag}{ifpriority}{nickname1}将优先级从 {priority1} 修改为 {priority2} {endpriority}', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}/Messagecount') + self.create(type: 'MessageTemplate::PullRequestClosed', sys_notice: '你提交的合并请求:{title} 被拒绝', notification_url: '') + self.create(type: 'MessageTemplate::PullRequestJournal', sys_notice: '{nickname}评论合并请求{title}:{notes}', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}/Messagecount') + self.create(type: 'MessageTemplate::PullRequestMerged', sys_notice: '你提交的合并请求:{title} 已通过', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}/Messagecount') + end + + def self.sys_notice + self.last&.sys_notice + end + + def self.email + self.last&.email + end + + def self.email_title + self.last&.email_title + end + + def self.notification_url + self.last&.notification_url.gsub('{baseurl}', base_url) + end + + def self.base_url + Rails.application.config_for(:configuration)['platform_url'] + end + + def self.receivers_string(receivers) + receivers.pluck(:id).join(",") + end + + def self.receivers_email_string(receivers) + receivers.pluck(:mail).join(",") + end + + def self.email_template_html_dir + "#{Rails.root}/public/message_template" + end + + def simple_type + self.type.split("::")[-1] + end +end diff --git a/app/models/message_template/followed_tip.rb b/app/models/message_template/followed_tip.rb new file mode 100644 index 000000000..6231db595 --- /dev/null +++ b/app/models/message_template/followed_tip.rb @@ -0,0 +1,24 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 被关注提示 +class MessageTemplate::FollowedTip < MessageTemplate + + # MessageTemplate::FollowedTip.get_message_content(User.where(login: 'yystopf'), User.last) + def self.get_message_content(receivers, followeder) + return receivers_string(receivers), sys_notice.gsub('{nickname}', followeder&.real_name), notification_url.gsub('{login}', followeder.login) + rescue + return '', '', '' + end +end diff --git a/app/models/message_template/issue_assigned.rb b/app/models/message_template/issue_assigned.rb new file mode 100644 index 000000000..91daa8dcc --- /dev/null +++ b/app/models/message_template/issue_assigned.rb @@ -0,0 +1,54 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 有新指派给我的易修 +class MessageTemplate::IssueAssigned < MessageTemplate + + # MessageTemplate::IssueAssigned.get_message_content(User.where(login: 'yystopf'), User.last, Issue.last) + def self.get_message_content(receivers, operator, issue) + project = issue&.project + owner = project&.owner + content = sys_notice.gsub('{nickname1}', operator&.real_name).gsub('{nickname2}', owner&.real_name).gsub('{repository}', project&.name).gsub('{title}', issue&.subject) + url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.id.to_s) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::IssueAssigned.get_message_content [ERROR] #{e}") + return '', '', '' + end + + def self.get_email_message_content(receiver, operator, issue) + project = issue&.project + owner = project&.owner + title = email_title + title.gsub!('{nickname1}', operator&.real_name) + title.gsub!('{nickname2}', owner&.real_name) + title.gsub!('{repository}', project&.name) + content = email + content.gsub!('{receiver}', receiver&.real_name) + content.gsub!('{nickname1}', operator&.real_name) + content.gsub!('{login1}', operator&.login) + content.gsub!('{nickname2}', owner&.real_name) + content.gsub!('{login2}', owner&.login) + content.gsub!('{identifier}', project&.identifier) + content.gsub!('{repository}', project&.name) + content.gsub!('{baseurl}', base_url) + content.gsub!('{title}', issue&.subject) + content.gsub!('{id}', issue&.id.to_s) + + return receiver&.mail, title, content + rescue => e + Rails.logger.info("MessageTemplate::IssueAssigned.get_email_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/issue_assigner_expire.rb b/app/models/message_template/issue_assigner_expire.rb new file mode 100644 index 000000000..405d44bb4 --- /dev/null +++ b/app/models/message_template/issue_assigner_expire.rb @@ -0,0 +1,29 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 我负责的易修截止日期到达最后一天 +class MessageTemplate::IssueAssignerExpire < MessageTemplate + + # MessageTemplate::IssueAssignerExpire.get_message_content(User.where(login: 'yystopf'), Issue.last) + def self.get_message_content(receivers, issue) + project = issue&.project + owner = project&.owner + content = sys_notice.gsub('{title}', issue&.subject) + url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.id.to_s) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::IssueAssignerExpire.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/issue_atme.rb b/app/models/message_template/issue_atme.rb new file mode 100644 index 000000000..d9dfd1957 --- /dev/null +++ b/app/models/message_template/issue_atme.rb @@ -0,0 +1,29 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 在易修中@我 +class MessageTemplate::IssueAtme < MessageTemplate + + # MessageTemplate::IssueAtme.get_message_content(User.where(login: 'yystopf'), User.last, Issue.last) + def self.get_message_content(receivers, operator, issue) + project = issue&.project + owner = project&.owner + content = sys_notice.gsub('{nickname}', operator&.real_name).gsub('{title}', issue&.subject) + url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.id.to_s) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::IssueAtme.get_message_content [ERROR] #{e}") + return 0, '', '' + end +end diff --git a/app/models/message_template/issue_changed.rb b/app/models/message_template/issue_changed.rb new file mode 100644 index 000000000..6b9e79763 --- /dev/null +++ b/app/models/message_template/issue_changed.rb @@ -0,0 +1,183 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 我创建或负责的易修状态变更 +class MessageTemplate::IssueChanged < MessageTemplate + + # MessageTemplate::IssueChanged.get_message_content(User.where(login: 'yystopf'), User.last, Issue.last, {status_id: [1, 2], assigned_to_id: [nil, 203], tracker_id: [4, 3], priority_id: [2, 4], fixed_version_id: [nil, 5], due_date: ['', '2021-09-11'], done_ratio: [0, 40], issue_tags_value: ["", "7"], branch_name: ["", "master"]}) + def self.get_message_content(receivers, operator, issue, change_params) + return '', '', '' if change_params.blank? + project = issue&.project + owner = project&.owner + content = MessageTemplate::IssueChanged.sys_notice.gsub('{nickname1}', operator&.real_name).gsub('{nickname2}', owner&.real_name).gsub('{repository}', project&.name).gsub('{title}', issue&.subject) + url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.id.to_s) + change_count = change_params.keys.size + # 易修负责人修改 + if change_params[:assigned_to_id].present? + assigner1 = User.find_by_id(change_params[:assigned_to_id][0]) + assigner2 = User.find_by_id(change_params[:assigned_to_id][1]) + if change_count > 1 + content.sub!('{ifassigner}', '
') + else + content.sub!('{ifassigner}', '') + end + content.sub!('{endassigner}', '') + content.gsub!('{assigner1}', assigner1.present? ? assigner1&.real_name : '未指派成员') + content.gsub!('{assigner2}', assigner2.present? ? assigner2&.real_name : '未指派成员') + else + content.gsub!(/({ifassigner})(.*)({endassigner})/, '') + end + # 易修状态修改 + if change_params[:status_id].present? + status1 = IssueStatus.find_by_id(change_params[:status_id][0]) + status2 = IssueStatus.find_by_id(change_params[:status_id][1]) + if change_count > 1 + content.sub!('{ifstatus}', '
') + else + content.sub!('{ifstatus}', '') + end + content.sub!('{endstatus}', '') + content.gsub!('{status1}', status1&.name) + content.gsub!('{status2}', status2&.name) + else + content.gsub!(/({ifstatus})(.*)({endstatus})/, '') + end + # 易修类型修改 + if change_params[:tracker_id].present? + tracker1 = Tracker.find_by_id(change_params[:tracker_id][0]) + tracker2 = Tracker.find_by_id(change_params[:tracker_id][1]) + if change_count > 1 + content.sub!('{iftracker}', '
') + else + content.sub!('{iftracker}', '') + end + content.sub!('{endtracker}', '') + content.gsub!('{tracker1}', tracker1&.name) + content.gsub!('{tracker2}', tracker2&.name) + else + content.gsub!(/({iftracker})(.*)({endtracker})/, '') + end + # 易修里程碑修改 + if change_params[:fixed_version_id].present? + fix_version1 = Version.find_by_id(change_params[:fixed_version_id][0]) + fix_version2 = Version.find_by_id(change_params[:fixed_version_id][1]) + if change_count > 1 + content.sub!('{ifmilestone}', '
') + else + content.sub!('{ifmilestone}', '') + end + content.sub!('{endmilestone}', '') + content.gsub!('{milestone1}', fix_version1.present? ? fix_version1&.name : '未选择里程碑') + content.gsub!('{milestone2}', fix_version2.present? ? fix_version2&.name : '未选择里程碑') + else + content.gsub!(/({ifmilestone})(.*)({endmilestone})/, '') + end + # 易修标记修改 + if change_params[:issue_tags_value].present? + issue_tags1 = IssueTag.where(id: change_params[:issue_tags_value][0]).distinct + issue_tags2 = IssueTag.where(id: change_params[:issue_tags_value][1]).distinct + tag1 = issue_tags1.pluck(:name).join(",").blank? ? '未选择标记' : issue_tags1.pluck(:name).join(",") + tag2 = issue_tags2.pluck(:name).join(",").blank? ? '未选择标记' : issue_tags2.pluck(:name).join(",") + if change_count > 1 + content.sub!('{iftag}', '
') + else + content.sub!('{iftag}', '') + end + content.sub!('{endtag}', '') + content.gsub!('{tag1}', tag1) + content.gsub!('{tag2}', tag2) + else + content.gsub!(/({iftag})(.*)({endtag})()/, '') + end + # 易修优先级修改 + if change_params[:priority_id].present? + priority1 = IssuePriority.find_by_id(change_params[:priority_id][0]) + priority2 = IssuePriority.find_by_id(change_params[:priority_id][1]) + if change_count > 1 + content.sub!('{ifpriority}', '
') + else + content.sub!('{ifpriority}', '') + end + content.sub!('{endpriority}', '') + content.gsub!('{priority1}', priority1&.name) + content.gsub!('{priority2}', priority2&.name) + else + content.gsub!(/({ifpriority})(.*)({endpriority})/, '') + end + # 易修完成度修改 + if change_params[:done_ratio].present? + doneratio1 = change_params[:done_ratio][0] + doneratio2 = change_params[:done_ratio][1] + if change_count > 1 + content.sub!('{ifdoneratio}', '
') + else + content.sub!('{ifdoneratio}', '') + end + content.sub!('{enddoneratio}', '') + content.gsub!('{doneratio1}', "#{doneratio1}%") + content.gsub!('{doneratio2}', "#{doneratio2}%") + else + content.gsub!(/({ifdoneratio})(.*)({enddoneratio})/, '') + end + # 易修指定分支修改 + if change_params[:branch_name].present? + branch1 = change_params[:branch_name][0].blank? ? '分支未指定' : change_params[:branch_name][0] + branch2 = change_params[:branch_name][1].blank? ? '分支未指定' : change_params[:branch_name][1] + if change_count > 1 + content.sub!('{ifbranch}', '
') + else + content.sub!('{ifbranch}', '') + end + content.sub!('{endbranch}', '') + content.gsub!('{branch1}', branch1) + content.gsub!('{branch2}', branch2) + else + content.gsub!(/({ifbranch})(.*)({endbranch})/, '') + end + # 易修开始日期修改 + if change_params[:start_date].present? + startdate1 = change_params[:start_date][0].blank? ? "未选择开始日期" : change_params[:start_date][0] + startdate2 = change_params[:start_date][1].blank? ? "未选择开始日期" : change_params[:start_date][1] + if change_count > 1 + content.sub!('{ifstartdate}', '
') + else + content.sub!('{ifstartdate}', '') + end + content.sub!('{endstartdate}', '') + content.gsub!('{startdate1}', startdate1 ) + content.gsub!('{startdate2}', startdate2) + else + content.gsub!(/({ifstartdate})(.*)({endstartdate})/, '') + end + # 易修结束日期修改 + if change_params[:due_date].present? + duedate1 = change_params[:due_date][0].blank? ? '未选择结束日期' : change_params[:due_date][0] + duedate2 = change_params[:due_date][1].blank? ? '未选择结束日期' : change_params[:due_date][1] + if change_count > 1 + content.sub!('{ifduedate}', '
') + else + content.sub!('{ifduedate}', '') + end + content.sub!('{endduedate}', '') + content.gsub!('{duedate1}', duedate1) + content.gsub!('{duedate2}', duedate2) + else + content.gsub!(/({ifduedate})(.*)({endduedate})/, '') + end + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::IssueAssigned.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/issue_creator_expire.rb b/app/models/message_template/issue_creator_expire.rb new file mode 100644 index 000000000..38ef2fe29 --- /dev/null +++ b/app/models/message_template/issue_creator_expire.rb @@ -0,0 +1,29 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 我创建的易修截止日期到达最后一天 +class MessageTemplate::IssueCreatorExpire < MessageTemplate + + # MessageTemplate::IssueCreatorExpire.get_message_content(User.where(login: 'yystopf'), Issue.last) + def self.get_message_content(receivers, issue) + project = issue&.project + owner = project&.owner + content = sys_notice.gsub('{title}', issue&.subject) + url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.id.to_s) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::IssueAssignerExpire.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/issue_deleted.rb b/app/models/message_template/issue_deleted.rb new file mode 100644 index 000000000..8c4145716 --- /dev/null +++ b/app/models/message_template/issue_deleted.rb @@ -0,0 +1,26 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 我创建或负责的易修删除 +class MessageTemplate::IssueDeleted < MessageTemplate + + # MessageTemplate::IssueDeleted.get_message_content(User.where(login: 'yystopf'), User.last, "hahah") + def self.get_message_content(receivers, operator, issue_title) + content = sys_notice.gsub('{nickname}', operator&.real_name).gsub('{title}', issue_title) + return receivers_string(receivers), content, notification_url + rescue => e + Rails.logger.info("MessageTemplate::IssueAtme.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/issue_journal.rb b/app/models/message_template/issue_journal.rb new file mode 100644 index 000000000..0b3851475 --- /dev/null +++ b/app/models/message_template/issue_journal.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# TODO 我创建或负责的易修有新的评论 +class MessageTemplate::IssueJournal < MessageTemplate + + # MessageTemplate::IssueJournal.get_message_content(User.where(login: 'yystopf')) + def self.get_message_content(receivers) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::IssueJournal.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/login_ip_tip.rb b/app/models/message_template/login_ip_tip.rb new file mode 100644 index 000000000..5c7caa936 --- /dev/null +++ b/app/models/message_template/login_ip_tip.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# TODO 登录异常提示 +class MessageTemplate::LoginIpTip < MessageTemplate + + # MessageTemplate::LoginIpTip.get_message_content(User.where(login: 'yystopf')) + def self.get_message_content(receivers) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::LoginIpTip.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/organization_joined.rb b/app/models/message_template/organization_joined.rb new file mode 100644 index 000000000..9045ac424 --- /dev/null +++ b/app/models/message_template/organization_joined.rb @@ -0,0 +1,42 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 账号被拉入组织 +class MessageTemplate::OrganizationJoined < MessageTemplate + + # MessageTemplate::OrganizationJoined.get_message_content(User.where(login: 'yystopf'), Organization.last) + def self.get_message_content(receivers, organization) + content = sys_notice.gsub('{organization}', organization&.real_name) + url = notification_url.gsub('{login}', organization&.name) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::OrganizationJoined.get_message_content [ERROR] #{e}") + return '', '', '' + end + + def self.get_email_message_content(receiver, organization) + title = email_title + title.gsub!('{organization}', organization&.real_name) + content = email + content.gsub!('{receiver}', receiver&.real_name) + content.gsub!('{baseurl}', base_url) + content.gsub!('{login}', organization&.login) + content.gsub!('{organization}', organization&.real_name) + + return receiver&.mail, title, content + rescue => e + Rails.logger.info("MessageTemplate::OrganizationJoined.get_email_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/organization_left.rb b/app/models/message_template/organization_left.rb new file mode 100644 index 000000000..edf8b32ec --- /dev/null +++ b/app/models/message_template/organization_left.rb @@ -0,0 +1,42 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 账号被移出组织 +class MessageTemplate::OrganizationLeft < MessageTemplate + + # MessageTemplate::OrganizationLeft.get_message_content(User.where(login: 'yystopf'), Organization.last) + def self.get_message_content(receivers, organization) + content = sys_notice.gsub('{organization}', organization&.real_name) + url = notification_url.gsub('{login}', organization&.name) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::OrganizationLeft.get_message_content [ERROR] #{e}") + return '', '', '' + end + + def self.get_email_message_content(receiver, organization) + title = email_title + title.gsub!('{organization}', organization&.real_name) + content = email + content.gsub!('{receiver}', receiver&.real_name) + content.gsub!('{baseurl}', base_url) + content.gsub!('{login}', organization&.login) + content.gsub!('{organization}', organization&.real_name) + + return receiver&.mail, title, content + rescue => e + Rails.logger.info("MessageTemplate::OrganizationLeft.get_email_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/organization_role.rb b/app/models/message_template/organization_role.rb new file mode 100644 index 000000000..4bc96a63e --- /dev/null +++ b/app/models/message_template/organization_role.rb @@ -0,0 +1,44 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 账号组织权限变更 +class MessageTemplate::OrganizationRole < MessageTemplate + + # MessageTemplate::OrganizationRole.get_message_content(User.where(login: 'yystopf'), Organization.last, '管理员') + def self.get_message_content(receivers, organization, role) + content = sys_notice.gsub('{organization}', organization&.real_name).gsub('{role}', role) + url = notification_url.gsub('{login}', organization&.login) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::OrganizationRole.get_message_content [ERROR] #{e}") + return '', '', '' + end + + def self.get_email_message_content(receiver, organization, role) + title = email_title + title.gsub!('{organization}', organization&.real_name) + title.gsub!('{role}', role) + content = email + content.gsub!('{receiver}', receiver&.real_name) + content.gsub!('{baseurl}', base_url) + content.gsub!('{login}', organization&.login) + content.gsub!('{organization}', organization&.real_name) + content.gsub!('{role}', role) + + return receiver&.mail, title, content + rescue => e + Rails.logger.info("MessageTemplate::OrganizationRole.get_email_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/project_deleted.rb b/app/models/message_template/project_deleted.rb new file mode 100644 index 000000000..aa45e818e --- /dev/null +++ b/app/models/message_template/project_deleted.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# TODO 我关注的仓库被删除 +class MessageTemplate::ProjectDeleted < MessageTemplate + + # MessageTemplate::ProjectDeleted.get_message_content(User.where(login: 'yystopf')) + def self.get_message_content(receivers) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::ProjectDeleted.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/project_followed.rb b/app/models/message_template/project_followed.rb new file mode 100644 index 000000000..e9dd9a2a8 --- /dev/null +++ b/app/models/message_template/project_followed.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# TODO 我管理的仓库被关注 +class MessageTemplate::ProjectFollowed < MessageTemplate + + # MessageTemplate::ProjectFollowed.get_message_content(User.where(login: 'yystopf')) + def self.get_message_content(receivers) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::ProjectFollowed.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/project_forked.rb b/app/models/message_template/project_forked.rb new file mode 100644 index 000000000..7cd17222d --- /dev/null +++ b/app/models/message_template/project_forked.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# TODO 我管理的仓库被复刻 +class MessageTemplate::ProjectForked < MessageTemplate + + # MessageTemplate::ProjectForked.get_message_content(User.where(login: 'yystopf')) + def self.get_message_content(receivers) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::ProjectForked.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/project_issue.rb b/app/models/message_template/project_issue.rb new file mode 100644 index 000000000..8e319bf3b --- /dev/null +++ b/app/models/message_template/project_issue.rb @@ -0,0 +1,57 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# TODO 我管理/关注的仓库有新的易修 +class MessageTemplate::ProjectIssue < MessageTemplate + + # MessageTemplate::ProjectIssue.get_message_content(User.where(login: 'yystopf'), User.where(login: 'forgetest1'), User.last, Issue.last) + def self.get_message_content(managers, followers, operator, issue) + project = issue&.project + owner = project&.owner + receivers = managers + followers + content = sys_notice.gsub('{nickname1}', operator&.real_name).gsub('{nickname2}', owner&.real_name).gsub('{repository}', project&.name).gsub('{title}', issue&.subject) + url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.id.to_s) + + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::ProjectIssue.get_message_content [ERROR] #{e}") + return '', '', '' + end + + def self.get_email_message_content(receiver, is_manager, operator, issue) + project = issue&.project + owner = project&.owner + title = email_title + title.gsub!('{nickname1}', operator&.real_name) + title.gsub!('{nickname2}', owner&.real_name) + title.gsub!('{repository}', project&.name) + + content = email + content.gsub!('{receiver}', receiver&.real_name) + content.gsub!('{baseurl}', base_url) + content.gsub!('{login1}', operator&.login) + content.gsub!('{nickname1}', operator&.real_name) + content.gsub!('{nickname2}', owner&.real_name) + content.gsub!('{repository}', project&.name) + content.gsub!('{login2}', owner&.login) + content.gsub!('{identifier}', project&.identifier) + content.gsub!('{id}', issue&.id.to_s) + content.gsub!('{title}', issue&.subject) + + return receiver&.mail, title, content + rescue => e + Rails.logger.info("MessageTemplate::ProjectIssue.get_email_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/project_joined.rb b/app/models/message_template/project_joined.rb new file mode 100644 index 000000000..3ff1d23d7 --- /dev/null +++ b/app/models/message_template/project_joined.rb @@ -0,0 +1,45 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 账号被拉入项目 +class MessageTemplate::ProjectJoined < MessageTemplate + + # MessageTemplate::ProjectJoined.get_message_content(User.where(login: 'yystopf'), Project.last) + def self.get_message_content(receivers, project) + content = sys_notice.gsub('{repository}', project&.name) + url = notification_url.gsub('{owner}', project&.owner&.login).gsub('{identifier}', project&.identifier) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::ProjectJoined.get_message_content [ERROR] #{e}") + return '', '', '' + end + + def self.get_email_message_content(receiver, project) + title = email_title + title.gsub!('{repository}', project&.name) + + content = email + content.gsub!('{receiver}', receiver&.real_name) + content.gsub!('{baseurl}', base_url) + content.gsub!('{login}', project&.owner&.login) + content.gsub!('{identifier}', project&.identifier) + content.gsub!('{nickname}', project&.owner&.real_name) + content.gsub!('{repository}', project&.name) + + return receiver&.mail, title, content + rescue => e + Rails.logger.info("MessageTemplate::ProjectJoined.get_email_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/project_left.rb b/app/models/message_template/project_left.rb new file mode 100644 index 000000000..3244e59a0 --- /dev/null +++ b/app/models/message_template/project_left.rb @@ -0,0 +1,45 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 账号被移出项目 +class MessageTemplate::ProjectLeft < MessageTemplate + + # MessageTemplate::ProjectLeft.get_message_content(User.where(login: 'yystopf'), Project.last) + def self.get_message_content(receivers, project) + content = sys_notice.gsub('{repository}', project&.name) + url = notification_url.gsub('{owner}', project&.owner&.login).gsub('{identifier}', project&.identifier) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::ProjectLeft.get_message_content [ERROR] #{e}") + return '', '', '' + end + + def self.get_email_message_content(receiver, project) + title = email_title + title.gsub!('{repository}', project&.name) + + content = email + content.gsub!('{receiver}', receiver&.real_name) + content.gsub!('{baseurl}', base_url) + content.gsub!('{login}', project&.owner&.login) + content.gsub!('{identifier}', project&.identifier) + content.gsub!('{nickname}', project&.owner&.real_name) + content.gsub!('{repository}', project&.name) + + return receiver&.mail, title, content + rescue => e + Rails.logger.info("MessageTemplate::ProjectLeft.get_email_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/project_member_joined.rb b/app/models/message_template/project_member_joined.rb new file mode 100644 index 000000000..e2ab7d610 --- /dev/null +++ b/app/models/message_template/project_member_joined.rb @@ -0,0 +1,49 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 我管理的仓库有成员加入 +class MessageTemplate::ProjectMemberJoined < MessageTemplate + + # MessageTemplate::ProjectMemberJoined.get_message_content(User.where(login: 'yystopf')) + def self.get_message_content(receivers, user, project) + content = sys_notice.gsub('{nickname1}', user&.real_name).gsub('{nickname2}', project&.owner&.real_name).gsub('{repository}', project&.name) + url = notification_url.gsub('{owner}', project&.owner&.login).gsub('{identifier}', project&.identifier) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::ProjectMemberJoined.get_message_content [ERROR] #{e}") + return '', '', '' + end + + def self.get_email_message_content(receiver, user, project) + title = email_title + title.gsub!('{nickname1}', user&.real_name) + title.gsub!('{nickname2}', project&.owner&.real_name) + title.gsub!('{repository}', project&.name) + + content = email + content.gsub!('{receiver}', receiver&.real_name) + content.gsub!('{baseurl}', base_url) + content.gsub!('{login1}', user&.login) + content.gsub!('{login2}', project&.owner&.login) + content.gsub!('{identifier}', project&.identifier) + content.gsub!('{nickname1}', user&.real_name) + content.gsub!('{nickname2}', project&.owner&.real_name) + content.gsub!('{repository}', project&.name) + + return receiver&.mail, title, content + rescue => e + Rails.logger.info("MessageTemplate::ProjectMemberJoined.get_email_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/project_member_left.rb b/app/models/message_template/project_member_left.rb new file mode 100644 index 000000000..a7d9911d6 --- /dev/null +++ b/app/models/message_template/project_member_left.rb @@ -0,0 +1,49 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 我管理的仓库有成员移出 +class MessageTemplate::ProjectMemberLeft < MessageTemplate + + # MessageTemplate::ProjectMemberLeft.get_message_content(User.where(login: 'yystopf'), User.last, Project.last) + def self.get_message_content(receivers, user, project) + content = sys_notice.gsub('{nickname1}', user&.real_name).gsub('{nickname2}', project&.owner&.real_name).gsub('{repository}', project&.name) + url = notification_url.gsub('{owner}', project&.owner&.login).gsub('{identifier}', project&.identifier) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::ProjectMemberLeft.get_message_content [ERROR] #{e}") + return '', '', '' + end + + def self.get_email_message_content(receiver, user, project) + title = email_title + title.gsub!('{nickname1}', user&.real_name) + title.gsub!('{nickname2}', project&.owner&.real_name) + title.gsub!('{repository}', project&.name) + + content = email + content.gsub!('{receiver}', receiver&.real_name) + content.gsub!('{baseurl}', base_url) + content.gsub!('{login1}', user&.login) + content.gsub!('{login2}', project&.owner&.login) + content.gsub!('{identifier}', project&.identifier) + content.gsub!('{nickname1}', user&.real_name) + content.gsub!('{nickname2}', project&.owner&.real_name) + content.gsub!('{repository}', project&.name) + + return receiver&.mail, title, content + rescue => e + Rails.logger.info("MessageTemplate::ProjectMemberLeft.get_email_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/project_milestone.rb b/app/models/message_template/project_milestone.rb new file mode 100644 index 000000000..14f992cdd --- /dev/null +++ b/app/models/message_template/project_milestone.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# TODO 我管理的仓库有新的里程碑 +class MessageTemplate::ProjectMilestone < MessageTemplate + + # MessageTemplate::ProjectMilestone.get_message_content(User.where(login: 'yystopf')) + def self.get_message_content(receivers) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::ProjectMilestone.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/project_praised.rb b/app/models/message_template/project_praised.rb new file mode 100644 index 000000000..e6acee6f5 --- /dev/null +++ b/app/models/message_template/project_praised.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# TODO 我管理的仓库被点赞 +class MessageTemplate::ProjectPraised < MessageTemplate + + # MessageTemplate::ProjectPraised.get_message_content(User.where(login: 'yystopf')) + def self.get_message_content(receivers) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::ProjectPraised.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/project_pull_request.rb b/app/models/message_template/project_pull_request.rb new file mode 100644 index 000000000..704936f54 --- /dev/null +++ b/app/models/message_template/project_pull_request.rb @@ -0,0 +1,57 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# TODO 我管理/关注的仓库有新的合并请求 +class MessageTemplate::ProjectPullRequest < MessageTemplate + + # MessageTemplate::ProjectPullRequest.get_message_content(User.where(login: 'yystopf'), User.where(login: 'testforge2'), User.last, PullRequest.last) + def self.get_message_content(managers, followers, operator, pull_request) + project = pull_request&.project + owner = project&.owner + receivers = managers + followers + content = sys_notice.gsub('{nickname1}', operator&.real_name).gsub('{nickname2}', owner&.real_name).gsub('{repository}', project&.name).gsub('{title}', pull_request&.title) + url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', pull_request&.id.to_s) + + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::ProjectPullRequest.get_message_content [ERROR] #{e}") + return '', '', '' + end + + def self.get_email_message_content(receiver, is_manager, operator, pull_request) + project = pull_request&.project + owner = project&.owner + title = email_title + title.gsub!('{nickname1}', operator&.real_name) + title.gsub!('{nickname2}', owner&.real_name) + title.gsub!('{repository}', project&.name) + + content = email + content.gsub!('{receiver}', receiver&.real_name) + content.gsub!('{baseurl}', base_url) + content.gsub!('{login1}', operator&.login) + content.gsub!('{nickname1}', operator&.real_name) + content.gsub!('{nickname2}', owner&.real_name) + content.gsub!('{repository}', project&.name) + content.gsub!('{login2}', owner&.login) + content.gsub!('{identifier}', project&.identifier) + content.gsub!('{id}', pull_request&.id.to_s) + content.gsub!('{title}', pull_request&.title) + + return receiver&.mail, title, content + rescue => e + Rails.logger.info("MessageTemplate::ProjectPullRequest.get_email_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/project_role.rb b/app/models/message_template/project_role.rb new file mode 100644 index 000000000..4db81ab47 --- /dev/null +++ b/app/models/message_template/project_role.rb @@ -0,0 +1,45 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 账号仓库权限变更 +class MessageTemplate::ProjectRole < MessageTemplate + + # MessageTemplate::ProjectRole.get_message_content(User.where(login: 'yystopf'), Project.last, '管理员') + def self.get_message_content(receivers, project, role) + content = sys_notice.gsub('{repository}', project&.name).gsub('{role}', role) + url = notification_url.gsub('{owner}', project&.owner&.login).gsub('{identifier}', project&.identifier) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::ProjectRole.get_message_content [ERROR] #{e}") + return '', '', '' + end + + def self.get_email_message_content(receivers, project, role) + title = email_title + title.gsub!('{repository}', project&.name) + title.gsub!('{role}', role) + content = email + content.gsub!('{receiver}', receiver&.real_name) + content.gsub!('{baseurl}', base_url) + content.gsub!('{login}', project&.owner&.login) + content.gsub!('{identifier}', project&.identifier) + content.gsub!('{repository}', project&.name) + content.gsub!('{role}', role) + + return receiver&.mail, title, content + rescue => e + Rails.logger.info("MessageTemplate::ProjectRole.get_email_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/project_setting_changed.rb b/app/models/message_template/project_setting_changed.rb new file mode 100644 index 000000000..3a8df5ffc --- /dev/null +++ b/app/models/message_template/project_setting_changed.rb @@ -0,0 +1,277 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 我管理的仓库项目设置被更改 +class MessageTemplate::ProjectSettingChanged < MessageTemplate + + # MessageTemplate::ProjectSettingChanged.get_message_content(User.where(login: 'yystopf'), User.last, Project.last, {description: '测试修改项目简介', category: '大数据', language: 'Ruby', permission: '公有', navbar: '易修, 合并请求'}) + def self.get_message_content(receivers, operator, project, change_params) + return '', '', '' if change_params.blank? + owner = project&.owner + content = sys_notice.gsub('{nickname1}', operator&.real_name).gsub('{nickname2}', owner&.real_name).gsub('{repository}', project&.name) + url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier) + change_count = change_params.keys.size + # 项目名称更改 + if change_params[:name].present? + if change_count > 1 + content.sub!('{ifname}', '
') + else + content.sub!('{ifname}', '') + end + content.sub!('{endname}', '') + content.gsub!('{name}', change_params[:name][1]) + else + content.gsub!(/({ifname})(.*)({endname})/, '') + end + # 项目简介更改 + if change_params[:description].present? + if change_params[:description][1].blank? + if change_count > 1 + content.gsub!(/({ifdescription})(.*)({enddescription})/, '
删除了项目简介') + else + content.gsub!(/({ifdescription})(.*)({enddescription})/, '删除了项目简介') + end + else + if change_count > 1 + content.sub!('{ifdescription}', '
') + else + content.sub!('{ifdescription}', '') + end + content.sub!('{enddescription}', '') + content.gsub!('{description}', change_params[:description][1]) + end + else + content.gsub!(/({ifdescription})(.*)({enddescription})/, '') + end + # 项目类别更改 + if change_params[:project_category_id].present? + category = ProjectCategory.find_by_id(change_params[:project_category_id][1]) + if category.present? + if change_count > 1 + content.sub!('{ifcategory}', '
') + else + content.sub!('{ifcategory}', '') + end + content.sub!('{endcategory}', '') + content.gsub!('{category}', category&.name) + else + if change_count > 1 + content.gsub!(/({ifcategory})(.*)({endcategory})/, '
删除了项目类别') + else + content.gsub!(/({ifcategory})(.*)({endcategory})/, '删除了项目类别') + end + end + else + content.gsub!(/({ifcategory})(.*)({endcategory})/, '') + end + # 项目语言更改 + if change_params[:project_language_id].present? + language = ProjectLanguage.find_by_id(change_params[:project_language_id][1]) + if language.present? + if change_count > 1 + content.sub!('{iflanguage}', '
') + else + content.sub!('{iflanguage}', '') + end + content.sub!('{endlanguage}', '') + content.gsub!('{language}', language&.name) + else + if change_count > 1 + content.gsub!(/({iflanguage})(.*)({endlanguage})/, '
删除了项目语言') + else + content.gsub!(/({iflanguage})(.*)({endlanguage})/, '删除了项目语言') + end + end + else + content.gsub!(/({iflanguage})(.*)({endlanguage})/, '') + end + # 项目公私有更改 + if change_params[:is_public].present? + permission = change_params[:is_public][1] ? '公有' : '私有' + if change_count > 1 + content.sub!('{ifpermission}', '
') + else + content.sub!('{ifpermission}', '') + end + content.sub!('{endpermission}', '') + content.gsub!('{permission}', permission) + else + content.gsub!(/({ifpermission})(.*)({endpermission})/, '') + end + # 项目导航更改 + if change_params[:navbar].present? + unit_types = project.project_units.order(unit_type: :asc).pluck(:unit_type) + unit_types.delete('code') + unit_types.unshift('代码库') + unit_types.unshift('主页') + unit_types.append('动态') + navbar = unit_types.join(',') + navbar.gsub!('issues', '易修') + navbar.gsub!('pulls', '合并请求') + navbar.gsub!('wiki', 'Wiki') + navbar.gsub!('devops', '工作流') + navbar.gsub!('versions', '里程碑') + navbar.gsub!('resources', '资源库') + if change_count > 1 + content.sub!('{ifnavbar}', '
') + else + content.sub!('{ifnavbar}', '') + end + content.sub!('{endnavbar}', '') + content.gsub!('{navbar}', navbar) + else + content.gsub!(/({ifnavbar})(.*)({endnavbar})/, '') + end + + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::ProjectSettingChanged.get_message_content [ERROR] #{e}") + return '', '', '' + end + + def self.get_email_message_content(receiver, operator, project, change_params) + return '', '', '' if change_params.blank? + owner = project&.owner + title = email_title + title.gsub!('{nickname2}', owner&.real_name) + title.gsub!('{repository}', project&.name) + + content = email + content.gsub!('{receiver}', receiver&.real_name) + content.gsub!('{baseurl}', base_url) + content.gsub!('{login1}', operator&.login) + content.gsub!('{nickname1}', operator&.real_name) + content.gsub!('{login2}', owner&.login) + content.gsub!('{nickname2}', owner&.real_name) + content.gsub!('{identifier}', project&.identifier) + content.gsub!('{repository}', project&.name) + change_count = change_params.keys.size + # 项目名称更改 + if change_params[:name].present? + if change_count > 1 + content.sub!('{ifname}', '
') + else + content.sub!('{ifname}', '') + end + content.sub!('{endname}', '') + content.gsub!('{name}', change_params[:name][1]) + else + content.gsub!(/({ifname})(.*)({endname})/, '') + end + # 项目简介更改 + if change_params[:description].present? + if change_params[:description][1].blank? + if change_count > 1 + content.gsub!(/({ifdescription})(.*)({enddescription})/, '
删除了项目简介') + else + content.gsub!(/({ifdescription})(.*)({enddescription})/, '删除了项目简介') + end + else + if change_count > 1 + content.sub!('{ifdescription}', '
') + else + content.sub!('{ifdescription}', '') + end + content.sub!('{enddescription}', '') + content.gsub!('{description}', change_params[:description][1]) + end + else + content.gsub!(/({ifdescription})(.*)({enddescription})/, '') + end + # 项目类别更改 + if change_params[:project_category_id].present? + category = ProjectCategory.find_by_id(change_params[:project_category_id][1]) + if category.present? + if change_count > 1 + content.sub!('{ifcategory}', '
') + else + content.sub!('{ifcategory}', '') + end + content.sub!('{endcategory}', '') + content.gsub!('{category}', category&.name) + else + if change_count > 1 + content.gsub!(/({ifcategory})(.*)({endcategory})/, '
删除了项目类别') + else + content.gsub!(/({ifcategory})(.*)({endcategory})/, '删除了项目类别') + end + end + else + content.gsub!(/({ifcategory})(.*)({endcategory})/, '') + end + # 项目语言更改 + if change_params[:project_language_id].present? + language = ProjectLanguage.find_by_id(change_params[:project_language_id][1]) + if language.present? + if change_count > 1 + content.sub!('{iflanguage}', '
') + else + content.sub!('{iflanguage}', '') + end + content.sub!('{endlanguage}', '') + content.gsub!('{language}', language&.name) + else + if change_count > 1 + content.gsub!(/({iflanguage})(.*)({endlanguage})/, '
删除了项目语言') + else + content.gsub!(/({iflanguage})(.*)({endlanguage})/, '删除了项目语言') + end + end + else + content.gsub!(/({iflanguage})(.*)({endlanguage})/, '') + end + # 项目公私有更改 + if change_params[:is_public].present? + permission = change_params[:is_public][1] ? '公有' : '私有' + if change_count > 1 + content.sub!('{ifpermission}', '
') + else + content.sub!('{ifpermission}', '') + end + content.sub!('{endpermission}', '') + content.gsub!('{permission}', permission) + else + content.gsub!(/({ifpermission})(.*)({endpermission})/, '') + end + # 项目导航更改 + if change_params[:navbar].present? + unit_types = project.project_units.order(unit_type: :asc).pluck(:unit_type) + unit_types.delete('code') + unit_types.unshift('代码库') + unit_types.unshift('主页') + unit_types.append('动态') + navbar = unit_types.join(',') + navbar.gsub!('issues', '易修') + navbar.gsub!('pulls', '合并请求') + navbar.gsub!('wiki', 'Wiki') + navbar.gsub!('devops', '工作流') + navbar.gsub!('versions', '里程碑') + navbar.gsub!('resources', '资源库') + if change_count > 1 + content.sub!('{ifnavbar}', '
') + else + content.sub!('{ifnavbar}', '') + end + content.sub!('{endnavbar}', '') + content.gsub!('{navbar}', navbar) + else + content.gsub!(/({ifnavbar})(.*)({endnavbar})/, '') + end + + return receiver&.mail, title, content + rescue => e + Rails.logger.info("MessageTemplate::ProjectSettingChanged.get_email_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/project_transfer.rb b/app/models/message_template/project_transfer.rb new file mode 100644 index 000000000..7a99276ba --- /dev/null +++ b/app/models/message_template/project_transfer.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# TODO 我关注的仓库被转移 +class MessageTemplate::ProjectTransfer < MessageTemplate + + # MessageTemplate::ProjectTransfer.get_message_content(User.where(login: 'yystopf')) + def self.get_message_content(receivers) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::ProjectTransfer.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/project_version.rb b/app/models/message_template/project_version.rb new file mode 100644 index 000000000..14ae46306 --- /dev/null +++ b/app/models/message_template/project_version.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# TODO 我关注的仓库有新的发行版 +class MessageTemplate::ProjectVersion < MessageTemplate + + # MessageTemplate::ProjectVersion.get_message_content(User.where(login: 'yystopf')) + def self.get_message_content(receivers) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::ProjectVersion.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/pull_request_assigned.rb b/app/models/message_template/pull_request_assigned.rb new file mode 100644 index 000000000..54d51f3f3 --- /dev/null +++ b/app/models/message_template/pull_request_assigned.rb @@ -0,0 +1,54 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 有新指派给我的合并请求 +class MessageTemplate::PullRequestAssigned < MessageTemplate + + # MessageTemplate::PullRequestAssigned.get_message_content(User.where(login: 'yystopf'), User.last, PullRequest.last) + def self.get_message_content(receivers, operator, pull_request) + project = pull_request&.project + owner = project&.owner + content = sys_notice.gsub('{nickname1}', operator&.real_name).gsub('{nickname2}', owner&.real_name).gsub('{repository}', project&.name).gsub('{title}', pull_request&.title) + url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', pull_request&.id.to_s) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::PullRequestAssigned.get_message_content [ERROR] #{e}") + return '', '', '' + end + + def self.get_email_message_content(receiver, operator, pull_request) + project = pull_request&.project + owner = project&.owner + title = email_title + title.gsub!('{nickname1}', operator&.real_name) + title.gsub!('{nickname2}', owner&.real_name) + title.gsub!('{repository}', project&.name) + content = email + content.gsub!('{receiver}', receiver&.real_name) + content.gsub!('{nickname1}', operator&.real_name) + content.gsub!('{login1}', operator&.login) + content.gsub!('{nickname2}', owner&.real_name) + content.gsub!('{login2}', owner&.login) + content.gsub!('{identifier}', project&.identifier) + content.gsub!('{repository}', project&.name) + content.gsub!('{baseurl}', base_url) + content.gsub!('{title}', pull_request&.title) + content.gsub!('{id}', pull_request&.id.to_s) + + return receiver&.mail, title, content + rescue => e + Rails.logger.info("MessageTemplate::PullRequestAssigned.get_email_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/pull_request_atme.rb b/app/models/message_template/pull_request_atme.rb new file mode 100644 index 000000000..54f8c9585 --- /dev/null +++ b/app/models/message_template/pull_request_atme.rb @@ -0,0 +1,29 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 在合并请求中@我 +class MessageTemplate::PullRequestAtme < MessageTemplate + + # MessageTemplate::PullRequestAtme.get_message_content(User.where(login: 'yystopf'), User.last, PullRequest.last) + def self.get_message_content(receivers, operator, pull_request) + project = pull_request&.project + owner = project&.owner + content = sys_notice.gsub('{nickname}', operator&.real_name).gsub('{title}', pull_request&.title) + url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', pull_request&.id.to_s) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::PullRequestAtme.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/pull_request_changed.rb b/app/models/message_template/pull_request_changed.rb new file mode 100644 index 000000000..099607fbe --- /dev/null +++ b/app/models/message_template/pull_request_changed.rb @@ -0,0 +1,95 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 我创建或负责的合并请求状态变更 +class MessageTemplate::PullRequestChanged < MessageTemplate + + # MessageTemplate::PullRequestChanged.get_message_content(User.where(login: 'yystopf'), User.last, PullRequest.last, {assigned_to_id: [nil, 203], priority_id: [2, 4], fixed_version_id: [nil, 5], issue_tags_value: ["", "7"]}) + def self.get_message_content(receivers, operator, pull_request, change_params) + return '', '', '' if change_params.blank? + project = pull_request&.project + owner = project&.owner + issue = pull_request&.issue + content = sys_notice.gsub('{nickname1}', operator&.real_name).gsub('{nickname2}', owner&.real_name).gsub('{repository}', project&.name).gsub("{title}", pull_request&.title) + url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', pull_request&.id.to_s) + change_count = change_params.keys.size + # 合并请求审查成员修改 + if change_params[:assigned_to_id].present? + assigner1 = User.find_by_id(change_params[:assigned_to_id][0]) + assigner2 = User.find_by_id(change_params[:assigned_to_id][1]) + if change_count > 1 + content.sub!('{ifassigner}', '
') + else + content.sub!('{ifassigner}', '') + end + content.sub!('{endassigner}', '') + content.gsub!('{assigner1}', assigner1.present? ? assigner1&.real_name : '未指派成员') + content.gsub!('{assigner2}', assigner2.present? ? assigner2&.real_name : '未指派成员') + else + content.gsub!(/({ifassigner})(.*)({endassigner})/, '') + end + # 合并请求里程碑修改 + if change_params[:fixed_version_id].present? + fix_version1 = Version.find_by_id(change_params[:fixed_version_id][0]) + fix_version2 = Version.find_by_id(change_params[:fixed_version_id][1]) + if change_count > 1 + content.sub!('{ifmilestone}', '
') + else + content.sub!('{ifmilestone}', '') + end + content.sub!('{endmilestone}', '') + content.gsub!('{milestone1}', fix_version1.present? ? fix_version1&.name : '未选择里程碑') + content.gsub!('{milestone2}', fix_version2.present? ? fix_version2&.name : '未选择里程碑') + else + content.gsub!(/({ifmilestone})(.*)({endmilestone})/, '') + end + # 合并请求标记修改 + if change_params[:issue_tags_value].present? + issue_tags1 = IssueTag.where(id: change_params[:issue_tags_value][0]).distinct + issue_tags2 = IssueTag.where(id: change_params[:issue_tags_value][1]).distinct + tag1 = issue_tags1.pluck(:name).join(",").blank? ? '未选择标记' : issue_tags1.pluck(:name).join(",") + tag2 = issue_tags2.pluck(:name).join(",").blank? ? '未选择标记' : issue_tags2.pluck(:name).join(",") + if change_count > 1 + content.sub!('{iftag}', '
') + else + content.sub!('{iftag}', '') + end + content.sub!('{endtag}', '') + content.gsub!('{tag1}', tag1) + content.gsub!('{tag2}', tag2) + else + content.gsub!(/({iftag})(.*)({endtag})()/, '') + end + # 合并请求优先级修改 + if change_params[:priority_id].present? + priority1 = IssuePriority.find_by_id(change_params[:priority_id][0]) + priority2 = IssuePriority.find_by_id(change_params[:priority_id][1]) + if change_count > 1 + content.sub!('{ifpriority}', '
') + else + content.sub!('{ifpriority}', '') + end + content.sub!('{ifpriority}', '') + content.sub!('{endpriority}', '') + content.gsub!('{priority1}', priority1&.name) + content.gsub!('{priority2}', priority2&.name) + else + content.gsub!(/({ifpriority})(.*)({endpriority})/, '') + end + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::PullRequestChanged.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/pull_request_closed.rb b/app/models/message_template/pull_request_closed.rb new file mode 100644 index 000000000..6ebb23a63 --- /dev/null +++ b/app/models/message_template/pull_request_closed.rb @@ -0,0 +1,29 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 我创建或负责的合并请求被关闭 +class MessageTemplate::PullRequestClosed < MessageTemplate + + # MessageTemplate::PullRequestClosed.get_message_content(User.where(login: 'yystopf'), User.last, PullRequest.last) + def self.get_message_content(receivers, operator, pull_request) + project = pull_request&.project + owner = project&.owner + content = sys_notice.gsub('{title}', pull_request&.title) + url = notification_url + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::PullRequestClosed.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/pull_request_journal.rb b/app/models/message_template/pull_request_journal.rb new file mode 100644 index 000000000..9b2fae949 --- /dev/null +++ b/app/models/message_template/pull_request_journal.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# TODO 我创建或负责的合并请求有新的评论 +class MessageTemplate::PullRequestJournal < MessageTemplate + + # MessageTemplate::PullRequestJournal.get_message_content(User.where(login: 'yystopf')) + def self.get_message_content(receivers) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::PullRequestJournal.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/message_template/pull_request_merged.rb b/app/models/message_template/pull_request_merged.rb new file mode 100644 index 000000000..8df4255a0 --- /dev/null +++ b/app/models/message_template/pull_request_merged.rb @@ -0,0 +1,29 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + +# 我创建或负责的合并请求被合并 +class MessageTemplate::PullRequestMerged < MessageTemplate + + # MessageTemplate::PullRequestMerged.get_message_content(User.where(login: 'yystopf'), User.last, PullRequest.last) + def self.get_message_content(receivers, operator, pull_request) + project = pull_request&.project + owner = project&.owner + content = sys_notice.gsub('{title}', pull_request&.title) + url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', pull_request&.id.to_s) + return receivers_string(receivers), content, url + rescue => e + Rails.logger.info("MessageTemplate::PullRequestMerged.get_message_content [ERROR] #{e}") + return '', '', '' + end +end diff --git a/app/models/mirror.rb b/app/models/mirror.rb index 67ef73775..b71dce3fb 100644 --- a/app/models/mirror.rb +++ b/app/models/mirror.rb @@ -18,7 +18,7 @@ class Mirror < ApplicationRecord # 0: 同步镜像成功;1: 正在同步镜像;2: 同步失败; 默认值为0 enum status: { succeeded: 0, waiting: 1, failed: 2 } - after_update :websocket_boardcast, if: :saved_change_to_status? + # after_update :websocket_boardcast, if: :saved_change_to_status? belongs_to :repository, foreign_key: :repo_id diff --git a/app/models/organization_user.rb b/app/models/organization_user.rb index cf9e22371..1ad2abd9a 100644 --- a/app/models/organization_user.rb +++ b/app/models/organization_user.rb @@ -22,6 +22,9 @@ class OrganizationUser < ApplicationRecord validates :user_id, uniqueness: {scope: :organization_id} + after_create :send_create_message_to_notice_system + after_destroy :send_destroy_message_to_notice_system + def self.build(organization_id, user_id) org_user = self.find_by(organization_id: organization_id, user_id: user_id) return org_user unless org_user.nil? @@ -31,4 +34,12 @@ class OrganizationUser < ApplicationRecord def teams organization.teams.joins(:team_users).where(team_users: {user_id: user_id}) end + + def send_create_message_to_notice_system + SendTemplateMessageJob.perform_later('OrganizationJoined', self.user_id, self.organization_id) + end + + def send_destroy_message_to_notice_system + SendTemplateMessageJob.perform_later('OrganizationLeft', self.user_id, self.organization_id) + end end diff --git a/app/models/project_unit.rb b/app/models/project_unit.rb index 5c519e1d5..cc35a6b28 100644 --- a/app/models/project_unit.rb +++ b/app/models/project_unit.rb @@ -32,9 +32,13 @@ class ProjectUnit < ApplicationRecord types.delete("pulls") if project.sync_mirror? # 默认code类型自动创建 types << "code" + before_units = project.project_units.pluck(:unit_type).sort project.project_units.where.not(unit_type: types).each(&:destroy!) types.each do |type| project.project_units.find_or_create_by!(unit_type: type) end + after_units = project.project_units.pluck(:unit_type).sort + return before_units, after_units end + end diff --git a/app/models/team.rb b/app/models/team.rb index 72df05097..19d05c77a 100644 --- a/app/models/team.rb +++ b/app/models/team.rb @@ -54,4 +54,15 @@ class Team < ApplicationRecord team_users.where(user_id: user_id).present? end + def authorize_name + case self.authorize + when 'read' then '报告者' + when 'write' then '开发者' + when 'admin' then '管理员' + when 'owner' then '拥有者' + else + '' + end + end + end diff --git a/app/models/user.rb b/app/models/user.rb index fb2a0d376..7cc1bcc20 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -187,7 +187,7 @@ class User < Owner :show_email, :show_location, :show_department, :technical_title, :province, :city, :custom_department, to: :user_extension, allow_nil: true - before_save :update_hashed_password, :set_lastname, :set_profile_completed + before_save :update_hashed_password, :set_lastname after_create do SyncTrustieJob.perform_later("user", 1) if allow_sync_to_trustie? end @@ -770,6 +770,10 @@ class User < Owner laboratory_id.present? && laboratory_id != 1 end + def profile_is_completed? + self.nickname.present? && self.gender.present? && self.mail.present? && self.custom_department.present? + end + protected def validate_password_length # 管理员的初始密码是5位 @@ -796,10 +800,6 @@ class User < Owner def set_lastname self.lastname = self.nickname if changes[:nickname].present? end - - def set_profile_completed - self.profile_completed = self.nickname.present? && self.gender.present? && self.mail.present? && self.custom_department.present? - end end diff --git a/app/models/version_release.rb b/app/models/version_release.rb index 16b823a78..3c97420ea 100644 --- a/app/models/version_release.rb +++ b/app/models/version_release.rb @@ -17,6 +17,7 @@ # created_at :datetime not null # updated_at :datetime not null # repository_id :integer +# sha :string(255) # # Indexes # @@ -29,4 +30,9 @@ class VersionRelease < ApplicationRecord has_many :project_trends, as: :trend, dependent: :destroy scope :releases_size, ->{where(draft: false, prerelease: false).size} has_many :attachments, as: :container, dependent: :destroy + + def update_sha + git_release = Gitea::Versions::GetService.call(user.gitea_token, repository&.owner&.login, repository&.identifier, version_gid) + self.update(sha: git_release["sha"]) + end end diff --git a/app/models/watcher.rb b/app/models/watcher.rb index ccc8eefaa..7ff20943e 100644 --- a/app/models/watcher.rb +++ b/app/models/watcher.rb @@ -24,6 +24,7 @@ class Watcher < ApplicationRecord after_save :reset_cache_data after_destroy :reset_cache_data + after_create :send_create_message_to_notice_system def reset_cache_data if self.watchable.is_a?(User) @@ -35,4 +36,8 @@ class Watcher < ApplicationRecord self.reset_platform_cache_async_job end + def send_create_message_to_notice_system + SendTemplateMessageJob.perform_later('FollowTip', self.id) if self.watchable.is_a?(User) + end + end diff --git a/app/queries/projects/list_query.rb b/app/queries/projects/list_query.rb index 771d4c927..4658408d2 100644 --- a/app/queries/projects/list_query.rb +++ b/app/queries/projects/list_query.rb @@ -11,7 +11,7 @@ class Projects::ListQuery < ApplicationQuery end def call - q = Project.all_visible(current_user_id).by_name_or_identifier(params[:search]) + q = Project.visible.by_name_or_identifier(params[:search]) scope = q .with_project_type(params[:project_type]) diff --git a/app/services/admins/delete_unit_apply_service.rb b/app/services/admins/delete_unit_apply_service.rb index a64c8d64d..af2e9668d 100644 --- a/app/services/admins/delete_unit_apply_service.rb +++ b/app/services/admins/delete_unit_apply_service.rb @@ -18,7 +18,7 @@ class Admins::DeleteUnitApplyService < ApplicationService use_extensions = UserExtension&.where(school_id: @unit_apply.school_id) user_ids = UserExtension&.where(school_id: @unit_apply.school_id)&.pluck(:user_id) - User.where(id: user_ids).update_all(profile_completed: false) + User.where(id: user_ids) use_extensions.update_all(school_id: nil,department_id: nil) @unit_apply&.user&.user_extension&.update_attribute("department_id", nil) diff --git a/app/services/admins/import_user_service.rb b/app/services/admins/import_user_service.rb index 5d4c2e10f..2fac412e1 100644 --- a/app/services/admins/import_user_service.rb +++ b/app/services/admins/import_user_service.rb @@ -57,7 +57,7 @@ class Admins::ImportUserService < ApplicationService password: '12345678', phone: data.phone, mail: "#{prefix}#{data.student_id}@qq.com", - profile_completed: true + # profile_completed: true } ActiveRecord::Base.transaction do user = User.create!(attr) diff --git a/app/services/gitea/pull_request/files_service.rb b/app/services/gitea/pull_request/files_service.rb index 9785588e2..a8cb26627 100644 --- a/app/services/gitea/pull_request/files_service.rb +++ b/app/services/gitea/pull_request/files_service.rb @@ -24,7 +24,7 @@ class Gitea::PullRequest::FilesService < Gitea::ClientService def params Hash.new.merge(token: token) end - + def url "/repos/#{owner}/#{repo}/pulls/#{pull_number}/files".freeze end diff --git a/app/services/gitea/repository/branches/list_slice_service.rb b/app/services/gitea/repository/branches/list_slice_service.rb new file mode 100644 index 000000000..6b643831a --- /dev/null +++ b/app/services/gitea/repository/branches/list_slice_service.rb @@ -0,0 +1,22 @@ +class Gitea::Repository::Branches::ListSliceService < Gitea::ClientService + attr_reader :user, :repo + + def initialize(user, repo) + @user = user + @repo = repo + end + + def call + response = get(url, params) + render_200_response(response) + end + + private + def params + Hash.new.merge(token: user.gitea_token) + end + + def url + "/repos/#{user.login}/#{repo}/branches/branches_slice".freeze + end +end diff --git a/app/services/gitea/repository/commits/file_list_service.rb b/app/services/gitea/repository/commits/file_list_service.rb new file mode 100644 index 000000000..b1606a0f3 --- /dev/null +++ b/app/services/gitea/repository/commits/file_list_service.rb @@ -0,0 +1,43 @@ +# Get a list of all commits from a repository +class Gitea::Repository::Commits::FileListService < Gitea::ClientService + attr_reader :owner, :repo_name, :filepath, :args + + # sha: SHA or branch to start listing commits from (usually 'master') + # ex: + # Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier, + # sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call + def initialize(owner, repo_name, filepath, **args) + @owner = owner + @repo_name = repo_name + @filepath = filepath + @args = args + end + + def call + response = get(url, params) + render_result(response) + end + + private + def params + {sha: args[:sha] || 'master', page: args[:page] || PAGINATE_DEFAULT_PAGE, limit: args[:limit] || PAGINATE_DEFAULT_LIMIT, token: args[:token] || "" } + end + + def url + "/repos/#{owner}/#{repo_name}/file_commits/#{filepath}".freeze + end + + def render_result(response) + case response.status + when 200 + result = {} + headers = response.headers.to_hash + body = JSON.parse(response.body) + total_count = headers["x-total"] + result.merge(total_count: total_count.to_i, body: body) + else + nil + # {status: -1, message: "#{body['message']}"} + end + end +end diff --git a/app/services/gitea/repository/commits/list_slice_service.rb b/app/services/gitea/repository/commits/list_slice_service.rb new file mode 100644 index 000000000..04f45f55b --- /dev/null +++ b/app/services/gitea/repository/commits/list_slice_service.rb @@ -0,0 +1,42 @@ +# Get a list of all commits from a repository +class Gitea::Repository::Commits::ListSliceService < Gitea::ClientService + attr_reader :owner, :repo_name, :args + + # sha: SHA or branch to start listing commits from (usually 'master') + # ex: + # Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier, + # sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call + def initialize(owner, repo_name, **args) + @owner = owner + @repo_name = repo_name + @args = args + end + + def call + response = get(url, params) + render_result(response) + end + + private + def params + { sha: args[:sha] || 'master', page: args[:page] || PAGINATE_DEFAULT_PAGE, limit: args[:limit] || PAGINATE_DEFAULT_LIMIT, token: args[:token] || "" } + end + + def url + "/repos/#{owner}/#{repo_name}/commits_slice".freeze + end + + def render_result(response) + case response.status + when 200 + result = {} + headers = response.headers.to_hash + body = JSON.parse(response.body) + total_count = headers["x-total"] + result.merge(total_count: total_count.to_i, body: body) + else + nil + # {status: -1, message: "#{body['message']}"} + end + end +end diff --git a/app/services/gitea/repository/readme/dir_service.rb b/app/services/gitea/repository/readme/dir_service.rb new file mode 100644 index 000000000..587fb5d55 --- /dev/null +++ b/app/services/gitea/repository/readme/dir_service.rb @@ -0,0 +1,34 @@ +class Gitea::Repository::Readme::DirService < Gitea::ClientService + attr_reader :owner, :repo, :ref, :dir, :token + + def initialize(owner, repo, dir, ref='', token=nil) + @owner = owner + @repo = repo + @dir = dir + @ref = ref + @token = token + end + + def call + response = get(url, params) + status, message, body = render_response(response) + json_format(status, message, body) + end + + private + def params + Hash.new.merge(token: token, ref: ref) + end + + def url + "/repos/#{owner}/#{repo}/readme/#{dir}".freeze + end + + def json_format(status, message, body) + case status + when 200 then success(body) + when 404 then error(message, 404) + else error(message, status) + end + end +end \ No newline at end of file diff --git a/app/services/gitea/versions/get_service.rb b/app/services/gitea/versions/get_service.rb new file mode 100644 index 000000000..b3c6cf9cc --- /dev/null +++ b/app/services/gitea/versions/get_service.rb @@ -0,0 +1,37 @@ +# Get a list of all commits from a repository +class Gitea::Versions::GetService < Gitea::ClientService + attr_reader :token, :user_name, :repo, :gid, :args + + # sha: SHA or branch to start listing commits from (usually 'master') + def initialize(token, user_name, repo, gid, args={}) + @token = token + @user_name = user_name + @repo = repo + @gid = gid + @args = args + end + + def call + response = get(url, params) + render_result(response) + end + + private + def params + args.merge(token: token) + end + + def url + "/repos/#{@user_name}/#{@repo}/releases/#{@gid}".freeze + end + + def render_result(response) + body = JSON.parse(response.body) + case response.status + when 200 + body + else + {status: -1, message: "#{body['message']}"} + end + end +end diff --git a/app/services/notice/read/client_service.rb b/app/services/notice/read/client_service.rb new file mode 100644 index 000000000..95ab8159e --- /dev/null +++ b/app/services/notice/read/client_service.rb @@ -0,0 +1,108 @@ +class Notice::Read::ClientService < ApplicationService + attr_reader :url, :params + + def initialize(options={}) + @url = options[:url] + @params = options[:params] + end + + def post(url, params={}) + puts "[notice][read][POST] request params: #{params}" + conn.post do |req| + req.url = full_url(url) + req.body = params[:data].to_json + end + end + + def get(url, params={}) + puts "[notice][read][GET] request params: #{params}" + conn.get do |req| + req.url full_url(url, 'get') + params.each_pair do |key, value| + req.params["#{key}"] = value + end + end + end + + def delete(url, params={}) + puts "[notice][read][DELETE] request params: #{params}" + conn.delete do |req| + req.url full_url(url) + req.body = params[:data].to_json + end + end + + def patch(url, params={}) + puts "[notice][read][PATCH] request params: #{params}" + conn.patch do |req| + req.url full_url(url) + req.body = params[:data].to_json + end + end + + def put(url, params={}) + puts "[notice][read][PUT] request params: #{params}" + conn.put do |req| + req.url full_url(url) + req.body = params[:data].to_json + end + end + + def platform + Notice.notice_config[:platform] + end + + private + def conn + @client ||= begin + Faraday.new(url: domain) do |req| + req.request :url_encoded + req.headers['Content-Type'] = 'application/json' + req.adapter Faraday.default_adapter + end + end + + @client + end + + def base_url + Notice.notice_config[:base_url] + end + + def domain + Notice.notice_config[:read_domain] + end + + def api_url + [domain, base_url].join('') + end + + 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 "[notice][read] request url: #{url}" + return url + end + + def log_error(status, body) + puts "[notice][read] status: #{status}" + puts "[notice][read] body: #{body}" + end + + def render_response(response) + status = response.status + body = JSON.parse(response&.body) + + log_error(status, body) + + if status == 200 + if body["code"] == 1 + return [body["code"], body["message"], body["data"]] + else + puts "[notice][read][ERROR] code: #{body["code"]}" + puts "[notice][read][ERROR] message: #{body["message"]}" + end + end + end +end \ No newline at end of file diff --git a/app/services/notice/read/count_service.rb b/app/services/notice/read/count_service.rb new file mode 100644 index 000000000..fade12278 --- /dev/null +++ b/app/services/notice/read/count_service.rb @@ -0,0 +1,25 @@ +class Notice::Read::CountService < Notice::Read::ClientService + attr_accessor :receiver, :type + + def initialize(receiver, type=-1) + @receiver = receiver + @type = type + end + + def call + result = get(url, request_params) + response = render_response(result) + end + + private + def request_params + { + receiver: receiver, + type: type + }.stringify_keys + end + + def url + "/notification/#{platform}/count".freeze + end +end \ No newline at end of file diff --git a/app/services/notice/read/list_service.rb b/app/services/notice/read/list_service.rb new file mode 100644 index 000000000..3f6645d77 --- /dev/null +++ b/app/services/notice/read/list_service.rb @@ -0,0 +1,32 @@ +class Notice::Read::ListService < Notice::Read::ClientService + attr_accessor :receiver, :type, :status, :page, :size + + def initialize(receiver, type=-1, status=2, page=1, size=15) + @receiver = receiver + @type = type + @status = status + @page = page + @size = size + end + + def call + result = get(url, request_params) + response = render_response(result) + end + + private + + def request_params + { + receiver: receiver, + page: page, + status: status, + size: size, + type: type + }.stringify_keys + end + + def url + "/notification/#{platform}/list".freeze + end +end \ No newline at end of file diff --git a/app/services/notice/write/change_status_service.rb b/app/services/notice/write/change_status_service.rb new file mode 100644 index 000000000..de2c89815 --- /dev/null +++ b/app/services/notice/write/change_status_service.rb @@ -0,0 +1,35 @@ +class Notice::Write::ChangeStatusService < Notice::Write::ClientService + attr_accessor :notification_ids, :receiver, :type, :status + + def initialize(notification_ids, receiver, type=-1, status=2) + @notification_ids = notification_ids + @receiver = receiver + @type = type + @status = status + end + + def call + result = put(url, request_params) + response = render_response(result) + end + + private + + def request_notification_ids + notification_ids.join(",") + end + + def request_params + Hash.new.merge(data: { + notificationIds: request_notification_ids, + receiver: receiver, + type: type, + status: status + }.stringify_keys) + end + + def url + "/notification/#{platform}".freeze + end + +end \ No newline at end of file diff --git a/app/services/notice/write/client_service.rb b/app/services/notice/write/client_service.rb new file mode 100644 index 000000000..c76691cfb --- /dev/null +++ b/app/services/notice/write/client_service.rb @@ -0,0 +1,108 @@ +class Notice::Write::ClientService < ApplicationService + attr_reader :url, :params + + def initialize(options={}) + @url = options[:url] + @params = options[:params] + end + + def post(url, params={}) + puts "[notice][write][POST] request params: #{params}" + conn.post do |req| + req.url full_url(url) + req.body = params[:data].to_json + end + end + + def get(url, params={}) + puts "[notice][write][GET] request params: #{params}" + conn.get do |req| + req.url full_url(url, 'get') + params.each_pair do |key, value| + req.params["#{key}"] = value + end + end + end + + def delete(url, params={}) + puts "[notice][write][DELETE] request params: #{params}" + conn.delete do |req| + req.url full_url(url) + req.body = params[:data].to_json + end + end + + def patch(url, params={}) + puts "[notice][write][PATCH] request params: #{params}" + conn.patch do |req| + req.url full_url(url) + req.body = params[:data].to_json + end + end + + def put(url, params={}) + puts "[notice][write][PUT] request params: #{params}" + conn.put do |req| + req.url full_url(url) + req.body = params[:data].to_json + end + end + + def platform + Notice.notice_config[:platform] + end + + private + def conn + @client ||= begin + Faraday.new(url: domain) do |req| + req.request :url_encoded + req.headers['Content-Type'] = 'application/json' + req.adapter Faraday.default_adapter + end + end + + @client + end + + def base_url + Notice.notice_config[:base_url] + end + + def domain + Notice.notice_config[:write_domain] + end + + def api_url + [domain, base_url].join('') + end + + 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 "[notice][write] request url: #{url}" + return url + end + + def log_error(status, body) + puts "[notice][write] status: #{status}" + puts "[notice][write] body: #{body}" + end + + def render_response(response) + status = response.status + body = JSON.parse(response&.body) + + log_error(status, body) + + if status == 200 + if body["code"] == 1 + return [body["code"], body["message"], body["data"]] + else + puts "[notice][write][ERROR] code: #{body["code"]}" + puts "[notice][write][ERROR] message: #{body["message"]}" + end + end + end +end \ No newline at end of file diff --git a/app/services/notice/write/create_service.rb b/app/services/notice/write/create_service.rb new file mode 100644 index 000000000..8dfc88483 --- /dev/null +++ b/app/services/notice/write/create_service.rb @@ -0,0 +1,42 @@ +class Notice::Write::CreateService < Notice::Write::ClientService + attr_accessor :receivers, :sender, :content, :notification_url, :source, :extra, :type + + def initialize(receivers, content, notification_url, source, extra={}, type=1, sender=-1) + @receivers = receivers + @sender = sender + @content = content + @notification_url = notification_url + @source = source + @extra = extra + @type = type + end + + def call + return nil if request_receivers.blank? + result = post(url, request_params) + response = render_response(result) + end + + private + + def request_receivers + receivers.is_a?(Array) ? receivers.join(",") : receivers + end + + def request_params + Hash.new.merge(data: { + receivers: request_receivers, + sender: sender, + content: content, + notification_url: notification_url, + source: source, + extra: extra.to_json.to_s, + type: type + }.stringify_keys) + end + + def url + "/notification/#{platform}".freeze + end + +end \ No newline at end of file diff --git a/app/services/notice/write/delete_service.rb b/app/services/notice/write/delete_service.rb new file mode 100644 index 000000000..f63584819 --- /dev/null +++ b/app/services/notice/write/delete_service.rb @@ -0,0 +1,33 @@ +class Notice::Write::DeleteService < Notice::Write::ClientService + attr_accessor :notification_ids, :receiver, :type + + def initialize(notification_ids, receiver, type=-1) + @notification_ids = notification_ids + @receiver = receiver + @type = type + end + + def call + result = delete(url, request_params) + response = render_response(result) + end + + private + + def request_notification_ids + notification_ids.join(",") + end + + def request_params + Hash.new.merge(data: { + notificationIds: request_notification_ids, + receiver: receiver, + type: type + }.stringify_keys) + end + + def url + "/notification/#{platform}".freeze + end + +end \ No newline at end of file diff --git a/app/services/notice/write/email_create_service.rb b/app/services/notice/write/email_create_service.rb new file mode 100644 index 000000000..070b42689 --- /dev/null +++ b/app/services/notice/write/email_create_service.rb @@ -0,0 +1,40 @@ +class Notice::Write::EmailCreateService < Notice::Write::ClientService + attr_accessor :receivers, :sender, :content, :subject + + def initialize(receivers, subject, content, sender=-1) + @receivers = receivers + @sender = sender + @content = content + @subject = subject + end + + def call + return nil if request_receivers.blank? + result = post(url, request_params) + response = render_response(result) + end + + private + + def request_receivers + receivers.is_a?(Array) ? receivers.join(",") : receivers + end + + def request_subject + "Trustie: #{subject}" + end + + def request_params + Hash.new.merge(data: { + emails: request_receivers, + sender: sender, + content: content, + subject: request_subject + }.stringify_keys) + end + + def url + "/email/#{platform}".freeze + end + +end \ No newline at end of file diff --git a/app/services/projects/accept_join_service.rb b/app/services/projects/accept_join_service.rb index 69cb97603..b1a996fd8 100644 --- a/app/services/projects/accept_join_service.rb +++ b/app/services/projects/accept_join_service.rb @@ -53,6 +53,8 @@ class Projects::AcceptJoinService < ApplicationService def operate_project_member Projects::AddMemberInteractor.call(@project.owner, @project, @applied_project.user, permission) + SendTemplateMessageJob.perform_later('ProjectJoined', @user.id, @applied_project.user_id, @project.id) + SendTemplateMessageJob.perform_later('ProjectMemberJoined', @user.id, @applied_project.user_id, @project.id) end def send_apply_message diff --git a/app/services/projects/apply_transfer_service.rb b/app/services/projects/apply_transfer_service.rb index 2953cd3d2..98847a91c 100644 --- a/app/services/projects/apply_transfer_service.rb +++ b/app/services/projects/apply_transfer_service.rb @@ -24,7 +24,7 @@ class Projects::ApplyTransferService < ApplicationService raise Error, '仓库标识不正确' if @project.identifier != params[:identifier] raise Error, '该仓库正在迁移' if @project.is_transfering raise Error, '新拥有者不存在' unless @owner.present? - raise Error, '新拥有者资料不完善' if @owner.is_a?(User) && !@owner.profile_completed + raise Error, '新拥有者资料不完善' if @owner.is_a?(User) && !@owner.profile_is_completed? raise Error, '新拥有者已经存在同名仓库!' if Project.where(user_id: @owner.id, identifier: params[:identifier]).present? raise Error, '未拥有转移权限' unless is_permit_owner end diff --git a/app/services/repositories/detail_service.rb b/app/services/repositories/detail_service.rb index d8853cf45..4b29d50a7 100644 --- a/app/services/repositories/detail_service.rb +++ b/app/services/repositories/detail_service.rb @@ -8,27 +8,25 @@ class Repositories::DetailService < ApplicationService end def call - if @repo.project.educoder? - return { - repo: {}, - release: [], - branch: [], - tag: [], - contributor: [], - language: {}, - readme: {} - } - else - return { - repo: repo_suitable, - release: release_suitable, - branch: branch_suitable, - tag: tag_suitable, - contributor: contributor_suitable, - language: language_suitable, - readme: readme_suitable - } - end + return { + repo: repo_suitable, + release: release_suitable, + branch: branch_suitable, + tag: tag_suitable, + contributor: contributor_suitable, + language: language_suitable + } + rescue + return { + repo: {}, + release: [], + branch: [], + branch_type: [], + tag: [], + contributor: [], + language: {}, + readme: {} + } end private @@ -43,7 +41,7 @@ class Repositories::DetailService < ApplicationService def branch_suitable branches = Gitea::Repository::Branches::ListService.call(@owner, @repo.identifier) - branches.is_a?(Hash) && branches[:status] == :error ? [] : branches + branches.is_a?(Hash) && branches.key?(:status) ? [] : branches end def tag_suitable @@ -60,9 +58,4 @@ class Repositories::DetailService < ApplicationService result = Gitea::Repository::Languages::ListService.call(@owner.login, @repo.identifier, @user&.gitea_token) result[:status] === :success ? hash_transform_precentagable(result[:body]) : nil end - - def readme_suitable - result = Gitea::Repository::Readme::GetService.call(@owner.login, @repo.identifier, @repo.default_branch, @owner.gitea_token) - result[:status] === :success ? result[:body] : nil - end end diff --git a/app/services/users/apply_authentication_service.rb b/app/services/users/apply_authentication_service.rb index a6b02f431..5e2562105 100644 --- a/app/services/users/apply_authentication_service.rb +++ b/app/services/users/apply_authentication_service.rb @@ -7,7 +7,7 @@ class Users::ApplyAuthenticationService < ApplicationService end def call - raise Error, '请先完善基本信息' unless user.profile_completed? + raise Error, '请先完善基本信息' unless user.profile_is_completed? Users::ApplyAuthenticationForm.new(params).validate! # raise Error, '您已经申请过实名认证了' if ApplyUserAuthentication.real_name_auth.processing.exists?(user_id: user.id) diff --git a/app/services/users/apply_professional_auth_service.rb b/app/services/users/apply_professional_auth_service.rb index c94481890..9f3057ddb 100644 --- a/app/services/users/apply_professional_auth_service.rb +++ b/app/services/users/apply_professional_auth_service.rb @@ -9,7 +9,7 @@ class Users::ApplyProfessionalAuthService < ApplicationService end def call - raise Error, '请先完善基本信息' unless user.profile_completed? + raise Error, '请先完善基本信息' unless user.profile_is_completed? Users::ApplyProfessionalAuthForm.new(params).validate! # raise Error, '您已经申请过职业认证了' if ApplyUserAuthentication.professional_auth.processing.exists?(user_id: user.id) diff --git a/app/services/users/update_account_service.rb b/app/services/users/update_account_service.rb index 16824a90e..8a0b1885d 100644 --- a/app/services/users/update_account_service.rb +++ b/app/services/users/update_account_service.rb @@ -43,7 +43,7 @@ class Users::UpdateAccountService < ApplicationService end # 表示资料完整 - user.profile_completed = true + # user.profile_completed = true extension.save! user.save! diff --git a/app/views/admins/message_templates/_form.html.erb b/app/views/admins/message_templates/_form.html.erb new file mode 100644 index 000000000..4b2a6c767 --- /dev/null +++ b/app/views/admins/message_templates/_form.html.erb @@ -0,0 +1,65 @@ +
+
<%= type == "create" ? "新建" : "编辑" %><%= @message_template.simple_type %>消息模版
+ <%= link_to "返回", admins_message_templates_path, class: "btn btn-default pull-right" %> +
+ +
+ <%= form_for @message_template, url: {controller: "message_templates", action: "#{type}"} do |f| %> + +
+ +
+ <%= f.text_area :sys_notice, class:"form-control", rows: "10", cols: "20",placeholer: "请输入站内信" %> +
+ + + +
+
+ +
+ <%= f.text_field :notification_url, class: "form-control input-lg", maxlength: "60", placeholder: "请输入站内信跳转地址" %> +
+ +
+
+ +
+ <%= f.text_field :email_title, class: "form-control input-lg", maxlength: "60", placeholder: "请输入邮件标题" %> +
+ +
+
+ +
+
+ <%= f.text_area :email, class:"form-control", style: 'display: none;', rows: "10", cols: "20", placeholer: "请输入邮件正文" %> +
+
+
+ +
+ <%= f.submit "确认", class: "btn btn-primary submit-btn" %> +
+ <% end %> +
\ No newline at end of file diff --git a/app/views/admins/message_templates/_list.html.erb b/app/views/admins/message_templates/_list.html.erb new file mode 100644 index 000000000..4082735f5 --- /dev/null +++ b/app/views/admins/message_templates/_list.html.erb @@ -0,0 +1,39 @@ + + + + + + + + + + + + + <% if message_templates.present? %> + <% message_templates.each_with_index do |message_template_type, index| %> + <% message_template = message_template_type.constantize.last%> + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
序号类型系统消息模版邮件模版通知地址操作
<%= list_index_no((params[:page] || 1).to_i, index) %><%= message_template.simple_type %> + <%= message_template.sys_notice.to_s.truncate(200) %> + + <%= message_template.email.to_s.truncate(100) %> + + <%= message_template.notification_url.to_s.truncate(200) %> + + <%= link_to "编辑", edit_admins_message_template_path(message_template),remote: true, class: "action" %> +
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: message_templates } %> \ No newline at end of file diff --git a/app/views/admins/message_templates/edit.js.erb b/app/views/admins/message_templates/edit.js.erb new file mode 100644 index 000000000..5d797af2f --- /dev/null +++ b/app/views/admins/message_templates/edit.js.erb @@ -0,0 +1,2 @@ +$("#admins-message-templates-content").html("<%= j render partial: 'admins/message_templates/form', locals:{type: 'update'} %>") +createMDEditor('message-template-email-editor', { height: 500, placeholder: '请输入邮件模版' }); diff --git a/app/views/admins/message_templates/index.html.erb b/app/views/admins/message_templates/index.html.erb new file mode 100644 index 000000000..54d273332 --- /dev/null +++ b/app/views/admins/message_templates/index.html.erb @@ -0,0 +1,12 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('消息模版') %> +<% end %> +
+
+ <%= link_to "初始化数据", init_data_admins_message_templates_path, class: "btn btn-primary pull-right", "data-disabled-with":"...初始化数据" %> +
+
+ <%= render partial: 'admins/message_templates/list', locals: { message_templates: @message_templates } %> +
+
+ diff --git a/app/views/admins/message_templates/index.js.erb b/app/views/admins/message_templates/index.js.erb new file mode 100644 index 000000000..d8e0fdd26 --- /dev/null +++ b/app/views/admins/message_templates/index.js.erb @@ -0,0 +1 @@ +$('.message-templates-list-container').html("<%= j( render partial: 'admins/message_templates/list', locals: { message_templates: @message_templates } ) %>"); \ No newline at end of file diff --git a/app/views/admins/shared/_sidebar.html.erb b/app/views/admins/shared/_sidebar.html.erb index e58337c18..402449b3b 100644 --- a/app/views/admins/shared/_sidebar.html.erb +++ b/app/views/admins/shared/_sidebar.html.erb @@ -30,6 +30,7 @@
  • <%= sidebar_item(admins_reversed_keywords_path, '系统保留关键词', icon: 'key', controller: 'admins-reversed_keywords') %>
  • +
  • <%= sidebar_item(admins_message_templates_path, '消息模版', icon: 'folder', controller: 'admins-message_templates') %>
  • <%= sidebar_item(admins_laboratories_path, '云上实验室', icon: 'cloud', controller: 'admins-laboratories') %>
  • diff --git a/app/views/organizations/organizations/_simple.json.jbuilder b/app/views/organizations/organizations/_simple.json.jbuilder index 12792b125..7fed762a2 100644 --- a/app/views/organizations/organizations/_simple.json.jbuilder +++ b/app/views/organizations/organizations/_simple.json.jbuilder @@ -1,5 +1,9 @@ -json.id organization.id -json.name organization.login -json.nickname organization.nickname.blank? ? organization.name : organization.nickname -json.description organization.description -json.avatar_url url_to_avatar(organization) +if organization.present? + json.id organization.id + json.name organization.login + json.nickname organization.nickname.blank? ? organization.name : organization.nickname + json.description organization.description + json.avatar_url url_to_avatar(organization) +else + nil +end \ No newline at end of file diff --git a/app/views/organizations/teams/show.json.jbuilder b/app/views/organizations/teams/show.json.jbuilder index d0290897a..c86f9b546 100644 --- a/app/views/organizations/teams/show.json.jbuilder +++ b/app/views/organizations/teams/show.json.jbuilder @@ -1,3 +1,6 @@ json.partial! "detail", team: @team, organization: @organization json.is_admin @is_admin -json.is_member @is_member \ No newline at end of file +json.is_member @is_member +json.organization do + json.partial! "organizations/organizations/simple", organization: @organization +end \ No newline at end of file diff --git a/app/views/projects/branches.json.jbuilder b/app/views/projects/branches.json.jbuilder index dd722c9d9..ad4f4328a 100644 --- a/app/views/projects/branches.json.jbuilder +++ b/app/views/projects/branches.json.jbuilder @@ -11,7 +11,11 @@ json.array! @branches do |branch| json.message branch['commit']['message'] json.timestamp render_unix_time(branch['commit']['timestamp']) json.time_from_now time_from_now(branch['commit']['timestamp']) - json.author branch['commit']['author'] - json.committer branch['commit']['committer'] + json.author do + json.partial! 'repositories/commit_author', user: render_commit_author(branch['commit']['author']), name: branch['commit']['author']['name'] + end + json.committer do + json.partial! 'repositories/commit_author', user: render_commit_author(branch['commit']['committer']), name: branch['commit']['committer']['name'] + end end end diff --git a/app/views/projects/branches_slice.json.jbuilder b/app/views/projects/branches_slice.json.jbuilder new file mode 100644 index 000000000..31c662a13 --- /dev/null +++ b/app/views/projects/branches_slice.json.jbuilder @@ -0,0 +1,24 @@ +json.array! @branches_slice do |branch_slice| + json.branch_type branch_slice['branch_name'] + json.list branch_slice['branches'].each do |branch| + json.name branch['name'] + json.user_can_push branch['user_can_push'] + json.user_can_merge branch['user_can_merge'] + json.protected branch['protected'] + json.http_url render_http_url(@project) + json.zip_url render_zip_url(@owner, @repository, branch['name']) + json.tar_url render_tar_url(@owner, @repository, branch['name']) + json.last_commit do + json.sha branch['commit']['id'] + json.message branch['commit']['message'] + json.timestamp render_unix_time(branch['commit']['timestamp']) + json.time_from_now time_from_now(branch['commit']['timestamp']) + json.author do + json.partial! 'repositories/commit_author', user: render_commit_author(branch['commit']['author']), name: branch['commit']['author']['name'] + end + json.committer do + json.partial! 'repositories/commit_author', user: render_commit_author(branch['commit']['committer']), name: branch['commit']['committer']['name'] + end + end + end +end \ No newline at end of file diff --git a/app/views/pull_requests/_detail.json.jbuilder b/app/views/pull_requests/_detail.json.jbuilder new file mode 100644 index 000000000..06c6377a9 --- /dev/null +++ b/app/views/pull_requests/_detail.json.jbuilder @@ -0,0 +1,24 @@ +pr = issue.pull_request +json.pull_request_id pr.id +json.pull_request_status pr.status +json.pull_request_head pr.head +json.pull_request_base pr.base +json.pull_request_staus pr.status == 1 ? "merged" : (pr.status == 2 ? "closed" : "open") +json.is_original pr.is_original +json.fork_project_id pr&.fork_project_id +json.fork_project_identifier pr&.fork_project&.identifier +json.fork_project_user pr&.fork_project&.owner.try(:login) + + +json.id issue.id +json.name issue.subject +json.pr_time time_from_now(pr.status == 1 ? pr.updated_at : issue.updated_on) +json.assign_user_name issue.get_assign_user.try(:show_real_name) +json.assign_user_login issue.get_assign_user.try(:login) +json.author_name issue.user.try(:show_real_name) +json.author_login issue.user.try(:login) +json.avatar_url url_to_avatar(issue.user) +json.priority issue.priority.try(:name) +json.version issue.version.try(:name) +json.journals_count issue.get_journals_size +json.issue_tags issue.get_issue_tags \ No newline at end of file diff --git a/app/views/pull_requests/create.json.jbuilder b/app/views/pull_requests/create.json.jbuilder new file mode 100644 index 000000000..1483420d7 --- /dev/null +++ b/app/views/pull_requests/create.json.jbuilder @@ -0,0 +1,2 @@ +json.partial! "commons/success" +json.partial! 'detail', pr: @pull_request, issue: @pull_request.issue \ No newline at end of file diff --git a/app/views/pull_requests/show.json.jbuilder b/app/views/pull_requests/show.json.jbuilder index 6f6343903..5c30bd172 100644 --- a/app/views/pull_requests/show.json.jbuilder +++ b/app/views/pull_requests/show.json.jbuilder @@ -5,7 +5,8 @@ json.project_identifier @project.identifier json.pr_time time_from_now(@pull_request.updated_at) json.commits_count @pull_request.commits_count json.files_count @pull_request.files_count -json.comments_count @pull_request.comments_count +json.comments_count @issue.journals.parent_journals.size +json.comments_total_count @issue.get_journals_size json.pull_request do json.extract! @pull_request, :id,:base, :head, :status,:fork_project_id, :is_original diff --git a/app/views/repositories/_commit.json.jbuilder b/app/views/repositories/_commit.json.jbuilder index 3f3dd5544..95cb03412 100644 --- a/app/views/repositories/_commit.json.jbuilder +++ b/app/views/repositories/_commit.json.jbuilder @@ -26,9 +26,9 @@ if @project.forge? end json.author do - json.partial! 'commit_author', user: render_commit_author(commit['author']), name: commit['commit']['author']['name'] + json.partial! 'commit_author', user: render_commit_author(commit['commit']['author']), name: commit['commit']['author']['name'] end json.committer do - json.partial! 'commit_author', user: render_commit_author(commit['committer']), name: commit['commit']['committer']['name'] + json.partial! 'commit_author', user: render_commit_author(commit['commit']['committer']), name: commit['commit']['committer']['name'] end end diff --git a/app/views/repositories/_commit_author.json.jbuilder b/app/views/repositories/_commit_author.json.jbuilder index 1478cca0e..c63edf9b1 100644 --- a/app/views/repositories/_commit_author.json.jbuilder +++ b/app/views/repositories/_commit_author.json.jbuilder @@ -2,10 +2,12 @@ if user json.id user.id json.login user.login json.name user.real_name + json.type user&.type json.image_url url_to_avatar(user) else json.id nil json.login name json.name name + json.type nil json.image_url User::Avatar.get_letter_avatar_url(name) end diff --git a/app/views/repositories/_simple_entry.json.jbuilder b/app/views/repositories/_simple_entry.json.jbuilder index cc9bdc3ae..6f33ac35e 100644 --- a/app/views/repositories/_simple_entry.json.jbuilder +++ b/app/views/repositories/_simple_entry.json.jbuilder @@ -9,7 +9,7 @@ if @project.forge? json.type entry['type'] json.size entry['size'] - json.content decode64_content(entry, @owner, @repository, @ref) + json.content decode64_content(entry, @owner, @repository, @ref, @path) json.target entry['target'] download_url = diff --git a/app/views/repositories/commit.json.jbuilder b/app/views/repositories/commit.json.jbuilder index 77f6e3f1b..b15abef77 100644 --- a/app/views/repositories/commit.json.jbuilder +++ b/app/views/repositories/commit.json.jbuilder @@ -9,3 +9,4 @@ json.parents @commit['parents'] do |parent| json.sha parent['sha'] # json.url EduSetting.get('host_name') + commit_repository_path(@repo, parent['sha']) end +json.branch @commit['branch'] diff --git a/app/views/repositories/commits.json.jbuilder b/app/views/repositories/commits.json.jbuilder index 9dd90446e..cf4a409f5 100644 --- a/app/views/repositories/commits.json.jbuilder +++ b/app/views/repositories/commits.json.jbuilder @@ -7,25 +7,31 @@ else json.array! @hash_commit[:body] do |commit| commiter = commit['committer'] - forge_user = - if commiter.present? - User.simple_select.find_by(gitea_uid: commiter['id']) - end + # forge_user = + # if commiter.present? + # User.simple_select.find_by(gitea_uid: commiter['id']) + # end json.sha commit['sha'] json.message commit['commit']['message'] json.timestamp render_unix_time(commit['commit']['author']['date']) json.time_from_now time_from_now(commit['commit']['author']['date']) - if forge_user - json.partial! 'author', user: forge_user - else - json.author do - json.id nil - json.login commit['commit']['author']['name'] - json.type nil - json.name commit['commit']['author']['name'] - json.image_url User::Avatar.get_letter_avatar_url(commit['commit']['author']['name']) - end + # if forge_user + # json.partial! 'author', user: forge_user + # else + # json.author do + # json.id nil + # json.login commit['commit']['author']['name'] + # json.type nil + # json.name commit['commit']['author']['name'] + # json.image_url User::Avatar.get_letter_avatar_url(commit['commit']['author']['name']) + # end + # end + json.author do + json.partial! 'commit_author', user: render_commit_author(commit['commit']['author']), name: commit['commit']['author']['name'] + end + json.committer do + json.partial! 'commit_author', user: render_commit_author(commit['commit']['committer']), name: commit['commit']['committer']['name'] end end end diff --git a/app/views/repositories/commits_slice.json.jbuilder b/app/views/repositories/commits_slice.json.jbuilder new file mode 100644 index 000000000..e252cda69 --- /dev/null +++ b/app/views/repositories/commits_slice.json.jbuilder @@ -0,0 +1,38 @@ +if @hash_commit.blank? #如果有状态值,则表示报错了 + json.total_count 0 + json.commits [] +else + json.total_count @hash_commit[:total_count] + json.commit_dates do + json.array! @hash_commit[:body] do |commit_date| + json.commit_date commit_date["commit_date"] + json.commits do + json.array! commit_date["Commits"] do |commit| + commiter = commit['committer'] + + forge_user = + if commiter.present? + User.simple_select.find_by(gitea_uid: commiter['id']) + end + + json.sha commit['sha'] + json.message commit['commit']['message'] + json.timestamp render_unix_time(commit['commit']['author']['date']) + json.time_from_now time_from_now(commit['commit']['author']['date']) + if forge_user + json.partial! 'author', user: forge_user + else + json.author do + json.id nil + json.login commit['commit']['author']['name'] + json.type nil + json.name commit['commit']['author']['name'] + json.image_url User::Avatar.get_letter_avatar_url(commit['commit']['author']['name']) + end + end + end + end + end + end + +end diff --git a/app/views/repositories/contributors.json.jbuilder b/app/views/repositories/contributors.json.jbuilder index a5edb37e0..9165cf948 100644 --- a/app/views/repositories/contributors.json.jbuilder +++ b/app/views/repositories/contributors.json.jbuilder @@ -6,7 +6,7 @@ json.contributors @contributors.each do |contributor| next end json.contributions contributor["contributions"] - json.gid contributor["id"] + # json.gid contributor["id"] json.login user.login json.type user&.type json.name user.real_name diff --git a/app/views/repositories/detail.json.jbuilder b/app/views/repositories/detail.json.jbuilder index c00ae3462..c08077088 100644 --- a/app/views/repositories/detail.json.jbuilder +++ b/app/views/repositories/detail.json.jbuilder @@ -1,11 +1,6 @@ json.content @project.content json.website @project.website json.lesson_url @project.lesson_url -if @result[:readme].blank? - json.readme nil -else - json.readme @result[:readme].merge(content: readme_render_decode64_content(@result[:readme]["content"], nil)) -end json.identifier render_identifier(@project) json.invite_code @project.invite_code json.name @project.name @@ -95,6 +90,6 @@ json.contributors do end json.total_count total_count end -json.languages @result[:language] +json.languages @result[:language].blank? ? nil : @result[:language] json.partial! 'author', locals: { user: @project.owner } diff --git a/app/views/repositories/entries.json.jbuilder b/app/views/repositories/entries.json.jbuilder index 8c028e6a2..8d1e67beb 100644 --- a/app/views/repositories/entries.json.jbuilder +++ b/app/views/repositories/entries.json.jbuilder @@ -61,5 +61,4 @@ if @project.forge? end end - json.readme @readme.merge(content: readme_render_decode64_content(@readme["content"], nil)) end diff --git a/app/views/repositories/tags.json.jbuilder b/app/views/repositories/tags.json.jbuilder index 9db3ff93e..d1ee3671e 100644 --- a/app/views/repositories/tags.json.jbuilder +++ b/app/views/repositories/tags.json.jbuilder @@ -4,8 +4,23 @@ json.array! @tags do |tag| json.id tag['id'] json.zipball_url render_zip_url(@owner, @repository, tag['name']) json.tarball_url render_tar_url(@owner, @repository, tag['name']) + json.tagger do + json.partial! 'commit_author', user: render_commit_author(tag['tagger']), name: tag['tagger']['name'] + end + json.time_ago time_from_now(tag['tagger']['date'].to_time) + json.created_at_unix tag['tagger']['date'].to_time.to_i + json.message tag['message'] json.commit do json.sha tag['commit']['sha'] + json.message tag['commit']['message'] + json.time_ago time_from_now(tag['commit']['commiter']['date'].to_time) + json.created_at_unix tag['commit']['commiter']['date'].to_time.to_i + json.committer do + json.partial! 'commit_author', user: render_commit_author(tag['commit']['commiter']), name: tag['commit']['commiter']['name'] + end + json.author do + json.partial! 'commit_author', user: render_commit_author(tag['commit']['author']), name: tag['commit']['author']['name'] + end end end end diff --git a/app/views/users/get_user_info.json.jbuilder b/app/views/users/get_user_info.json.jbuilder index 29fbbd2d2..c02a3160f 100644 --- a/app/views/users/get_user_info.json.jbuilder +++ b/app/views/users/get_user_info.json.jbuilder @@ -12,8 +12,8 @@ json.tidding_count 0 json.user_phone_binded @user.phone.present? json.need_edit_info @user.need_edit_info? # json.phone @user.phone -json.email @user.mail -json.profile_completed @user.profile_completed? +# json.email @user.mail +json.profile_completed @user.profile_is_completed? json.professional_certification @user.professional_certification json.devops_step @user.devops_step json.ci_certification @user.ci_certification? @@ -22,4 +22,5 @@ json.province @user.province json.city @user.city json.custom_department @user.custom_department json.description @user.description -json.(@user, :show_email, :show_department, :show_location) \ No newline at end of file +json.(@user, :show_email, :show_department, :show_location) +json.message_unread_total @message_unread_total diff --git a/app/views/users/messages/_message.json.jbuilder b/app/views/users/messages/_message.json.jbuilder new file mode 100644 index 000000000..9f6f0c504 --- /dev/null +++ b/app/views/users/messages/_message.json.jbuilder @@ -0,0 +1,24 @@ +json.id message["id"] +# json.receiver do +# json.partial! '/users/user_simple', locals: {user: current_user} +# end +json.status message["status"] +json.content message["content"] +json.notification_url message["notification_url"] +json.source message["source"] +json.time_ago time_from_now(message["created_at"].to_time) + +case message["type"] +when 1 + json.type "notification" +when 2 + json.type "atme" + json.sender do + sender = User.find_by_id(message["sender"]) + if sender.present? + json.partial! '/users/user_simple', locals: {user: sender} + else + json.nil + end + end +end diff --git a/app/views/users/messages/index.json.jbuilder b/app/views/users/messages/index.json.jbuilder new file mode 100644 index 000000000..b16d806b7 --- /dev/null +++ b/app/views/users/messages/index.json.jbuilder @@ -0,0 +1,7 @@ +json.total_count @data["records_count"] +json.type %w(notification atme).include?(params[:type]) ? params[:type] : "" +json.unread_notification @data["unread_notification"] +json.unread_atme @data["unread_atme"] +json.messages @data["records"].each do |message| + json.partial! "message", message: message.stringify_keys +end \ No newline at end of file diff --git a/app/views/version_releases/_version_release.json.jbuilder b/app/views/version_releases/_version_release.json.jbuilder new file mode 100644 index 000000000..1ccdbe617 --- /dev/null +++ b/app/views/version_releases/_version_release.json.jbuilder @@ -0,0 +1,21 @@ +json.version_id version.try(:id) +json.id version&.version_gid +json.tag_name version&.tag_name +json.target_commitish version&.target_commitish +json.name version&.name +json.sha version&.sha +json.body version&.body +json.url version&.url +json.tarball_url render_tar_url(@owner, @repository, version&.tag_name) +json.zipball_url render_zip_url(@owner, @repository, version&.tag_name) +json.draft version&.draft ? "草稿" : (version&.prerelease ? "预发行" : "稳定") +json.created_at format_time(version.created_at.to_s.to_time) +json.published_at format_time(version.created_at.to_s.to_time) +json.user_name user.present? ? user.try(:show_real_name) : "" +json.user_login user&.login +json.image_url user.present? ? url_to_avatar(user) : "" +json.attachments do + json.array! version.try(:attachments) do |attachment| + json.partial! "attachments/attachment_simple", locals: {attachment: attachment} + end +end \ No newline at end of file diff --git a/app/views/version_releases/index.json.jbuilder b/app/views/version_releases/index.json.jbuilder index 88ae36f26..85d1be256 100644 --- a/app/views/version_releases/index.json.jbuilder +++ b/app/views/version_releases/index.json.jbuilder @@ -1,51 +1,17 @@ json.partial! "commons/success" json.user_permission @user_permission +json.user_admin_permission @user_admin_permission # json.releases @version_releases json.releases do - json.array! @version_releases.to_a.each do |re| - if re.present? - user = User.select(:id, :gitea_uid, :login, :lastname,:firstname, :nickname).find_by_gitea_uid(re["author"]["id"]) - version = @forge_releases.find_by(version_gid: re["id"]) - if @user_permission && re["draft"] - json.version_id version.try(:id) - json.id re["id"] - json.tag_name re["tag_name"] - json.target_commitish re["target_commitish"] - json.name re["name"] - json.body re["body"] - json.url re["url"] - json.tarball_url render_tar_url(@owner, @repository, re["tag_name"]) - json.zipball_url render_zip_url(@owner, @repository, re["tag_name"]) - json.draft re["draft"] ? "草稿" : (re["prerelease"] ? "预发行" : "稳定") - json.created_at format_time(version.created_at.to_s.to_time) - json.published_at format_time(version.created_at.to_s.to_time) - json.user_name user.present? ? user.try(:show_real_name) : "" - json.image_url user.present? ? url_to_avatar(user) : "" - else - unless re["draft"] - json.version_id version.try(:id) - json.id re["id"] - json.tag_name re["tag_name"] - json.target_commitish re["target_commitish"] - json.name re["name"] - json.body re["body"] - json.url re["url"] - json.tarball_url render_tar_url(@owner, @repository, re["tag_name"]) - json.zipball_url render_zip_url(@owner, @repository, re["tag_name"]) - json.draft re["draft"] ? "草稿" : (re["prerelease"] ? "预发行" : "稳定") - json.created_at format_time(version.created_at.to_s.to_time) - json.published_at format_time(version.created_at.to_s.to_time) - json.user_name user.present? ? user.try(:show_real_name) : "" - json.image_url user.present? ? url_to_avatar(user) : "" - end - end - - json.attachments do - json.array! version.try(:attachments) do |attachment| - json.partial! "attachments/attachment_simple", locals: {attachment: attachment} - end + json.array! @version_releases.each do |version| + version.update_sha if version.sha.nil? + if @user_permission && version&.draft + json.partial! "version_release", locals: {version: version, user: version&.user} + else + unless version&.draft + json.partial! "version_release", locals: {version: version, user: version&.user} end end - end end +json.total_count @version_releases.total_count \ No newline at end of file diff --git a/app/views/version_releases/show.json.jbuilder b/app/views/version_releases/show.json.jbuilder new file mode 100644 index 000000000..716f5d7b2 --- /dev/null +++ b/app/views/version_releases/show.json.jbuilder @@ -0,0 +1,2 @@ + +json.partial! "version_release", locals: {version: @version, user: @version&.user} \ No newline at end of file diff --git a/config/configuration.yml.example b/config/configuration.yml.example index 9b012277a..4c9683e12 100644 --- a/config/configuration.yml.example +++ b/config/configuration.yml.example @@ -62,6 +62,11 @@ default: &default domain: 'https://testgit.trustie.net' base_url: '/api/v1' + notice: + platform: '' + write_domain: '' + read_domain: '' + base_url: '' production: <<: *default diff --git a/config/database.yml.example b/config/database.yml.example index b0f1f2bef..52f6538e1 100644 --- a/config/database.yml.example +++ b/config/database.yml.example @@ -18,7 +18,7 @@ default: &default password: 123456 # socket: /var/run/mysqld/mysqld.sock gitea_server: - aadapter: mysql2 + adapter: mysql2 database: gitea_development host: 127.0.0.1 username: root diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index db1030163..f6f478625 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -31,7 +31,7 @@ zh-CN: branch_name: 分支 close_pr: 合并 merge: 合并 - issue_tags_value: 标签 + issue_tags_value: 标记 lock_issue: 锁定工单 unlock_issue: 解锁工单 destroy_issue_depend: 删除依赖 @@ -55,7 +55,7 @@ zh-CN: f: 否 true: 是 false: 否 - issue_tag_ids: 标签 + issue_tag_ids: 标记 issue_type: 分类 token: 悬赏金额 close_issue: 工单 diff --git a/config/routes.rb b/config/routes.rb index 1369b4579..c9c9718c2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -303,6 +303,15 @@ Rails.application.routes.draw do # resources :recent_contacts, only: [:index] # resource :private_message_details, only: [:show] # resource :unread_message_info, only: [:show] + + # 通知中心 + resources :messages, only: [:index, :create] do + collection do + post :read + + end + end + delete 'messages', to: 'messages#delete' end resources :tidings, only: [:index] @@ -411,6 +420,7 @@ Rails.application.routes.draw do member do get :menu_list get :branches + get :branches_slice get :simple get :watchers, to: 'projects#watch_users' get :stargazers, to: 'projects#praise_users' @@ -426,6 +436,7 @@ Rails.application.routes.draw do get :entries match :sub_entries, :via => [:get, :put] get :commits + get :commits_slice get :tags get :contributors post :create_file @@ -521,7 +532,7 @@ Rails.application.routes.draw do resources :forks, only: [:create] resources :project_trends, :path => :activity, only: [:index, :create] resources :issue_tags, :path => :labels, only: [:create, :edit, :update, :destroy, :index] - resources :version_releases, :path => :releases, only: [:index,:new, :create, :edit, :update, :destroy] + resources :version_releases, :path => :releases, only: [:index,:new, :show, :create, :edit, :update, :destroy] scope module: :ci do scope do @@ -663,6 +674,11 @@ Rails.application.routes.draw do resources :project_licenses resources :project_ignores resources :reversed_keywords + resources :message_templates, only: [:index, :edit, :update] do + collection do + get :init_data + end + end resources :major_informations, only: [:index] resources :ec_templates, only: [:index, :destroy] do collection do diff --git a/config/sidekiq.yml b/config/sidekiq.yml index a50c1c1aa..f8981a8b1 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -8,3 +8,4 @@ - [notify, 100] - [mailers, 101] - [cache, 10] + - [message, 20] diff --git a/config/sidekiq_cron.yml b/config/sidekiq_cron.yml index 3e807fa10..448e9c945 100644 --- a/config/sidekiq_cron.yml +++ b/config/sidekiq_cron.yml @@ -2,3 +2,8 @@ sync_gitea_repo_update_time: cron: "0 0 * * *" class: "SyncRepoUpdateTimeJob" queue: default + +delay_expired_issue: + cron: "0 0 * * *" + class: "DelayExpiredIssueJob" + queue: message diff --git a/db/migrate/20210909030759_create_message_templates.rb b/db/migrate/20210909030759_create_message_templates.rb new file mode 100644 index 000000000..dbae7f55d --- /dev/null +++ b/db/migrate/20210909030759_create_message_templates.rb @@ -0,0 +1,11 @@ +class CreateMessageTemplates < ActiveRecord::Migration[5.2] + def change + create_table :message_templates do |t| + t.string :type + t.text :sys_notice + t.text :email + + t.timestamps + end + end +end diff --git a/db/migrate/20210914064456_add_notification_url_to_message_templates.rb b/db/migrate/20210914064456_add_notification_url_to_message_templates.rb new file mode 100644 index 000000000..2adb842c8 --- /dev/null +++ b/db/migrate/20210914064456_add_notification_url_to_message_templates.rb @@ -0,0 +1,5 @@ +class AddNotificationUrlToMessageTemplates < ActiveRecord::Migration[5.2] + def change + add_column :message_templates, :notification_url, :string + end +end diff --git a/db/migrate/20210924021337_add_email_title_to_message_templates.rb b/db/migrate/20210924021337_add_email_title_to_message_templates.rb new file mode 100644 index 000000000..33d84b243 --- /dev/null +++ b/db/migrate/20210924021337_add_email_title_to_message_templates.rb @@ -0,0 +1,5 @@ +class AddEmailTitleToMessageTemplates < ActiveRecord::Migration[5.2] + def change + add_column :message_templates, :email_title, :string + end +end diff --git a/db/migrate/20210930025555_add_sha_to_version_releases.rb b/db/migrate/20210930025555_add_sha_to_version_releases.rb new file mode 100644 index 000000000..475d361f8 --- /dev/null +++ b/db/migrate/20210930025555_add_sha_to_version_releases.rb @@ -0,0 +1,5 @@ +class AddShaToVersionReleases < ActiveRecord::Migration[5.2] + def change + add_column :version_releases, :sha, :string + end +end diff --git a/public/docs/api.html b/public/docs/api.html index 35099a4e8..8368e8fae 100644 --- a/public/docs/api.html +++ b/public/docs/api.html @@ -345,6 +345,18 @@
  • 获取当前登陆用户信息
  • +
  • + 用户消息列表 +
  • +
  • + 发送消息 +
  • +
  • + 阅读消息 +
  • +
  • + 删除消息 +
  • 更改用户信息
  • @@ -457,6 +469,9 @@
  • 仓库详情(新版)
  • +
  • + 仓库标签列表 +
  • 编辑仓库信息
  • @@ -487,6 +502,12 @@
  • 获取仓库代码子目录或者文件
  • +
  • + 获取仓库README文件 +
  • +
  • + 获取仓库贡献者 +
  • 获取仓库webhooks列表
  • @@ -512,6 +533,23 @@
  • Pulls +
  • Issues @@ -909,7 +947,7 @@ Success — a happy kitten is an authenticated kitten!

    Users

    获取当前登陆用户信息

    @@ -971,16 +1009,16 @@ Success — a happy kitten is an authenticated kitten! -

    更改用户信息

    -

    更改用户信息

    +

    用户消息列表

    +

    获取用户消息列表

    示例:

    -
    curl -X PATCH/PUT http://localhost:3000/api/users/yystopf.json
    -
    await octokit.request('PATCH/PUT /api/users/:login.json')
    +
    curl -X GET http://localhost:3000/api/users/:login/messages.json
    +
    await octokit.request('GET /api/users/:login/messages.json')
     

    HTTP 请求

    -

    PATCH/PUT /api/users/:login.json

    +

    GET api/users/yystopf/messages.json

    请求字段说明:

    @@ -990,222 +1028,229 @@ Success — a happy kitten is an authenticated kitten! - + - + - - - + + + - - - + + + - - - + + + +
    user.nicknametype string用户昵称消息类型,不传为所有消息,notification为系统消息,atme为@我消息
    user.imagebase64/file用户头像statusinteger是否已读,不传为所有消息,1为未读,2为已读
    user.user_extension_attributes.genderint性别, 0男 1女limitinteger每页个数
    user.user_extension_attributes.provincestring省份pageinteger页码
    +

    返回字段说明:

    + - + + + + + + + + + + + + - + - + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - - - + + + - - - + + + - - - + + +
    user.user_extension_attributes.city参数类型字段说明
    total_countinteger消息总数
    type string城市消息类型
    user.user_extension_attributes.descriptionunread_notificationinteger未读系统通知数量
    unread_atmeinteger未读@我数量
    messages.idinteger消息id
    messages.statusinteger消息是否已读,1为未读,2为已读
    messages.content string简介消息内容
    user.user_extension_attributes.custom_departmentmessages.notification_url string单位名称消息跳转地址
    user.user_extension_attributes.technical_titlemessages.source string职业消息来源
    user.user_extension_attributes.show_emailbool是否展示邮箱messages.timeagostring消息时间
    user.user_extension_attributes.show_locationbool是否展示位置messages.typestring消息类型,notification为系统消息,atme为@我消息
    user.user_extension_attributes.show_departmentbool是否展示公司senderobject消息发送者
    - -
    -

    请求的JSON示例:

    -
    -
    {
    -    "user": {
    -        "nickname": "xxx",
    -        "user_extension_attributes": {
    -            "gender": 0,
    -            "province": "湖南",
    -            "city": "长沙",
    -            "description": "个性签名",
    -            "custom_department": "湖南智擎科技有限公司",
    -        }
    -    }
    -}
    -
    -
    -

    返回的JSON示例:

    -
    -
    {
    -    "status": 0,
    -    "message": "success"
    -}
    -

    获取用户星标项目

    -

    获取用户星标项目

    - -
    -

    示例:

    -
    -
    curl -X GET http://localhost:3000/api/users/yystopf/is_pinned_projects.json
    -
    await octokit.request('GET /api/users/:login/is_pinned_projects.json')
    -

    HTTP 请求

    -

    GET api/users/:login/is_pinned_projects.json

    -

    返回字段说明:

    +

    消息来源source字段说明

    - - + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数 类型字段说明说明
    total_countint星标项目数量IssueAssigned有新指派给我的易修
    identifierstring项目标识IssueAssignerExpire我负责的易修截止日期到达最后一天
    namestring项目名称IssueAtme在易修中@我
    descriptionstring项目描述IssueChanged我创建或负责的易修状态变更
    visitsint项目访问数量IssueCreatorExpire我创建的易修截止日期到达最后一天
    praises_countint项目点赞数量IssueDeleted我创建或负责的易修删除
    watchers_countint项目关注数量IssueJournal我创建或负责的易修有新的评论
    issues_countint项目issue数量LoginIpTip登录异常提示
    pull_requests_countint项目合并请求数量OrganizationJoined账号被拉入组织
    forked_countint项目复刻数量OrganizationLeft账号被移出组织
    is_publicbool项目是否公开OrganizationRole账号组织权限变更
    mirror_urlstring镜像地址ProjectDeleted我关注的仓库被删除
    typeint项目类型 0 普通项目 1 普通镜像项目 2 同步镜像项目ProjectFollowed我管理的仓库被关注
    time_agostring上次更新时间ProjectForked我管理的仓库被复刻
    open_devopsint是否开启devopsProjectIssue我管理/关注的仓库有新的易修
    forked_from_project_idintfork项目idProjectJoined账号被拉入项目
    platformstring项目平台ProjectLeft账号被移出项目
    author.namestring项目拥有者名称ProjectMemberJoined我管理的仓库有成员加入
    author.typestring项目拥有者类型ProjectMemberLeft我管理的仓库有成员移出
    author.loginstring项目拥有者用户名ProjectMilestone我管理的仓库有新的里程碑
    author.image_urlstring项目拥有者头像ProjectPraised我管理的仓库被点赞
    category.namestring项目分类名称ProjectPullRequest我管理/关注的仓库有新的合并请求
    language.namestring项目语言名称ProjectRole账号仓库权限变更
    positionint项目排序ProjectSettingChanged我管理的仓库项目设置被更改
    ProjectTransfer我关注的仓库被转移
    ProjectVersion我关注的仓库有新的发行版
    PullRequestAssigned有新指派给我的合并请求
    PullReuqestAtme在合并请求中@我
    PullRequestChanged我创建或负责的合并请求状态变更
    PullRequestClosed我创建或负责的合并请求被关闭
    PullRequestJournal我创建或负责的合并请求有新的评论
    PullRequestMerged我创建或负责的合并请求被合并
    @@ -1213,42 +1258,69 @@ Success — a happy kitten is an authenticated kitten!

    返回的JSON示例:

    {
    -    "total_count": 1,
    -    "projects": [
    +    "total_count": 5,
    +    "type": "",
    +    "unread_notification": 3,
    +    "unread_atme": 2,
    +    "messages": [
             {
    -            "id": 89,
    -            "repo_id": 89,
    -            "identifier": "monkey",
    -            "name": "boke",
    -            "description": "dkkd",
    -            "visits": 4,
    -            "praises_count": 0,
    -            "watchers_count": 0,
    -            "issues_count": 0,
    -            "pull_requests_count": 0,
    -            "forked_count": 0,
    -            "is_public": true,
    -            "mirror_url": "https://github.com/viletyy/monkey.git",
    -            "type": 1,
    -            "last_update_time": 1619685144,
    -            "time_ago": "27天前",
    -            "forked_from_project_id": null,
    -            "open_devops": false,
    -            "platform": "forge",
    -            "author": {
    -                "name": "测试组织",
    -                "type": "Organization",
    -                "login": "ceshi_org",
    -                "image_url": "images/avatars/Organization/9?t=1612706073"
    -            },
    -            "category": {
    -                "id": 3,
    -                "name": "深度学习"
    -            },
    -            "language": {
    -                "id": 2,
    -                "name": "C"
    +            "id": 1,
    +            "status": 1,
    +            "content": "Atme Message Content 1",
    +            "notification_url": "http://www.baidu.com",
    +            "source": "PullRequestAtme",
    +            "time_ago": "1天前",
    +            "type": "atme",
    +            "sender": {
    +                "id": 5,
    +                "type": "User",
    +                "name": "testforge2",
    +                "login": "testforge2",
    +                "image_url": "system/lets/letter_avatars/2/T/236_177_85/120.png"
    +            }
    +        },
    +        {
    +            "id": 2,
    +            "status": 0,
    +            "content": "Atme Message Content 2",
    +            "notification_url": "http://www.baidu.com",
    +            "source": "IssueAtme",
    +            "time_ago": "1天前",
    +            "type": "atme",
    +            "sender": {
    +                "id": 4,
    +                "type": "User",
    +                "name": "testforge1",
    +                "login": "testforge1",
    +                "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
                 }
    +        },
    +        {
    +            "id": 3,
    +            "status": 1,
    +            "content": "Notification Message Content 1",
    +            "notification_url": "http://www.baidu.com",
    +            "source": "IssueDelete",
    +            "time_ago": "1天前",
    +            "type": "notification"
    +        },
    +        {
    +            "id": 4,
    +            "status": 0,
    +            "content": "Notification Message Content 2",
    +            "notification_url": "http://www.baidu.com",
    +            "source": "IssueChanged",
    +            "time_ago": "1天前",
    +            "type": "notification"
    +        },
    +        {
    +            "id": 5,
    +            "status": 0,
    +            "content": "Notification Message Content 3",
    +            "notification_url": "http://www.baidu.com",
    +            "source": "ProjectJoined",
    +            "time_ago": "1天前",
    +            "type": "notification"
             }
         ]
     }
    @@ -1256,17 +1328,17 @@ Success — a happy kitten is an authenticated kitten!
     
    -

    用户添加星标项目

    -

    用户添加星标项目

    +

    发送消息

    +

    发送消息, 目前只支持atme

    示例:

    -
    curl -X POST http://localhost:3000/api/users/yystopf/is_pinned_projects/pin.json
    -
    await octokit.request('GET /api/users/:login/is_pinned_projects/pin.json')
    -

    HTTP 请求

    -

    POST /api/users/:login/is_pinned_projects/pin.json

    -

    请求字段说明:

    同时设定多个星标项目

    +
    curl -X POST http://localhost:3000/api/users/:login/messages.json
    +
    await octokit.request('POST /api/users/:login/messages.json')
    +

    HTTP 请求

    +

    POST api/users/yystopf/messages.json

    +

    请求字段说明:

    @@ -1275,26 +1347,37 @@ Success — a happy kitten is an authenticated kitten! - + + + + + + - + -
    参数
    is_pinned_project_idstypestring消息类型
    receivers_login array设定为星标项目的id需要发送消息的用户名数组
    -

    只设定一个星标项目

    - - - - + + + - - + - +
    参数类型字段说明atmeable_typestringatme消息对象,是从哪里@我的,比如评论:Journal、易修:Issue、合并请求:PullRequest
    is_pinned_project_idatmeable_id integer设定为星标项目的idatme消息对象id
    +
    +

    请求的JSON示例:

    +
    +
    {
    +    "type": "atme",
    +    "receivers_login": ["yystopf", "testforge1"],
    +    "atmeable_type": "Journal",
    +    "atmeable_id": 67
    +}
    +

    返回的JSON示例:

    @@ -1302,16 +1385,20 @@ Success — a happy kitten is an authenticated kitten!
    "status": 0, "message": "success" } -

    星标项目展示排序

    -

    星标项目展示排序

    +
    + +

    阅读消息

    +

    阅读消息

    示例:

    -
    curl -X PATCH http://localhost:3000/api/users/yystopf/is_pinned_projects/11.json
    -
    await octokit.request('PATCH/PUT /api/users/:login/is_pinned_projects/:id.json')
    -

    HTTP 请求

    -

    PATCH/PUT /api/users/:login/is_pinned_projects/:id.json

    +
    curl -X POST http://localhost:3000/api/users/:login/messages/read.json
    +
    await octokit.request('POST /api/users/:login/messages/read.json')
    +

    HTTP 请求

    +

    POST api/users/yystopf/messages/read.json

    请求字段说明:

    @@ -1321,21 +1408,17 @@ Success — a happy kitten is an authenticated kitten! - - - + + + + + + + +
    pinned_projects.positionint排序,数字越大排名越前typestring消息类型,不传为所有消息,notification为系统消息,atme为@我消息
    idsarray消息id数组,包含-1则把所有未读消息标记为已读
    -
    -

    请求的JSON示例:

    -
    -
    {
    -    "pinned_project": {
    -        "position": 1
    -    }
    -}
    -

    返回的JSON示例:

    @@ -1343,17 +1426,21 @@ Success — a happy kitten is an authenticated kitten! "status": 0, "message": "success" } -

    用户近期活动统计

    -

    用户近期活动统计, 默认显示近一周的数据

    + + +

    删除消息

    +

    删除消息

    示例:

    -
    curl -X GET http://localhost:3000/api/users/yystopf/statistics/activity.json
    -
    await octokit.request('GET /api/users/:login/statistics/activity.json')
    -

    HTTP 请求

    -

    GET /api/users/:login/statistics/activity.json

    -

    返回字段说明:

    +
    curl -X DELETE http://localhost:3000/api/users/:login/messages.json
    +
    await octokit.request('DELETE /api/users/:login/messages.json')
    +

    HTTP 请求

    +

    DELETE api/users/yystopf/messages.json

    +

    请求字段说明:

    @@ -1362,24 +1449,14 @@ Success — a happy kitten is an authenticated kitten! - - - - - - - - - - - - - + + + - + - +
    参数
    datesarray时间
    issues_countarray易修数量
    pull_requests_countarray合并请求数量typestring消息类型,atme为@我消息
    commtis_countids array贡献数量消息id数组,包含-1则把所有消息删除
    @@ -1387,62 +1464,24 @@ Success — a happy kitten is an authenticated kitten!

    返回的JSON示例:

    {
    -    "dates": [
    -        "2021.05.21",
    -        "2021.05.22",
    -        "2021.05.23",
    -        "2021.05.24",
    -        "2021.05.25",
    -        "2021.05.26",
    -        "2021.05.27",
    -        "2021.05.28"
    -    ],
    -    "issues_count": [
    -        0,
    -        0,
    -        0,
    -        0,
    -        0,
    -        0,
    -        0,
    -        0
    -    ],
    -    "pull_requests_count": [
    -        0,
    -        0,
    -        0,
    -        0,
    -        0,
    -        0,
    -        0,
    -        0
    -    ],
    -    "commits_count": [
    -        0,
    -        0,
    -        0,
    -        0,
    -        0,
    -        0,
    -        0,
    -        0
    -    ]
    +    "status": 0,
    +    "message": "success"
     }
     
    -

    获取用户贡献度

    -

    获取用户贡献度

    +

    更改用户信息

    +

    更改用户信息

    示例:

    -
    curl -X GET http://localhost:3000/api/users/yystopf/headmaps.json
    -
    await octokit.request('GET /api/users/:login/headmaps.json')
    -

    HTTP 请求

    -

    GET api/users/:login/headmaps.json

    -

    请求字段说明:

    +
    curl -X PATCH/PUT http://localhost:3000/api/users/yystopf.json
    +
    await octokit.request('PATCH/PUT /api/users/:login.json')
    +

    HTTP 请求

    +

    PATCH/PUT /api/users/:login.json

    +

    请求字段说明:

    @@ -1451,143 +1490,96 @@ Success — a happy kitten is an authenticated kitten! - + - + -
    参数
    yearuser.nickname string年份用户昵称
    -

    返回字段说明:

    - - - - + + + - - + - + - + - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明user.imagebase64/file用户头像
    total_contributionsuser.user_extension_attributes.gender int所选时间内的总贡献度性别, 0男 1女
    headmaps.dateuser.user_extension_attributes.province string时间省份
    headmaps.contributionsint贡献度user.user_extension_attributes.citystring城市
    user.user_extension_attributes.descriptionstring简介
    user.user_extension_attributes.custom_departmentstring单位名称
    user.user_extension_attributes.technical_titlestring职业
    user.user_extension_attributes.show_emailbool是否展示邮箱
    user.user_extension_attributes.show_locationbool是否展示位置
    user.user_extension_attributes.show_departmentbool是否展示公司
    +
    +

    请求的JSON示例:

    +
    +
    {
    +    "user": {
    +        "nickname": "xxx",
    +        "user_extension_attributes": {
    +            "gender": 0,
    +            "province": "湖南",
    +            "city": "长沙",
    +            "description": "个性签名",
    +            "custom_department": "湖南智擎科技有限公司",
    +        }
    +    }
    +}
    +

    返回的JSON示例:

    {
    -    "total_contributions": 139,
    -    "headmaps": [
    -        {
    -            "date": "2021-02-07",
    -            "contributions": 1
    -        },
    -        {
    -            "date": "2021-02-21",
    -            "contributions": 13
    -        },
    -        {
    -            "date": "2021-02-25",
    -            "contributions": 5
    -        },
    -        {
    -            "date": "2021-03-01",
    -            "contributions": 2
    -        },
    -        {
    -            "date": "2021-03-04",
    -            "contributions": 1
    -        },
    -        {
    -            "date": "2021-03-15",
    -            "contributions": 9
    -        },
    -        {
    -            "date": "2021-03-22",
    -            "contributions": 14
    -        },
    -        {
    -            "date": "2021-03-24",
    -            "contributions": 1
    -        },
    -        {
    -            "date": "2021-03-30",
    -            "contributions": 11
    -        },
    -        {
    -            "date": "2021-04-06",
    -            "contributions": 1
    -        },
    -        {
    -            "date": "2021-04-12",
    -            "contributions": 1
    -        },
    -        {
    -            "date": "2021-04-13",
    -            "contributions": 2
    -        },
    -        {
    -            "date": "2021-04-19",
    -            "contributions": 3
    -        },
    -        {
    -            "date": "2021-04-23",
    -            "contributions": 37
    -        },
    -        {
    -            "date": "2021-04-25",
    -            "contributions": 2
    -        },
    -        {
    -            "date": "2021-04-26",
    -            "contributions": 6
    -        },
    -        {
    -            "date": "2021-04-28",
    -            "contributions": 1
    -        },
    -        {
    -            "date": "2021-04-29",
    -            "contributions": 18
    -        },
    -        {
    -            "date": "2021-04-30",
    -            "contributions": 9
    -        },
    -        {
    -            "date": "2021-05-04",
    -            "contributions": 1
    -        },
    -        {
    -            "date": "2021-05-06",
    -            "contributions": 1
    -        }
    -    ]
    +    "status": 0,
    +    "message": "success"
     }
    -
    - -

    获取用户动态

    -

    获取用户动态

    +

    获取用户星标项目

    +

    获取用户星标项目

    示例:

    -
    curl -X GET http://localhost:3000/api/users/yystopf/project_trends.json
    -
    await octokit.request('GET /api/users/:login/project_trends.json')
    -

    HTTP 请求

    -

    GET api/users/:login/project_trends.json

    -

    请求字段说明:

    +
    curl -X GET http://localhost:3000/api/users/yystopf/is_pinned_projects.json
    +
    await octokit.request('GET /api/users/:login/is_pinned_projects.json')
    +

    HTTP 请求

    +

    GET api/users/:login/is_pinned_projects.json

    +

    返回字段说明:

    @@ -1596,63 +1588,124 @@ Success — a happy kitten is an authenticated kitten! - + + + + + + - + -
    参数
    datetotal_countint星标项目数量
    identifier string日期,格式: 2021-05-28项目标识
    -

    返回字段说明:

    - - - - + + + - - + + + + + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + + + - + - - - + + + - + + + + + + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + +
    参数类型字段说明namestring项目名称
    total_countdescriptionstring项目描述
    visits int所选时间内的总动态数项目访问数量
    project_trends.trend_typepraises_countint项目点赞数量
    watchers_countint项目关注数量
    issues_countint项目issue数量
    pull_requests_countint项目合并请求数量
    forked_countint项目复刻数量
    is_publicbool项目是否公开
    mirror_url string动态类型,Issue:易修,VersionRelease:版本发布,PullRequest:合并请求镜像地址
    project_trends.action_typetypeint项目类型 0 普通项目 1 普通镜像项目 2 同步镜像项目
    time_ago string操作类型上次更新时间
    project_trends.trend_idinteger动态idopen_devopsint是否开启devops
    project_trends.user_nameforked_from_project_idintfork项目id
    platform string用户名称项目平台
    project_trends.user_loginauthor.name string用户用户名项目拥有者名称
    project_trends.user_avatarauthor.type string用户头像项目拥有者类型
    project_trends.action_timeauthor.login string操作时间项目拥有者用户名
    project_trends.nameauthor.image_url string动态标题项目拥有者头像
    category.namestring项目分类名称
    language.namestring项目语言名称
    positionint项目排序
    @@ -1660,58 +1713,505 @@ Success — a happy kitten is an authenticated kitten!

    返回的JSON示例:

    {
    -    "total_count": 16,
    -    "project_trends": [
    -        {
    -            "id": 27,
    -            "trend_type": "Issue",
    -            "action_type": "创建了工单",
    -            "trend_id": 18,
    -            "user_name": "yystopf",
    -            "user_login": "yystopf",
    -            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    -            "action_time": "21天前",
    -            "name": "31213123123",
    -            "issue_type": "1",
    -            "status_id": 2,
    -            "priority_id": 4,
    -            "created_at": "2021-05-07 15:39",
    -            "updated_at": "2021-05-27 15:42",
    -            "assign_user_name": "yystopf",
    -            "assign_user_login": "yystopf",
    -            "issue_journal_size": 1,
    -            "issue_journals": []
    -        },
    -        {
    -            "id": 8,
    -            "trend_type": "VersionRelease",
    -            "action_type": "创建了版本发布",
    -            "trend_id": 8,
    -            "user_name": "yystopf",
    -            "user_login": "yystopf",
    -            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    -            "action_time": "24天前",
    -            "name": "heihei1",
    -            "tag_name": "v1.0",
    -            "target_commitish": "master",
    -            "tarball_url": "http://localhost:10080/forgeceshiorg1/ceshi1/archive/v1.0.tar.gz",
    -            "zipball_url": "http://localhost:10080/forgeceshiorg1/ceshi1/archive/v1.0.zip",
    -            "url": "http://localhost:10080/api/v1/repos/forgeceshiorg1/ceshi1/releases/84",
    -            "version_gid": "84",
    -            "created_at": "2021-05-04 12:04"
    -        },
    +    "total_count": 1,
    +    "projects": [
             {
    -            "id": 25,
    -            "trend_type": "PullRequest",
    -            "action_type": "关闭了合并请求",
    -            "trend_id": 14,
    -            "user_name": "yystopf",
    -            "user_login": "yystopf",
    -            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    -            "action_time": "28天前",
    -            "name": "13",
    -            "created_at": "2021-04-30 15:39"
    -        },
    +            "id": 89,
    +            "repo_id": 89,
    +            "identifier": "monkey",
    +            "name": "boke",
    +            "description": "dkkd",
    +            "visits": 4,
    +            "praises_count": 0,
    +            "watchers_count": 0,
    +            "issues_count": 0,
    +            "pull_requests_count": 0,
    +            "forked_count": 0,
    +            "is_public": true,
    +            "mirror_url": "https://github.com/viletyy/monkey.git",
    +            "type": 1,
    +            "last_update_time": 1619685144,
    +            "time_ago": "27天前",
    +            "forked_from_project_id": null,
    +            "open_devops": false,
    +            "platform": "forge",
    +            "author": {
    +                "name": "测试组织",
    +                "type": "Organization",
    +                "login": "ceshi_org",
    +                "image_url": "images/avatars/Organization/9?t=1612706073"
    +            },
    +            "category": {
    +                "id": 3,
    +                "name": "深度学习"
    +            },
    +            "language": {
    +                "id": 2,
    +                "name": "C"
    +            }
    +        }
    +    ]
    +}
    +
    + +

    用户添加星标项目

    +

    用户添加星标项目

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/users/yystopf/is_pinned_projects/pin.json
    +
    await octokit.request('GET /api/users/:login/is_pinned_projects/pin.json')
    +

    HTTP 请求

    +

    POST /api/users/:login/is_pinned_projects/pin.json

    +

    请求字段说明:

    同时设定多个星标项目

    + + + + + + + + + + + + +
    参数类型字段说明
    is_pinned_project_idsarray设定为星标项目的id
    +

    只设定一个星标项目

    + + + + + + + + + + + + +
    参数类型字段说明
    is_pinned_project_idinteger设定为星标项目的id
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "status": 0,
    +    "message": "success"
    +}
    +

    星标项目展示排序

    +

    星标项目展示排序

    + +
    +

    示例:

    +
    +
    curl -X PATCH http://localhost:3000/api/users/yystopf/is_pinned_projects/11.json
    +
    await octokit.request('PATCH/PUT /api/users/:login/is_pinned_projects/:id.json')
    +

    HTTP 请求

    +

    PATCH/PUT /api/users/:login/is_pinned_projects/:id.json

    +

    请求字段说明:

    + + + + + + + + + + + + +
    参数类型字段说明
    pinned_projects.positionint排序,数字越大排名越前
    + +
    +

    请求的JSON示例:

    +
    +
    {
    +    "pinned_project": {
    +        "position": 1
    +    }
    +}
    +
    +
    +

    返回的JSON示例:

    +
    +
    {
    +    "status": 0,
    +    "message": "success"
    +}
    +

    用户近期活动统计

    +

    用户近期活动统计, 默认显示近一周的数据

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/statistics/activity.json
    +
    await octokit.request('GET /api/users/:login/statistics/activity.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/statistics/activity.json

    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    datesarray时间
    issues_countarray易修数量
    pull_requests_countarray合并请求数量
    commtis_countarray贡献数量
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "dates": [
    +        "2021.05.21",
    +        "2021.05.22",
    +        "2021.05.23",
    +        "2021.05.24",
    +        "2021.05.25",
    +        "2021.05.26",
    +        "2021.05.27",
    +        "2021.05.28"
    +    ],
    +    "issues_count": [
    +        0,
    +        0,
    +        0,
    +        0,
    +        0,
    +        0,
    +        0,
    +        0
    +    ],
    +    "pull_requests_count": [
    +        0,
    +        0,
    +        0,
    +        0,
    +        0,
    +        0,
    +        0,
    +        0
    +    ],
    +    "commits_count": [
    +        0,
    +        0,
    +        0,
    +        0,
    +        0,
    +        0,
    +        0,
    +        0
    +    ]
    +}
    +
    + +

    获取用户贡献度

    +

    获取用户贡献度

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/headmaps.json
    +
    await octokit.request('GET /api/users/:login/headmaps.json')
    +

    HTTP 请求

    +

    GET api/users/:login/headmaps.json

    +

    请求字段说明:

    + + + + + + + + + + + + +
    参数类型字段说明
    yearstring年份
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    total_contributionsint所选时间内的总贡献度
    headmaps.datestring时间
    headmaps.contributionsint贡献度
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "total_contributions": 139,
    +    "headmaps": [
    +        {
    +            "date": "2021-02-07",
    +            "contributions": 1
    +        },
    +        {
    +            "date": "2021-02-21",
    +            "contributions": 13
    +        },
    +        {
    +            "date": "2021-02-25",
    +            "contributions": 5
    +        },
    +        {
    +            "date": "2021-03-01",
    +            "contributions": 2
    +        },
    +        {
    +            "date": "2021-03-04",
    +            "contributions": 1
    +        },
    +        {
    +            "date": "2021-03-15",
    +            "contributions": 9
    +        },
    +        {
    +            "date": "2021-03-22",
    +            "contributions": 14
    +        },
    +        {
    +            "date": "2021-03-24",
    +            "contributions": 1
    +        },
    +        {
    +            "date": "2021-03-30",
    +            "contributions": 11
    +        },
    +        {
    +            "date": "2021-04-06",
    +            "contributions": 1
    +        },
    +        {
    +            "date": "2021-04-12",
    +            "contributions": 1
    +        },
    +        {
    +            "date": "2021-04-13",
    +            "contributions": 2
    +        },
    +        {
    +            "date": "2021-04-19",
    +            "contributions": 3
    +        },
    +        {
    +            "date": "2021-04-23",
    +            "contributions": 37
    +        },
    +        {
    +            "date": "2021-04-25",
    +            "contributions": 2
    +        },
    +        {
    +            "date": "2021-04-26",
    +            "contributions": 6
    +        },
    +        {
    +            "date": "2021-04-28",
    +            "contributions": 1
    +        },
    +        {
    +            "date": "2021-04-29",
    +            "contributions": 18
    +        },
    +        {
    +            "date": "2021-04-30",
    +            "contributions": 9
    +        },
    +        {
    +            "date": "2021-05-04",
    +            "contributions": 1
    +        },
    +        {
    +            "date": "2021-05-06",
    +            "contributions": 1
    +        }
    +    ]
    +}
    +
    + +

    获取用户动态

    +

    获取用户动态

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/project_trends.json
    +
    await octokit.request('GET /api/users/:login/project_trends.json')
    +

    HTTP 请求

    +

    GET api/users/:login/project_trends.json

    +

    请求字段说明:

    + + + + + + + + + + + + +
    参数类型字段说明
    datestring日期,格式: 2021-05-28
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    total_countint所选时间内的总动态数
    project_trends.trend_typestring动态类型,Issue:易修,VersionRelease:版本发布,PullRequest:合并请求
    project_trends.action_typestring操作类型
    project_trends.trend_idinteger动态id
    project_trends.user_namestring用户名称
    project_trends.user_loginstring用户用户名
    project_trends.user_avatarstring用户头像
    project_trends.action_timestring操作时间
    project_trends.namestring动态标题
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "total_count": 16,
    +    "project_trends": [
    +        {
    +            "id": 27,
    +            "trend_type": "Issue",
    +            "action_type": "创建了工单",
    +            "trend_id": 18,
    +            "user_name": "yystopf",
    +            "user_login": "yystopf",
    +            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    +            "action_time": "21天前",
    +            "name": "31213123123",
    +            "issue_type": "1",
    +            "status_id": 2,
    +            "priority_id": 4,
    +            "created_at": "2021-05-07 15:39",
    +            "updated_at": "2021-05-27 15:42",
    +            "assign_user_name": "yystopf",
    +            "assign_user_login": "yystopf",
    +            "issue_journal_size": 1,
    +            "issue_journals": []
    +        },
    +        {
    +            "id": 8,
    +            "trend_type": "VersionRelease",
    +            "action_type": "创建了版本发布",
    +            "trend_id": 8,
    +            "user_name": "yystopf",
    +            "user_login": "yystopf",
    +            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    +            "action_time": "24天前",
    +            "name": "heihei1",
    +            "tag_name": "v1.0",
    +            "target_commitish": "master",
    +            "tarball_url": "http://localhost:10080/forgeceshiorg1/ceshi1/archive/v1.0.tar.gz",
    +            "zipball_url": "http://localhost:10080/forgeceshiorg1/ceshi1/archive/v1.0.zip",
    +            "url": "http://localhost:10080/api/v1/repos/forgeceshiorg1/ceshi1/releases/84",
    +            "version_gid": "84",
    +            "created_at": "2021-05-04 12:04"
    +        },
    +        {
    +            "id": 25,
    +            "trend_type": "PullRequest",
    +            "action_type": "关闭了合并请求",
    +            "trend_id": 14,
    +            "user_name": "yystopf",
    +            "user_login": "yystopf",
    +            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    +            "action_time": "28天前",
    +            "name": "13",
    +            "created_at": "2021-04-30 15:39"
    +        },
             {
                 "id": 24,
                 "trend_type": "PullRequest",
    @@ -1724,187 +2224,1645 @@ Success — a happy kitten is an authenticated kitten!
                 "name": "211212",
                 "created_at": "2021-04-30 15:37"
             },
    -        {
    -            "id": 23,
    -            "trend_type": "PullRequest",
    -            "action_type": "创建了合并请求",
    -            "trend_id": 12,
    -            "user_name": "yystopf",
    -            "user_login": "yystopf",
    -            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    -            "action_time": "28天前",
    -            "name": "奇偶哦iu",
    -            "created_at": "2021-04-30 10:19"
    +        {
    +            "id": 23,
    +            "trend_type": "PullRequest",
    +            "action_type": "创建了合并请求",
    +            "trend_id": 12,
    +            "user_name": "yystopf",
    +            "user_login": "yystopf",
    +            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    +            "action_time": "28天前",
    +            "name": "奇偶哦iu",
    +            "created_at": "2021-04-30 10:19"
    +        },
    +        {
    +            "id": 22,
    +            "trend_type": "PullRequest",
    +            "action_type": "创建了合并请求",
    +            "trend_id": 11,
    +            "user_name": "yystopf",
    +            "user_login": "yystopf",
    +            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    +            "action_time": "28天前",
    +            "name": "2112123",
    +            "created_at": "2021-04-29 18:46"
    +        },
    +        {
    +            "id": 21,
    +            "trend_type": "PullRequest",
    +            "action_type": "关闭了合并请求",
    +            "trend_id": 10,
    +            "user_name": "yystopf",
    +            "user_login": "yystopf",
    +            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    +            "action_time": "28天前",
    +            "name": "23123",
    +            "created_at": "2021-04-29 18:45"
    +        },
    +        {
    +            "id": 20,
    +            "trend_type": "PullRequest",
    +            "action_type": "创建了合并请求",
    +            "trend_id": 9,
    +            "user_name": "yystopf",
    +            "user_login": "yystopf",
    +            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    +            "action_time": "28天前",
    +            "name": "33",
    +            "created_at": "2021-04-29 18:37"
    +        },
    +        {
    +            "id": 19,
    +            "trend_type": "PullRequest",
    +            "action_type": "关闭了合并请求",
    +            "trend_id": 8,
    +            "user_name": "yystopf",
    +            "user_login": "yystopf",
    +            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    +            "action_time": "28天前",
    +            "name": "gggg",
    +            "created_at": "2021-04-29 17:51"
    +        },
    +        {
    +            "id": 16,
    +            "trend_type": "Issue",
    +            "action_type": "创建了工单",
    +            "trend_id": 8,
    +            "user_name": "yystopf",
    +            "user_login": "yystopf",
    +            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    +            "action_time": "1个月前",
    +            "name": "hjhkj",
    +            "issue_type": "1",
    +            "status_id": 1,
    +            "priority_id": 2,
    +            "created_at": "2021-04-19 10:52",
    +            "updated_at": "2021-04-19 10:52",
    +            "assign_user_name": null,
    +            "assign_user_login": null,
    +            "issue_journal_size": 0,
    +            "issue_journals": []
    +        },
    +        {
    +            "id": 7,
    +            "trend_type": "VersionRelease",
    +            "action_type": "创建了版本发布",
    +            "trend_id": 7,
    +            "user_name": "yystopf",
    +            "user_login": "yystopf",
    +            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    +            "action_time": "1个月前",
    +            "name": "v3.0.1",
    +            "tag_name": "v3.0.1",
    +            "target_commitish": "master",
    +            "tarball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v3.0.1.tar.gz",
    +            "zipball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v3.0.1.zip",
    +            "url": "http://localhost:10080/api/v1/repos/yystopf/ceshirepo1/releases/78",
    +            "version_gid": "78",
    +            "created_at": "2021-03-30 15:51"
    +        },
    +        {
    +            "id": 6,
    +            "trend_type": "VersionRelease",
    +            "action_type": "创建了版本发布",
    +            "trend_id": 6,
    +            "user_name": "yystopf",
    +            "user_login": "yystopf",
    +            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    +            "action_time": "1个月前",
    +            "name": "v3.0.0",
    +            "tag_name": "v3.0.0",
    +            "target_commitish": "master",
    +            "tarball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v3.0.0.tar.gz",
    +            "zipball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v3.0.0.zip",
    +            "url": "http://localhost:10080/api/v1/repos/yystopf/ceshirepo1/releases/77",
    +            "version_gid": "77",
    +            "created_at": "2021-03-30 15:33"
    +        },
    +        {
    +            "id": 5,
    +            "trend_type": "VersionRelease",
    +            "action_type": "创建了版本发布",
    +            "trend_id": 5,
    +            "user_name": "yystopf",
    +            "user_login": "yystopf",
    +            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    +            "action_time": "1个月前",
    +            "name": "v1.0.0",
    +            "tag_name": "v1.0.0",
    +            "target_commitish": "master",
    +            "tarball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v1.0.0.tar.gz",
    +            "zipball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v1.0.0.zip",
    +            "url": "http://localhost:10080/api/v1/repos/yystopf/ceshirepo1/releases/76",
    +            "version_gid": "76",
    +            "created_at": "2021-03-30 15:27"
    +        },
    +        {
    +            "id": 2,
    +            "trend_type": "VersionRelease",
    +            "action_type": "创建了版本发布",
    +            "trend_id": 2,
    +            "user_name": "yystopf",
    +            "user_login": "yystopf",
    +            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    +            "action_time": "2个月前",
    +            "name": "vvvv",
    +            "tag_name": "v1.1",
    +            "target_commitish": "dev",
    +            "tarball_url": "http://localhost:10080/yystopf/virus_blog/archive/v1.1.tar.gz",
    +            "zipball_url": "http://localhost:10080/yystopf/virus_blog/archive/v1.1.zip",
    +            "url": "http://localhost:10080/api/v1/repos/yystopf/virus_blog/releases/6",
    +            "version_gid": "6",
    +            "created_at": "2021-03-15 14:18"
    +        },
    +        {
    +            "id": 2,
    +            "trend_type": "PullRequest",
    +            "action_type": "创建了合并请求",
    +            "trend_id": 2,
    +            "user_name": "yystopf",
    +            "user_login": "yystopf",
    +            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    +            "action_time": "3个月前",
    +            "name": "444",
    +            "created_at": "2021-02-25 17:31"
    +        }
    +    ]
    +}
    +
    + +

    用户开发能力

    +

    用户开发能力, 默认为所有时间下的开发能力

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/statistics/develop.json
    +
    await octokit.request('GET /api/users/:login/statistics/develop.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/statistics/develop.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    start_timeinteger时间戳,开始时间,格式:1621526400
    end_timeinteger时间戳,结束时间,格式:1622131200
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    influenceint影响力
    contributionint贡献度
    activityint活跃度
    experienceint项目经验
    languageint语言能力
    languages_percentfloat语言百分比
    each_language_scoreint各门语言分数
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "platform": {
    +        "influence": 61,
    +        "contribution": 75,
    +        "activity": 66,
    +        "experience": 95,
    +        "language": 87,
    +        "languages_percent": {
    +            "CSS": 0.03,
    +            "C#": 0.13,
    +            "Ruby": 0.04,
    +            "Go": 0.05,
    +            "C": 0.19,
    +            "Java": 0.34,
    +            "Python": 0.09,
    +            "C+": 0.01,
    +            "C++": 0.11,
    +            "Scala": 0.01,
    +            "HTML": 0.01
    +        },
    +        "each_language_score": {
    +            "CSS": 71,
    +            "C#": 86,
    +            "Ruby": 75,
    +            "Go": 77,
    +            "C": 90,
    +            "Java": 93,
    +            "Python": 83,
    +            "C+": 66,
    +            "C++": 85,
    +            "Scala": 66,
    +            "HTML": 66
    +        }
    +    },
    +    "user": {
    +        "influence": 60,
    +        "contribution": 72,
    +        "activity": 65,
    +        "experience": 88,
    +        "language": 84,
    +        "languages_percent": {
    +            "C": 0.25,
    +            "C#": 0.33,
    +            "C++": 0.13,
    +            "CSS": 0.08,
    +            "Go": 0.04,
    +            "HTML": 0.04,
    +            "Java": 0.04,
    +            "Ruby": 0.08
             },
    -        {
    -            "id": 22,
    -            "trend_type": "PullRequest",
    -            "action_type": "创建了合并请求",
    -            "trend_id": 11,
    -            "user_name": "yystopf",
    -            "user_login": "yystopf",
    -            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    -            "action_time": "28天前",
    -            "name": "2112123",
    -            "created_at": "2021-04-29 18:46"
    +        "each_language_score": {
    +            "C": 81,
    +            "C#": 84,
    +            "C++": 75,
    +            "CSS": 71,
    +            "Go": 66,
    +            "HTML": 66,
    +            "Java": 66,
    +            "Ruby": 71
    +        }
    +    }
    +}
    +
    + +

    用户角色定位

    +

    用户角色定位,默认显示所有时间下的角色定位数据

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/statistics/role.json
    +
    await octokit.request('GET /api/users/:login/statistics/role.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/statistics/role.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    start_timeinteger时间戳,开始时间,格式:1621526400
    end_timeinteger时间戳,结束时间,格式:1622131200
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    total_projects_countint用户所有的项目数量
    role.object.countint用户该语言下的项目数量
    role.object.percentfloat用户该语言下的项目占比
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "total_projects_count": 27,
    +    "role": {
    +        "owner": {
    +            "count": 24,
    +            "percent": 0.89
             },
    -        {
    -            "id": 21,
    -            "trend_type": "PullRequest",
    -            "action_type": "关闭了合并请求",
    -            "trend_id": 10,
    -            "user_name": "yystopf",
    -            "user_login": "yystopf",
    -            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    -            "action_time": "28天前",
    -            "name": "23123",
    -            "created_at": "2021-04-29 18:45"
    +        "manager": {
    +            "count": 1,
    +            "percent": 0.04
    +        },
    +        "developer": {
    +            "count": 2,
    +            "percent": 0.07
             },
    +        "reporter": {
    +            "count": 0,
    +            "percent": 0.0
    +        }
    +    }
    +}
    +
    + +

    用户专业定位

    +

    用户专业定位,默认显示所有时间下的专业定位数据

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/statistics/major.json
    +
    await octokit.request('GET /api/users/:login/statistics/major.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/statistics/major.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    start_timeinteger时间戳,开始时间,格式:1621526400
    end_timeinteger时间戳,结束时间,格式:1622131200
    +

    返回字段说明:

    + + + + + + + + + + + + +
    参数类型字段说明
    categoriesint用户项目分类
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "categories": [
    +        "大数据",
    +        "机器学习",
    +        "深度学习",
    +        "人工智能",
    +        "智慧医疗",
    +        "云计算"
    +    ]
    +}
    +
    + +

    待办事项-用户通知信息

    +

    待办事项-用户通知信息

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/applied_messages.json
    +
    await octokit.request('GET /api/users/:login/applied_messages.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/applied_messages.json

    +

    请求字段说明:

    + + + + + + + + + + + + +
    参数类型字段说明
    loginstring用户标识
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    appliedobject通知主体
    applied.idint通知主体的迁移id
    applied.statusstring通知主体的迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝
    applied.time_agostring通知主体的迁移创建的时间
    applied.project.idint通知主体的迁移项目的id
    applied.project.identifierstring通知主体的迁移项目的标识
    applied.project.namestring通知主体的迁移项目的名称
    applied.project.descriptionstring通知主体的迁移项目的描述
    applied.project.is_publicbool通知主体的迁移项目是否公开
    applied.project.owner.idbool通知主体的迁移项目拥有者id
    applied.project.owner.typestring通知主体的迁移项目拥有者类型
    applied.project.owner.namestring通知主体的迁移项目拥有者昵称
    applied.project.owner.loginstring通知主体的迁移项目拥有者标识
    applied.project.owner.image_urlstring通知主体的迁移项目拥有者头像
    applied.user.idint通知主体的迁移创建者的id
    applied.user.typestring通知主体的迁移创建者的类型
    applied.user.namestring通知主体的迁移创建者的名称
    applied.user.loginstring通知主体的迁移创建者的标识
    applied.user.image_urlstring通知主体的迁移创建者头像
    applied_user.idint通知发起者的id
    applied_user.typestring通知发起者的类型
    applied_user.namestring通知发起者的名称
    applied_user.loginstring通知发起者的标识
    applied_user.image_urlstring通知发起者头像
    applied_typestring通知类型
    namestring通知内容
    viewedstring是否已读,waiting:未读,viewed:已读
    statusstring通知状态, canceled:已取消,common: 正常,successed:成功,failure:失败
    time_agostring通知时间
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "total_count": 5,
    +    "applied_messages": [
             {
    -            "id": 20,
    -            "trend_type": "PullRequest",
    -            "action_type": "创建了合并请求",
    -            "trend_id": 9,
    -            "user_name": "yystopf",
    -            "user_login": "yystopf",
    -            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    -            "action_time": "28天前",
    -            "name": "33",
    -            "created_at": "2021-04-29 18:37"
    +            "applied": {
    +                "project": {
    +                    "id": 74,
    +                    "identifier": "hehuisssjssjjsjs",
    +                    "name": "hehuisssjssjjsjs",
    +                    "description": "wwww",
    +                    "is_public": false,
    +                    "owner": {
    +                        "id": 10,
    +                        "type": "User",
    +                        "name": "testforge1",
    +                        "login": "testforge1",
    +                        "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
    +                    }
    +                },
    +                "user": {
    +                    "id": 6,
    +                    "type": "User",
    +                    "name": "何慧",
    +                    "login": "yystopf",
    +                    "image_url": "images/avatars/User/6?t=1622513134"
    +                },
    +                "id": 6,
    +                "status": "accepted",
    +                "created_at": "2021-06-09 16:34",
    +                "time_ago": "1分钟前"
    +            },
    +            "applied_user": {
    +                "id": 6,
    +                "type": "User",
    +                "name": "何慧",
    +                "login": "yystopf",
    +                "image_url": "images/avatars/User/6?t=1622513134"
    +            },
    +            "applied_type": "AppliedProject",
    +            "name": "已通过你加入【hehuisssjssjjsjs】仓库的申请。",
    +            "viewed": "waiting",
    +            "status": "successed",
    +            "created_at": "2021-06-09 16:34",
    +            "time_ago": "1分钟前"
             },
             {
    -            "id": 19,
    -            "trend_type": "PullRequest",
    -            "action_type": "关闭了合并请求",
    -            "trend_id": 8,
    -            "user_name": "yystopf",
    -            "user_login": "yystopf",
    -            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    -            "action_time": "28天前",
    -            "name": "gggg",
    -            "created_at": "2021-04-29 17:51"
    +            "applied": {
    +                "project": {
    +                    "id": 86,
    +                    "identifier": "ceshi_repo1",
    +                    "name": "测试项目啊1",
    +                    "description": "二十多",
    +                    "is_public": true,
    +                    "owner": {
    +                        "id": 52,
    +                        "type": "Organization",
    +                        "name": "身份卡手动阀",
    +                        "login": "ceshi1",
    +                        "image_url": "images/avatars/Organization/52?t=1618805056"
    +                    }
    +                },
    +                "user": {
    +                    "id": 6,
    +                    "type": "User",
    +                    "name": "yystopf",
    +                    "login": "yystopf",
    +                    "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
    +                },
    +                "owner": {
    +                    "id": 9,
    +                    "type": "Organization",
    +                    "name": "测试组织",
    +                    "login": "ceshi_org",
    +                    "image_url": "images/avatars/Organization/9?t=1612706073"
    +                },
    +                "id": 4,
    +                "status": "common",
    +                "created_at": "2021-04-26 09:54",
    +                "time_ago": "35分钟前"
    +            },
    +            "applied_user": {
    +                "id": 6,
    +                "type": "User",
    +                "name": "yystopf",
    +                "login": "yystopf",
    +                "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
    +            },
    +            "applied_type": "AppliedTransferProject",
    +            "name": "正在将【测试项目啊1】仓库转移给【测试组织】",
    +            "viewed": "viewed",
    +            "status": "common",
    +            "created_at": "2021-04-26 09:54",
    +            "time_ago": "35分钟前"
             },
    +        ...
    +    ]
    +}
    +

    待办事项-接受仓库

    +

    待办事项-接受仓库

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/applied_transfer_projects.json
    +
    await octokit.request('GET /api/users/:login/applied_transfer_projects.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/applied_transfer_projects.json

    +

    请求字段说明:

    + + + + + + + + + + + + +
    参数类型字段说明
    loginstring用户标识
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idint迁移id
    statusstring迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝
    time_agostring迁移创建的时间
    project.idint迁移项目的id
    project.identifierstring迁移项目的标识
    project.namestring迁移项目的名称
    project.descriptionstring迁移项目的描述
    project.is_publicbool迁移项目是否公开
    project.owner.idbool迁移项目拥有者id
    project.owner.typestring迁移项目拥有者类型
    project.owner.namestring迁移项目拥有者昵称
    project.owner.loginstring迁移项目拥有者标识
    project.owner.image_urlstring迁移项目拥有者头像
    user.idint迁移创建者的id
    user.typestring迁移创建者的类型
    user.namestring迁移创建者的名称
    user.loginstring迁移创建者的标识
    user.image_urlstring迁移创建者头像
    owner.idint迁移接受者的id
    owner.typestring迁移接受者的类型
    owner.namestring迁移接受者的名称
    owner.loginstring迁移接受者的标识
    owner.image_urlstring迁移接受者头像
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "total_count": 4,
    +    "applied_transfer_projects": [
             {
    -            "id": 16,
    -            "trend_type": "Issue",
    -            "action_type": "创建了工单",
    -            "trend_id": 8,
    -            "user_name": "yystopf",
    -            "user_login": "yystopf",
    -            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    -            "action_time": "1个月前",
    -            "name": "hjhkj",
    -            "issue_type": "1",
    -            "status_id": 1,
    -            "priority_id": 2,
    -            "created_at": "2021-04-19 10:52",
    -            "updated_at": "2021-04-19 10:52",
    -            "assign_user_name": null,
    -            "assign_user_login": null,
    -            "issue_journal_size": 0,
    -            "issue_journals": []
    +            "project": {
    +                "id": 86,
    +                "identifier": "ceshi_repo1",
    +                "name": "测试项目啊1",
    +                "description": "二十多",
    +                "is_public": true,
    +                "owner": {
    +                    "id": 52,
    +                    "type": "Organization",
    +                    "name": "身份卡手动阀",
    +                    "login": "ceshi1",
    +                    "image_url": "images/avatars/Organization/52?t=1618805056"
    +                }
    +            },
    +            "user": {
    +                "id": 6,
    +                "type": "User",
    +                "name": "yystopf",
    +                "login": "yystopf",
    +                "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
    +            },
    +            "owner": {
    +                "id": 52,
    +                "type": "Organization",
    +                "name": "身份卡手动阀",
    +                "login": "ceshi1",
    +                "image_url": "images/avatars/Organization/52?t=1618805056"
    +            },
    +            "id": 1,
    +            "status": "canceled",
    +            "created_at": "2021-04-25 18:06",
    +            "time_ago": "16小时前"
             },
    +        ...
    +    ]
    +}
    +

    用户接受迁移

    +

    用户接受迁移

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/users/yystopf/applied_transfer_projects/2/accept.json
    +
    await octokit.request('GET /api/users/:login/applied_transfer_projects/:id/accept.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/applied_transfer_projects/:id/accept.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    loginstring用户标识
    idint迁移id
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idint迁移id
    statusstring迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝
    time_agostring迁移创建的时间
    project.idint迁移项目的id
    project.identifierstring迁移项目的标识
    project.namestring迁移项目的名称
    project.descriptionstring迁移项目的描述
    project.is_publicbool迁移项目是否公开
    project.owner.idbool迁移项目拥有者id
    project.owner.typestring迁移项目拥有者类型
    project.owner.namestring迁移项目拥有者昵称
    project.owner.loginstring迁移项目拥有者标识
    project.owner.image_urlstring迁移项目拥有者头像
    user.idint迁移创建者的id
    user.typestring迁移创建者的类型
    user.namestring迁移创建者的名称
    user.loginstring迁移创建者的标识
    user.image_urlstring迁移创建者头像
    owner.idint迁移接受者的id
    owner.typestring迁移接受者的类型
    owner.namestring迁移接受者的名称
    owner.loginstring迁移接受者的标识
    owner.image_urlstring迁移接受者头像
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "project": {
    +        "id": 86,
    +        "identifier": "ceshi_repo1",
    +        "name": "测试项目啊1",
    +        "description": "二十多",
    +        "is_public": true,
    +        "owner": {
    +            "id": 52,
    +            "type": "Organization",
    +            "name": "身份卡手动阀",
    +            "login": "ceshi1",
    +            "image_url": "images/avatars/Organization/52?t=1618805056"
    +        }
    +    },
    +    "user": {
    +        "id": 6,
    +        "type": "User",
    +        "name": "yystopf",
    +        "login": "yystopf",
    +        "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
    +    },
    +    "owner": {
    +        "id": 52,
    +        "type": "Organization",
    +        "name": "身份卡手动阀",
    +        "login": "ceshi1",
    +        "image_url": "images/avatars/Organization/52?t=1618805056"
    +    },
    +    "id": 1,
    +    "status": "canceled",
    +    "created_at": "2021-04-25 18:06",
    +    "time_ago": "16小时前"
    +}
    +

    用户拒绝迁移

    +

    用户拒绝迁移

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/users/yystopf/applied_transfer_projects/2/refuse.json
    +
    await octokit.request('GET /api/users/:login/applied_transfer_projects/:id/refuse.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/applied_transfer_projects/:id/refuse.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    loginstring用户标识
    idint迁移id
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idint迁移id
    statusstring迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝
    time_agostring迁移创建的时间
    project.idint迁移项目的id
    project.identifierstring迁移项目的标识
    project.namestring迁移项目的名称
    project.descriptionstring迁移项目的描述
    project.is_publicbool迁移项目是否公开
    project.owner.idbool迁移项目拥有者id
    project.owner.typestring迁移项目拥有者类型
    project.owner.namestring迁移项目拥有者昵称
    project.owner.loginstring迁移项目拥有者标识
    project.owner.image_urlstring迁移项目拥有者头像
    user.idint迁移创建者的id
    user.typestring迁移创建者的类型
    user.namestring迁移创建者的名称
    user.loginstring迁移创建者的标识
    user.image_urlstring迁移创建者头像
    owner.idint迁移接受者的id
    owner.typestring迁移接受者的类型
    owner.namestring迁移接受者的名称
    owner.loginstring迁移接受者的标识
    owner.image_urlstring迁移接受者头像
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "project": {
    +        "id": 86,
    +        "identifier": "ceshi_repo1",
    +        "name": "测试项目啊1",
    +        "description": "二十多",
    +        "is_public": true,
    +        "owner": {
    +            "id": 52,
    +            "type": "Organization",
    +            "name": "身份卡手动阀",
    +            "login": "ceshi1",
    +            "image_url": "images/avatars/Organization/52?t=1618805056"
    +        }
    +    },
    +    "user": {
    +        "id": 6,
    +        "type": "User",
    +        "name": "yystopf",
    +        "login": "yystopf",
    +        "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
    +    },
    +    "owner": {
    +        "id": 52,
    +        "type": "Organization",
    +        "name": "身份卡手动阀",
    +        "login": "ceshi1",
    +        "image_url": "images/avatars/Organization/52?t=1618805056"
    +    },
    +    "id": 1,
    +    "status": "canceled",
    +    "created_at": "2021-04-25 18:06",
    +    "time_ago": "16小时前"
    +}
    +

    待办事项-项目申请

    +

    待办事项-项目申请

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/applied_projects.json
    +
    await octokit.request('GET /api/users/:login/applied_projects.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/applied_projects.json

    +

    请求字段说明:

    + + + + + + + + + + + + +
    参数类型字段说明
    loginstring用户标识
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idint申请id
    statusstring申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝
    time_agostring申请创建的时间
    project.idint申请项目的id
    project.identifierstring申请项目的标识
    project.namestring申请项目的名称
    project.descriptionstring申请项目的描述
    project.is_publicbool申请项目是否公开
    project.owner.idbool申请项目拥有者id
    project.owner.typestring申请项目拥有者类型
    project.owner.namestring申请项目拥有者昵称
    project.owner.loginstring申请项目拥有者标识
    project.owner.image_urlstring申请项目拥有者头像
    user.idint申请创建者的id
    user.typestring申请创建者的类型
    user.namestring申请创建者的名称
    user.loginstring申请创建者的标识
    user.image_urlstring申请创建者头像
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "total_count": 4,
    +    "applied_transfer_projects": [
             {
    +            "project": {
    +                "id": 74,
    +                "identifier": "hehuisssjssjjsjs",
    +                "name": "hehuisssjssjjsjs",
    +                "description": "wwww",
    +                "is_public": false,
    +                "owner": {
    +                    "id": 10,
    +                    "type": "User",
    +                    "name": "testforge1",
    +                    "login": "testforge1",
    +                    "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
    +                }
    +            },
    +            "user": {
    +                "id": 6,
    +                "type": "User",
    +                "name": "何慧",
    +                "login": "yystopf",
    +                "image_url": "images/avatars/User/6?t=1622513134"
    +            },
                 "id": 7,
    -            "trend_type": "VersionRelease",
    -            "action_type": "创建了版本发布",
    -            "trend_id": 7,
    -            "user_name": "yystopf",
    -            "user_login": "yystopf",
    -            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    -            "action_time": "1个月前",
    -            "name": "v3.0.1",
    -            "tag_name": "v3.0.1",
    -            "target_commitish": "master",
    -            "tarball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v3.0.1.tar.gz",
    -            "zipball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v3.0.1.zip",
    -            "url": "http://localhost:10080/api/v1/repos/yystopf/ceshirepo1/releases/78",
    -            "version_gid": "78",
    -            "created_at": "2021-03-30 15:51"
    -        },
    -        {
    -            "id": 6,
    -            "trend_type": "VersionRelease",
    -            "action_type": "创建了版本发布",
    -            "trend_id": 6,
    -            "user_name": "yystopf",
    -            "user_login": "yystopf",
    -            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    -            "action_time": "1个月前",
    -            "name": "v3.0.0",
    -            "tag_name": "v3.0.0",
    -            "target_commitish": "master",
    -            "tarball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v3.0.0.tar.gz",
    -            "zipball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v3.0.0.zip",
    -            "url": "http://localhost:10080/api/v1/repos/yystopf/ceshirepo1/releases/77",
    -            "version_gid": "77",
    -            "created_at": "2021-03-30 15:33"
    -        },
    -        {
    -            "id": 5,
    -            "trend_type": "VersionRelease",
    -            "action_type": "创建了版本发布",
    -            "trend_id": 5,
    -            "user_name": "yystopf",
    -            "user_login": "yystopf",
    -            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    -            "action_time": "1个月前",
    -            "name": "v1.0.0",
    -            "tag_name": "v1.0.0",
    -            "target_commitish": "master",
    -            "tarball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v1.0.0.tar.gz",
    -            "zipball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v1.0.0.zip",
    -            "url": "http://localhost:10080/api/v1/repos/yystopf/ceshirepo1/releases/76",
    -            "version_gid": "76",
    -            "created_at": "2021-03-30 15:27"
    -        },
    -        {
    -            "id": 2,
    -            "trend_type": "VersionRelease",
    -            "action_type": "创建了版本发布",
    -            "trend_id": 2,
    -            "user_name": "yystopf",
    -            "user_login": "yystopf",
    -            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    -            "action_time": "2个月前",
    -            "name": "vvvv",
    -            "tag_name": "v1.1",
    -            "target_commitish": "dev",
    -            "tarball_url": "http://localhost:10080/yystopf/virus_blog/archive/v1.1.tar.gz",
    -            "zipball_url": "http://localhost:10080/yystopf/virus_blog/archive/v1.1.zip",
    -            "url": "http://localhost:10080/api/v1/repos/yystopf/virus_blog/releases/6",
    -            "version_gid": "6",
    -            "created_at": "2021-03-15 14:18"
    +            "status": "common",
    +            "created_at": "2021-06-09 16:41",
    +            "time_ago": "7分钟前"
             },
    -        {
    -            "id": 2,
    -            "trend_type": "PullRequest",
    -            "action_type": "创建了合并请求",
    -            "trend_id": 2,
    -            "user_name": "yystopf",
    -            "user_login": "yystopf",
    -            "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
    -            "action_time": "3个月前",
    -            "name": "444",
    -            "created_at": "2021-02-25 17:31"
    +        ...
    +    ]
    +}
    +

    用户接受申请

    +

    用户接受申请

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/users/yystopf/applied_projects/2/accept.json
    +
    await octokit.request('GET /api/users/:login/applied_projects/:id/accept.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/applied_projects/:id/accept.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    loginstring用户标识
    idint申请id
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idint申请id
    statusstring申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝
    time_agostring申请创建的时间
    project.idint申请项目的id
    project.identifierstring申请项目的标识
    project.namestring申请项目的名称
    project.descriptionstring申请项目的描述
    project.is_publicbool申请项目是否公开
    project.owner.idbool申请项目拥有者id
    project.owner.typestring申请项目拥有者类型
    project.owner.namestring申请项目拥有者昵称
    project.owner.loginstring申请项目拥有者标识
    project.owner.image_urlstring申请项目拥有者头像
    user.idint申请创建者的id
    user.typestring申请创建者的类型
    user.namestring申请创建者的名称
    user.loginstring申请创建者的标识
    user.image_urlstring申请创建者头像
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "project": {
    +        "id": 74,
    +        "identifier": "hehuisssjssjjsjs",
    +        "name": "hehuisssjssjjsjs",
    +        "description": "wwww",
    +        "is_public": false,
    +        "owner": {
    +            "id": 10,
    +            "type": "User",
    +            "name": "testforge1",
    +            "login": "testforge1",
    +            "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
             }
    -    ]
    +    },
    +    "user": {
    +        "id": 6,
    +        "type": "User",
    +        "name": "何慧",
    +        "login": "yystopf",
    +        "image_url": "images/avatars/User/6?t=1622513134"
    +    },
    +    "id": 7,
    +    "status": "accept",
    +    "created_at": "2021-06-09 16:41",
    +    "time_ago": "7分钟前"
     }
    -
    - -

    用户开发能力

    -

    用户开发能力, 默认为所有时间下的开发能力

    +

    用户拒绝申请

    +

    用户拒绝申请

    示例:

    -
    curl -X GET http://localhost:3000/api/users/yystopf/statistics/develop.json
    -
    await octokit.request('GET /api/users/:login/statistics/develop.json')
    -

    HTTP 请求

    -

    GET /api/users/:login/statistics/develop.json

    -

    请求字段说明:

    +
    curl -X POST http://localhost:3000/api/users/yystopf/applied_projects/2/refuse.json
    +
    await octokit.request('GET /api/users/:login/applied_projects/:id/refuse.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/applied_projects/:id/refuse.json

    +

    请求字段说明:

    @@ -1913,17 +3871,17 @@ Success — a happy kitten is an authenticated kitten! - - - + + + - - - + + +
    参数
    start_timeinteger时间戳,开始时间,格式:1621526400loginstring用户标识
    end_timeinteger时间戳,结束时间,格式:1622131200idint申请id
    -

    返回字段说明:

    +

    返回字段说明:

    @@ -1932,39 +3890,94 @@ Success — a happy kitten is an authenticated kitten! - + - + - - - + + + - - - + + + - + - + - - - + + + - - - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + +
    参数
    influenceid int影响力申请id
    contributionint贡献度statusstring申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝
    activityint活跃度time_agostring申请创建的时间
    experienceproject.id int项目经验申请项目的id
    languageint语言能力project.identifierstring申请项目的标识
    languages_percentfloat语言百分比project.namestring申请项目的名称
    each_language_scoreproject.descriptionstring申请项目的描述
    project.is_publicbool申请项目是否公开
    project.owner.idbool申请项目拥有者id
    project.owner.typestring申请项目拥有者类型
    project.owner.namestring申请项目拥有者昵称
    project.owner.loginstring申请项目拥有者标识
    project.owner.image_urlstring申请项目拥有者头像
    user.id int各门语言分数申请创建者的id
    user.typestring申请创建者的类型
    user.namestring申请创建者的名称
    user.loginstring申请创建者的标识
    user.image_urlstring申请创建者头像
    @@ -1972,82 +3985,78 @@ Success — a happy kitten is an authenticated kitten!

    返回的JSON示例:

    {
    -    "platform": {
    -        "influence": 61,
    -        "contribution": 75,
    -        "activity": 66,
    -        "experience": 95,
    -        "language": 87,
    -        "languages_percent": {
    -            "CSS": 0.03,
    -            "C#": 0.13,
    -            "Ruby": 0.04,
    -            "Go": 0.05,
    -            "C": 0.19,
    -            "Java": 0.34,
    -            "Python": 0.09,
    -            "C+": 0.01,
    -            "C++": 0.11,
    -            "Scala": 0.01,
    -            "HTML": 0.01
    -        },
    -        "each_language_score": {
    -            "CSS": 71,
    -            "C#": 86,
    -            "Ruby": 75,
    -            "Go": 77,
    -            "C": 90,
    -            "Java": 93,
    -            "Python": 83,
    -            "C+": 66,
    -            "C++": 85,
    -            "Scala": 66,
    -            "HTML": 66
    +    "project": {
    +        "id": 74,
    +        "identifier": "hehuisssjssjjsjs",
    +        "name": "hehuisssjssjjsjs",
    +        "description": "wwww",
    +        "is_public": false,
    +        "owner": {
    +            "id": 10,
    +            "type": "User",
    +            "name": "testforge1",
    +            "login": "testforge1",
    +            "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
             }
         },
         "user": {
    -        "influence": 60,
    -        "contribution": 72,
    -        "activity": 65,
    -        "experience": 88,
    -        "language": 84,
    -        "languages_percent": {
    -            "C": 0.25,
    -            "C#": 0.33,
    -            "C++": 0.13,
    -            "CSS": 0.08,
    -            "Go": 0.04,
    -            "HTML": 0.04,
    -            "Java": 0.04,
    -            "Ruby": 0.08
    -        },
    -        "each_language_score": {
    -            "C": 81,
    -            "C#": 84,
    -            "C++": 75,
    -            "CSS": 71,
    -            "Go": 66,
    -            "HTML": 66,
    -            "Java": 66,
    -            "Ruby": 71
    -        }
    -    }
    +        "id": 6,
    +        "type": "User",
    +        "name": "何慧",
    +        "login": "yystopf",
    +        "image_url": "images/avatars/User/6?t=1622513134"
    +    },
    +    "id": 7,
    +    "status": "accept",
    +    "created_at": "2021-06-09 16:41",
    +    "time_ago": "7分钟前"
     }
    -
    - -

    用户角色定位

    -

    用户角色定位,默认显示所有时间下的角色定位数据

    +

    Projects

    申请加入项目

    +

    申请加入项目

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/applied_projects.json
    +
    await octokit.request('POST /api/appliedr_projects.json')
    +

    HTTP 请求

    +

    POST /api/applied_projects.json

    +

    请求参数

    + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    applied_project.codestring邀请码
    applied_project.rolestring项目权限,reporter: 报告者, developer: 开发者,manager:管理员
    -

    示例:

    +

    请求的JSON示例

    -
    curl -X GET http://localhost:3000/api/users/yystopf/statistics/role.json
    -
    await octokit.request('GET /api/users/:login/statistics/role.json')
    -

    HTTP 请求

    -

    GET /api/users/:login/statistics/role.json

    -

    请求字段说明:

    +
    {
    +  "applied_project": {
    +    "code": "1una34",
    +    "role": "developer"
    +  }
    +}
    +

    返回字段说明

    @@ -2056,110 +4065,94 @@ Success — a happy kitten is an authenticated kitten! - - - + + + - - - + + + -
    参数
    start_timeinteger时间戳,开始时间,格式:1621526400idint申请id
    end_timeinteger时间戳,结束时间,格式:1622131200statusstring申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝
    -

    返回字段说明:

    - - - - + + + - - + - + - - - + + + - - - + + + -
    参数类型字段说明time_agostring项目申请创建的时间
    total_projects_countproject.id int用户所有的项目数量申请项目的id
    role.object.countint用户该语言下的项目数量project.identifierstring申请项目的标识
    role.object.percentfloat用户该语言下的项目占比project.namestring申请项目的名称
    - -
    -

    返回的JSON示例:

    -
    -
    {
    -    "total_projects_count": 27,
    -    "role": {
    -        "owner": {
    -            "count": 24,
    -            "percent": 0.89
    -        },
    -        "manager": {
    -            "count": 1,
    -            "percent": 0.04
    -        },
    -        "developer": {
    -            "count": 2,
    -            "percent": 0.07
    -        },
    -        "reporter": {
    -            "count": 0,
    -            "percent": 0.0
    -        }
    -    }
    -}
    -
    - -

    用户专业定位

    -

    用户专业定位,默认显示所有时间下的专业定位数据

    - -
    -

    示例:

    -
    -
    curl -X GET http://localhost:3000/api/users/yystopf/statistics/major.json
    -
    await octokit.request('GET /api/users/:login/statistics/major.json')
    -

    HTTP 请求

    -

    GET /api/users/:login/statistics/major.json

    -

    请求字段说明:

    - - - - + + + - - - - + + + - - - + + + -
    参数类型字段说明project.descriptionstring申请项目的描述
    start_timeinteger时间戳,开始时间,格式:1621526400project.is_publicbool申请项目是否公开
    end_timeinteger时间戳,结束时间,格式:1622131200project.owner.idbool申请项目拥有者id
    -

    返回字段说明:

    - - - - + + + - - + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明project.owner.typestring申请项目拥有者类型
    categoriesproject.owner.namestring申请项目拥有者昵称
    project.owner.loginstring申请项目拥有者标识
    project.owner.image_urlstring申请项目拥有者头像
    user.id int用户项目分类申请创建者的id
    user.typestring申请创建者的类型
    user.namestring申请创建者的名称
    user.loginstring申请创建者的标识
    user.image_urlstring申请创建者头像
    @@ -2167,195 +4160,209 @@ Success — a happy kitten is an authenticated kitten!

    返回的JSON示例:

    {
    -    "categories": [
    -        "大数据",
    -        "机器学习",
    -        "深度学习",
    -        "人工智能",
    -        "智慧医疗",
    -        "云计算"
    -    ]
    +    "project": {
    +        "id": 74,
    +        "identifier": "hehuisssjssjjsjs",
    +        "name": "hehuisssjssjjsjs",
    +        "description": "wwww",
    +        "is_public": false,
    +        "owner": {
    +            "id": 10,
    +            "type": "User",
    +            "name": "testforge1",
    +            "login": "testforge1",
    +            "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
    +        }
    +    },
    +    "user": {
    +        "id": 6,
    +        "type": "User",
    +        "name": "何慧",
    +        "login": "yystopf",
    +        "image_url": "images/avatars/User/6?t=1622513134"
    +    },
    +    "id": 7,
    +    "status": "common",
    +    "created_at": "2021-06-09 16:41",
    +    "time_ago": "1分钟前"
     }
    -
    - -

    待办事项-用户通知信息

    -

    待办事项-用户通知信息

    +

    获取项目列表

    +

    获取项目列表,也可以更加相关条件过滤搜素

    示例:

    -
    curl -X GET http://localhost:3000/api/users/yystopf/applied_messages.json
    -
    await octokit.request('GET /api/users/:login/applied_messages.json')
    -

    HTTP 请求

    -

    GET /api/users/:login/applied_messages.json

    -

    请求字段说明:

    +
    curl -X GET \
    +-d "page=1" \
    +-d "limit=5" \
    +http://localhost:3000/api/projects  | jq
    +
    await octokit.request('GET /api/projects')
    +

    HTTP 请求

    +

    GET api/projects

    +

    请求参数

    + + - + + + - - -
    参数必选默认 类型 字段说明
    loginpagefalse1 string用户标识
    -

    返回字段说明:

    - - - - - + - - - - + + + + + - - - + + + + + - + + + - + - + + + - + - + + + - - - - - - + - - - + + + + + - + + + - - - - - - + +
    参数类型字段说明页数,第几页
    appliedobject通知主体limitfalse15string每页多少条数据,默认15条
    applied.idint通知主体的迁移idsort_byfalsestring排序类型, 取值:updated_on、created_on、forked_count、praises_count; updated_on: 更新时间排序,created_on: 创建时间排序,forked_count: fork数据排序,praises_count: 点赞数量排序,默认为updated_on更新时间排序
    applied.statussort_directionfalse string通知主体的迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝排序方式,取值为: desc、asc; desc: 降序排序, asc: 升序排序, 默认为:desc
    applied.time_agosearchfalse string通知主体的迁移创建的时间按照项目名称搜索
    applied.project.idcategory_idfalse int通知主体的迁移项目的id
    applied.project.identifierstring通知主体的迁移项目的标识项目类别id
    applied.project.namestring通知主体的迁移项目的名称language_idfalseint项目语言id
    applied.project.descriptionproject_typefalse string通知主体的迁移项目的描述
    applied.project.is_publicbool通知主体的迁移项目是否公开项目类型, 取值为:common、mirror; common:开源托管项目, mirror:开源镜像项目
    +

    返回字段说明

    + - - - + + + + - - - + + + - + - + - + - + - + - + - + - + - - - + + + - - - + + + - - - + + + - + - + - + - - - - - - + - - - + + + - + - + - - - + + + - - - + + + - + - + - - - + + + - - - + + + - + - +
    applied.project.owner.idbool通知主体的迁移项目拥有者id参数类型字段说明
    applied.project.owner.typestring通知主体的迁移项目拥有者类型total_countint项目总条数
    applied.project.owner.nameid string通知主体的迁移项目拥有者昵称项目id
    applied.project.owner.loginname string通知主体的迁移项目拥有者标识项目名称
    applied.project.owner.image_urldescription string通知主体的迁移项目拥有者头像项目简介
    applied.user.idvisits int通知主体的迁移创建者的id流量数
    applied.user.typestring通知主体的迁移创建者的类型forked_countint被fork的数量
    applied.user.namestring通知主体的迁移创建者的名称praises_countintstar数量
    applied.user.loginstring通知主体的迁移创建者的标识is_publicboolean是否公开, true:公开,false:未公开
    applied.user.image_urlmirror_url string通知主体的迁移创建者头像镜像url
    applied_user.idlast_update_time int通知发起者的id
    applied_user.typestring通知发起者的类型最后更新时间,为UNIX格式的时间戳
    applied_user.namestring通知发起者的名称authorobject项目创建者
    applied_user.login-- name string通知发起者的标识用户名,也是用户标识
    applied_user.image_urlstring通知发起者头像categoryobject项目类别
    applied_typestring通知类型-- idint项目类型id
    name-- name string通知内容项目类型名称
    viewedstring是否已读,waiting:未读,viewed:已读languageobject项目语言
    statusstring通知状态, canceled:已取消,common: 正常,successed:成功,failure:失败-- idint项目语言id
    time_ago-- name string通知时间项目语言名称
    @@ -2363,127 +4370,57 @@ Success — a happy kitten is an authenticated kitten!

    返回的JSON示例:

    {
    -    "total_count": 5,
    -    "applied_messages": [
    -        {
    -            "applied": {
    -                "project": {
    -                    "id": 74,
    -                    "identifier": "hehuisssjssjjsjs",
    -                    "name": "hehuisssjssjjsjs",
    -                    "description": "wwww",
    -                    "is_public": false,
    -                    "owner": {
    -                        "id": 10,
    -                        "type": "User",
    -                        "name": "testforge1",
    -                        "login": "testforge1",
    -                        "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
    -                    }
    -                },
    -                "user": {
    -                    "id": 6,
    -                    "type": "User",
    -                    "name": "何慧",
    -                    "login": "yystopf",
    -                    "image_url": "images/avatars/User/6?t=1622513134"
    -                },
    -                "id": 6,
    -                "status": "accepted",
    -                "created_at": "2021-06-09 16:34",
    -                "time_ago": "1分钟前"
    -            },
    -            "applied_user": {
    -                "id": 6,
    -                "type": "User",
    -                "name": "何慧",
    -                "login": "yystopf",
    -                "image_url": "images/avatars/User/6?t=1622513134"
    -            },
    -            "applied_type": "AppliedProject",
    -            "name": "已通过你加入【hehuisssjssjjsjs】仓库的申请。",
    -            "viewed": "waiting",
    -            "status": "successed",
    -            "created_at": "2021-06-09 16:34",
    -            "time_ago": "1分钟前"
    -        },
    -        {
    -            "applied": {
    -                "project": {
    -                    "id": 86,
    -                    "identifier": "ceshi_repo1",
    -                    "name": "测试项目啊1",
    -                    "description": "二十多",
    -                    "is_public": true,
    -                    "owner": {
    -                        "id": 52,
    -                        "type": "Organization",
    -                        "name": "身份卡手动阀",
    -                        "login": "ceshi1",
    -                        "image_url": "images/avatars/Organization/52?t=1618805056"
    -                    }
    -                },
    -                "user": {
    -                    "id": 6,
    -                    "type": "User",
    -                    "name": "yystopf",
    -                    "login": "yystopf",
    -                    "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
    -                },
    -                "owner": {
    -                    "id": 9,
    -                    "type": "Organization",
    -                    "name": "测试组织",
    -                    "login": "ceshi_org",
    -                    "image_url": "images/avatars/Organization/9?t=1612706073"
    -                },
    -                "id": 4,
    -                "status": "common",
    -                "created_at": "2021-04-26 09:54",
    -                "time_ago": "35分钟前"
    -            },
    -            "applied_user": {
    -                "id": 6,
    -                "type": "User",
    -                "name": "yystopf",
    -                "login": "yystopf",
    -                "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
    -            },
    -            "applied_type": "AppliedTransferProject",
    -            "name": "正在将【测试项目啊1】仓库转移给【测试组织】",
    -            "viewed": "viewed",
    -            "status": "common",
    -            "created_at": "2021-04-26 09:54",
    -            "time_ago": "35分钟前"
    -        },
    -        ...
    -    ]
    +  "total_count": 3096,
    +  "projects": [
    +    {
    +      "id": 1400794,
    +      "repo_id": 1402452,
    +      "identifier": "cscw_2021_sponsor",
    +      "name": "Sponsor机制下的开源贡献",
    +      "description": "CSCW 2021 sponsor机制研究",
    +      "visits": 5,
    +      "praises_count": 0,
    +      "forked_count": 0,
    +      "is_public": true,
    +      "mirror_url": null,
    +      "type": 0,
    +      "last_update_time": 1611971671,
    +      "time_ago": "2天前",
    +      "forked_from_project_id": null,
    +      "open_devops": false,
    +      "platform": "forge",
    +      "author": {
    +        "name": "张迅晖",
    +        "login": "Nigel",
    +        "image_url": "images/avatars/User/3675?t=1611832880"
    +      },
    +      "category": {
    +        "id": 13,
    +        "name": "云计算和大数据"
    +      },
    +      "language": {
    +        "id": 34,
    +        "name": "Python3.6"
    +      }
    +    }
    +  ]
     }
    -

    待办事项-接受仓库

    -

    待办事项-接受仓库

    + + +

    推荐项目

    +

    获取推荐项目列表

    示例:

    -
    curl -X GET http://localhost:3000/api/users/yystopf/applied_transfer_projects.json
    -
    await octokit.request('GET /api/users/:login/applied_transfer_projects.json')
    -

    HTTP 请求

    -

    GET /api/users/:login/applied_transfer_projects.json

    -

    请求字段说明:

    - - - - - - - - - - - - -
    参数类型字段说明
    loginstring用户标识
    -

    返回字段说明:

    +
    curl -X GET \
    +http://localhost:3000/api/projects/recommend  | jq
    +
    await octokit.request('GET /api/projects/recommend.json')
    +

    HTTP 请求

    +

    GET api/projects/recommend

    +

    返回字段说明

    @@ -2492,367 +4429,336 @@ Success — a happy kitten is an authenticated kitten! - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - - - - - - + - - - + + + - - - + + + - - - + + + - - - + + + - + - + - + - - - - - - + - - - + + + - + - + - - - + + + - + - + - + - + - - - + + + - - - + + + - + - +
    参数
    idint迁移id
    statusstring迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝
    time_agostring迁移创建的时间
    project.idtotal_count int迁移项目的id项目总条数
    project.identifierid string迁移项目的标识项目id
    project.namename string迁移项目的名称项目名称
    project.descriptiondescription string迁移项目的描述
    project.is_publicbool迁移项目是否公开项目简介
    project.owner.idbool迁移项目拥有者idvisitsint流量数
    project.owner.typestring迁移项目拥有者类型forked_countint被fork的数量
    project.owner.namestring迁移项目拥有者昵称praises_countintstar数量
    project.owner.loginstring迁移项目拥有者标识is_publicboolean是否公开, true:公开,false:未公开
    project.owner.image_urlmirror_url string迁移项目拥有者头像镜像url
    user.idlast_update_time int迁移创建者的id
    user.typestring迁移创建者的类型最后更新时间,为UNIX格式的时间戳
    user.namestring迁移创建者的名称authorobject项目创建者
    user.login-- name string迁移创建者的标识用户名,也是用户标识
    user.image_urlstring迁移创建者头像categoryobject项目类别
    owner.id-- id int迁移接受者的id项目类型id
    owner.type-- name string迁移接受者的类型项目类型名称
    owner.namestring迁移接受者的名称languageobject项目语言
    owner.loginstring迁移接受者的标识-- idint项目语言id
    owner.image_url-- name string迁移接受者头像项目语言名称

    返回的JSON示例:

    -
    {
    -    "total_count": 4,
    -    "applied_transfer_projects": [
    -        {
    -            "project": {
    -                "id": 86,
    -                "identifier": "ceshi_repo1",
    -                "name": "测试项目啊1",
    -                "description": "二十多",
    -                "is_public": true,
    -                "owner": {
    -                    "id": 52,
    -                    "type": "Organization",
    -                    "name": "身份卡手动阀",
    -                    "login": "ceshi1",
    -                    "image_url": "images/avatars/Organization/52?t=1618805056"
    -                }
    -            },
    -            "user": {
    -                "id": 6,
    -                "type": "User",
    -                "name": "yystopf",
    -                "login": "yystopf",
    -                "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
    -            },
    -            "owner": {
    -                "id": 52,
    -                "type": "Organization",
    -                "name": "身份卡手动阀",
    -                "login": "ceshi1",
    -                "image_url": "images/avatars/Organization/52?t=1618805056"
    -            },
    -            "id": 1,
    -            "status": "canceled",
    -            "created_at": "2021-04-25 18:06",
    -            "time_ago": "16小时前"
    -        },
    -        ...
    -    ]
    -}
    -

    用户接受迁移

    -

    用户接受迁移

    +
    [
    +  {
    +    "id": 20,
    +    "repo_id": 2,
    +    "identifier": "PNAekinmH",
    +    "name": "FNILL",
    +    "visits": 13567,
    +    "author": {
    +      "name": "王一达",
    +      "login": "wangyida",
    +      "image_url": "avatars/User/b"
    +    },
    +    "category": {
    +      "id": 8,
    +      "name": "其他"
    +    }
    +  }
    +]
    +
    + +

    项目导航

    +

    获取项目导航信息

    示例:

    -
    curl -X POST http://localhost:3000/api/users/yystopf/applied_transfer_projects/2/accept.json
    -
    await octokit.request('GET /api/users/:login/applied_transfer_projects/:id/accept.json')
    -

    HTTP 请求

    -

    GET /api/users/:login/applied_transfer_projects/:id/accept.json

    -

    请求字段说明:

    - - - - - - - - - - - - - - - - - -
    参数类型字段说明
    loginstring用户标识
    idint迁移id
    -

    返回字段说明:

    +
    curl -X GET \
    +http://localhost:3000/api/yystopf/ceshi/menu_list  | jq
    +
    await octokit.request('GET /api/yystopf/ceshi/menu_list')
    +

    HTTP 请求

    +

    GET api/:owner/:repo/menu_list

    +

    请求参数

    + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + - + + + - + +
    参数必选默认 类型 字段说明
    idint迁移id
    statusstring迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝
    time_agostring迁移创建的时间
    project.idint迁移项目的id
    project.identifierstring迁移项目的标识
    project.namestring迁移项目的名称
    project.descriptionstring迁移项目的描述
    project.is_publicbool迁移项目是否公开
    project.owner.idbool迁移项目拥有者id
    project.owner.typeowner string迁移项目拥有者类型用户登录名
    project.owner.namerepo string迁移项目拥有者昵称项目标识identifier
    +

    返回字段说明

    + - - - + + + + - + - + +
    project.owner.loginstring迁移项目拥有者标识参数类型字段说明
    project.owner.image_urlmenu_name string迁移项目拥有者头像导航名称, home:主页,code:代码库,issues:易修,pulls:合并请求,devops:工作流,versions:里程碑,activity:动态,setting:仓库设置
    + +
    +

    返回的JSON示例:

    +
    +
    [
    +    {
    +        "menu_name": "home"
    +    },
    +    {
    +        "menu_name": "code"
    +    },
    +    {
    +        "menu_name": "pulls"
    +    },
    +    {
    +        "menu_name": "activity"
    +    }
    +]
    +

    项目主页

    +

    获取项目主页信息

    + +
    +

    示例:

    +
    +
    curl -X GET \
    +http://localhost:3000/api/jasder/forgeplus/about  | jq
    +
    await octokit.request('GET /api/jasder/forgeplus/about')
    +

    HTTP 请求

    +

    GET api/:owner/:repo/about

    +

    请求参数

    + - - - + + + + + + - + + + - + - + + + - + +
    user.idint迁移创建者的id参数必选默认类型字段说明
    user.typeowner string迁移创建者的类型用户登录名
    user.namerepo string迁移创建者的名称项目标识identifier
    +

    返回字段说明

    + - - - + + + + - + - - - - - - + - + - + - - - + + + +
    user.loginstring迁移创建者的标识参数类型字段说明
    user.image_urlidentifier string迁移创建者头像
    owner.idint迁移接受者的idproject's identifier
    owner.typecontent string迁移接受者的类型主页内容
    owner.namestring迁移接受者的名称attachmentsarray附件
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +  "content": "",
    +  "identifier": "forgeplus",
    +  attachments: []
    +}
    +
    + +

    项目模块信息

    +

    项目模块信息

    + +
    +

    示例:

    +
    +
    curl -X GET \
    +http://localhost:3000/api/yystopf/ceshi/project_units.json
    +
    await octokit.request('GET /api/yystopf/ceshi/project_units')
    +

    HTTP 请求

    +

    GET /api/yystopf/ceshi/project_units

    +

    返回字段说明:

    + - - - + + + + - + - +
    owner.loginstring迁移接受者的标识参数类型字段说明
    owner.image_urltype string迁移接受者头像模块名称

    返回的JSON示例:

    -
    {
    -    "project": {
    -        "id": 86,
    -        "identifier": "ceshi_repo1",
    -        "name": "测试项目啊1",
    -        "description": "二十多",
    -        "is_public": true,
    -        "owner": {
    -            "id": 52,
    -            "type": "Organization",
    -            "name": "身份卡手动阀",
    -            "login": "ceshi1",
    -            "image_url": "images/avatars/Organization/52?t=1618805056"
    -        }
    -    },
    -    "user": {
    -        "id": 6,
    -        "type": "User",
    -        "name": "yystopf",
    -        "login": "yystopf",
    -        "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
    +
    [
    +    {
    +        "type": "code"
         },
    -    "owner": {
    -        "id": 52,
    -        "type": "Organization",
    -        "name": "身份卡手动阀",
    -        "login": "ceshi1",
    -        "image_url": "images/avatars/Organization/52?t=1618805056"
    +    {
    +        "type": "pulls"
         },
    -    "id": 1,
    -    "status": "canceled",
    -    "created_at": "2021-04-25 18:06",
    -    "time_ago": "16小时前"
    -}
    -

    用户拒绝迁移

    -

    用户拒绝迁移

    +
    { + "type": "issues" + } +] +

    更改项目模块展示

    +

    更改项目模块展示

    示例:

    -
    curl -X POST http://localhost:3000/api/users/yystopf/applied_transfer_projects/2/refuse.json
    -
    await octokit.request('GET /api/users/:login/applied_transfer_projects/:id/refuse.json')
    -

    HTTP 请求

    -

    GET /api/users/:login/applied_transfer_projects/:id/refuse.json

    -

    请求字段说明:

    +
    curl -X POST \
    +-H  "accept: application/json" \
    +-H  "Content-Type: application/json" \
    +-d "{ \"unit_typs\": [\"code\", \"pulls\"]}" \
    +http://localhost:3000/api/yystopf/ceshi/project_units.json
    +
    await octokit.request('POST /api/yystopf/ceshi/project_units')
    +

    HTTP 请求

    +

    POST /api/yystopf/ceshi/project_units

    +

    请求参数

    + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    unit_typesarray项目模块内容, 支持以下参数:code:代码库,issues:易修,pulls:合并请求,devops:工作流,versions:里程碑
    +

    返回字段说明:

    @@ -2861,138 +4767,266 @@ Success — a happy kitten is an authenticated kitten! - - - + + + - - - + + +
    参数
    loginstring用户标识statusint返回状态, 0: 表示操作成功
    idint迁移idmessagestring返回信息说明
    -

    返回字段说明:

    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "status": 0,
    +    "message": "success"
    +}
    +

    创建项目

    +

    创建项目

    + +
    +

    示例:

    +
    +
    curl -X POST \
    +-d "user_id=36401" \
    +-d "name=hnfl_demo" \
    +-d "description=my first project" \
    +-d "repository_name=hnfl_demo" \
    +-d "project_category_id=1" \
    +-d "project_language_id=2" \
    +-d "ignore_id=2" \
    +-d "license_id=1" \
    +http://localhost:3000/api/projects.json
    +
    await octokit.request('GET /api/projects.json')
    +

    HTTP 请求

    +

    POST api/projects

    +

    请求参数

    + + - + + + - + - + + + - + - + + + - + - - - + + + + + - - - + + + + + - - - + + + + + - - - + + + + + - - - + + + + + - - - + + + + + +
    参数必选默认 类型 字段说明
    iduser_id int迁移id用户id或者组织id
    statusname string迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝项目名称
    time_agodescription string迁移创建的时间项目描述
    project.idint迁移项目的idrepository_namestring仓库名称, 只含有数字、字母、下划线不能以下划线开头和结尾,且唯一
    project.identifierstring迁移项目的标识project_category_idint项目类别id
    project.namestring迁移项目的名称project_language_idint项目语言id
    project.descriptionstring迁移项目的描述ignore_idintgitignore相关id
    project.is_publicbool迁移项目是否公开license_idint开源许可证id
    project.owner.idbool迁移项目拥有者idprivateboolean项目是否私有, true:为私有,false: 公开,默认为公开
    +

    返回字段说明

    + - - - + + + + - - - + + + - + - + +
    project.owner.typestring迁移项目拥有者类型参数类型字段说明
    project.owner.namestring迁移项目拥有者昵称idintid
    project.owner.loginname string迁移项目拥有者标识项目名称
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +  "id": 3240,
    +  "name": "好项目"
    +}
    +

    创建镜像项目

    +

    创建镜像项目

    + +
    +

    示例:

    +
    +
    curl -X POST \
    +-d "user_id=36408" \
    +-d "clone_addr=https://gitea.com/mx8090alex/golden.git" \
    +-d "name=golden_mirror1" \
    +-d "description=golden_mirror" \
    +-d "project_category_id=1" \
    +-d "project_language_id=2" \
    +http://localhost:3000/api/projects/migrate.json
    +
    await octokit.request('GET /api/projects/migrate.json')
    +

    HTTP 请求

    +

    POST api/projects/migrate.json

    +

    请求参数

    + - - - + + + + + + - + + + - + - + + + - + - + + + - + - + + + - + - + + + - + - + + + - + - + + + + + + + + + + + + + + + + + - + - + + + - + + + + + + + + + +
    project.owner.image_urlstring迁移项目拥有者头像参数必选默认类型字段说明
    user.iduser_id int迁移创建者的id用户id或者组织id
    user.typename string迁移创建者的类型项目名称
    user.nameclone_addr string迁移创建者的名称镜像项目clone地址
    user.logindescription string迁移创建者的标识项目描述
    user.image_urlrepository_name string迁移创建者头像仓库名称, 只含有数字、字母、下划线不能以下划线开头和结尾,且唯一
    owner.idproject_category_id int迁移接受者的id项目类别id
    owner.typeproject_language_idint项目语言id
    is_mirrorboolean是否设置为镜像, true:是, false:否,默认为否
    auth_username string迁移接受者的类型镜像源仓库的登录用户名
    owner.nameauth_password string迁移接受者的名称镜像源仓库的登录秘密
    privateboolean项目是否私有, true:为私有,false: 非私有,默认为公开
    +

    返回字段说明

    + + + + + + - - - + + + - + - +
    参数类型字段说明
    owner.loginstring迁移接受者的标识idintid
    owner.image_urlname string迁移接受者头像项目名称
    @@ -3000,64 +5034,38 @@ Success — a happy kitten is an authenticated kitten!

    返回的JSON示例:

    {
    -    "project": {
    -        "id": 86,
    -        "identifier": "ceshi_repo1",
    -        "name": "测试项目啊1",
    -        "description": "二十多",
    -        "is_public": true,
    -        "owner": {
    -            "id": 52,
    -            "type": "Organization",
    -            "name": "身份卡手动阀",
    -            "login": "ceshi1",
    -            "image_url": "images/avatars/Organization/52?t=1618805056"
    -        }
    -    },
    -    "user": {
    -        "id": 6,
    -        "type": "User",
    -        "name": "yystopf",
    -        "login": "yystopf",
    -        "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
    -    },
    -    "owner": {
    -        "id": 52,
    -        "type": "Organization",
    -        "name": "身份卡手动阀",
    -        "login": "ceshi1",
    -        "image_url": "images/avatars/Organization/52?t=1618805056"
    -    },
    -    "id": 1,
    -    "status": "canceled",
    -    "created_at": "2021-04-25 18:06",
    -    "time_ago": "16小时前"
    +  "id": 3241,
    +  "name": "这是一个镜像项目"
     }
    -

    待办事项-项目申请

    -

    待办事项-项目申请

    +

    同步镜像

    +

    手动同步镜像

    示例:

    -
    curl -X GET http://localhost:3000/api/users/yystopf/applied_projects.json
    -
    await octokit.request('GET /api/users/:login/applied_projects.json')
    -

    HTTP 请求

    -

    GET /api/users/:login/applied_projects.json

    -

    请求字段说明:

    +
    curl -X POST http://localhost:3000/api/repositories/1244/sync_mirror.json
    +
    await octokit.request('POST /api/repositories/1244/sync_mirror.json')
    +

    HTTP 请求

    +

    POST api/repositories/:id/sync_mirror.json

    +

    请求参数

    + + - - - + + + + +
    参数必选默认 类型 字段说明
    loginstring用户标识idint仓库id
    -

    返回字段说明:

    +

    返回字段说明

    @@ -3066,163 +5074,224 @@ Success — a happy kitten is an authenticated kitten! - - - - - - - + + - + - - - - - - + +
    参数
    idint申请id
    statusstring申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝int状态码, 0:标识请求成功
    time_agomessage string申请创建的时间
    project.idint申请项目的id服务端返回的信息说明
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +  "status": 0,
    +  "message": "success"
    +}
    +

    Fork项目

    +

    fork项目

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/jasder/forgeplus/forks.json
    +
    await octokit.request('POST /api/jaser/jasder_test/forks.json')
    +

    HTTP 请求

    +

    POST api/:owner/:repo/forks.json

    +

    请求参数

    + - - - + + + + + + - + + + - + - + + + - + +
    project.identifierstring申请项目的标识参数必选默认类型字段说明
    project.nameowner string申请项目的名称用户登录名
    project.descriptionrepo string申请项目的描述项目标识identifier
    +

    返回字段说明

    + - - - + + + + - - - + + + - + - + +
    project.is_publicbool申请项目是否公开参数类型字段说明
    project.owner.idbool申请项目拥有者ididint项目id
    project.owner.typeidentifier string申请项目拥有者类型项目标识
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +  "id": 3290,
    +  "identifier": "newadm"
    +}
    +

    用户管理的组织列表

    +

    用户管理的组织列表

    + +
    +

    示例:

    +
    +
    curl -X GET \
    +http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizations.json  | jq
    +
    await octokit.request('GET /api/:owner/:repo/applied_transfer_projects/organizations')
    +

    HTTP 请求

    +

    GET api/:owner/:repo/applied_transfer_projects/organizations

    +

    请求参数

    + - - - + + + + + + - + + + - + - + + + - + +
    project.owner.namestring申请项目拥有者昵称参数必选默认类型字段说明
    project.owner.loginowner string申请项目拥有者标识用户登录名
    project.owner.image_urlrepo string申请项目拥有者头像项目标识identifier
    +

    返回字段说明

    + - - - + + + + - + - + - + - + - + - + - - - - -
    user.idint申请创建者的id参数类型字段说明
    user.typename string申请创建者的类型组织标识
    user.namenickname string申请创建者的名称组织名称
    user.logindescription string申请创建者的标识组织描述
    user.image_urlstring申请创建者头像
    - -
    -

    返回的JSON示例:

    -
    -
    {
    -    "total_count": 4,
    -    "applied_transfer_projects": [
    -        {
    -            "project": {
    -                "id": 74,
    -                "identifier": "hehuisssjssjjsjs",
    -                "name": "hehuisssjssjjsjs",
    -                "description": "wwww",
    -                "is_public": false,
    -                "owner": {
    -                    "id": 10,
    -                    "type": "User",
    -                    "name": "testforge1",
    -                    "login": "testforge1",
    -                    "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
    -                }
    -            },
    -            "user": {
    -                "id": 6,
    -                "type": "User",
    -                "name": "何慧",
    -                "login": "yystopf",
    -                "image_url": "images/avatars/User/6?t=1622513134"
    -            },
    -            "id": 7,
    -            "status": "common",
    -            "created_at": "2021-06-09 16:41",
    -            "time_ago": "7分钟前"
    +avatar_url
    +string|组织头像
    +
    +
    +
    +
    +
    +

    返回的JSON示例:

    +
    +
    {
    +    "total_count": 3,
    +    "organizations": [
    +        {
    +            "id": 9,
    +            "name": "ceshi_org",
    +            "nickname": "测试组织",
    +            "description": "测试组织",
    +            "avatar_url": "images/avatars/Organization/9?t=1612706073"
             },
    -        ...
    +        {
    +            "id": 51,
    +            "name": "ceshi",
    +            "nickname": "测试组织哈哈哈",
    +            "description": "23212312",
    +            "avatar_url": "images/avatars/Organization/51?t=1618800723"
    +        },
    +        {
    +            "id": 52,
    +            "name": "ceshi1",
    +            "nickname": "身份卡手动阀",
    +            "description": "1231手动阀是的",
    +            "avatar_url": "images/avatars/Organization/52?t=1618805056"
    +        }
         ]
     }
    -

    用户接受申请

    -

    用户接受申请

    +

    迁移项目

    +

    迁移项目,edit接口is_transfering为true表示正在迁移

    示例:

    -
    curl -X POST http://localhost:3000/api/users/yystopf/applied_projects/2/accept.json
    -
    await octokit.request('GET /api/users/:login/applied_projects/:id/accept.json')
    -

    HTTP 请求

    -

    GET /api/users/:login/applied_projects/:id/accept.json

    -

    请求字段说明:

    +
    curl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects.json
    +
    await octokit.request('POST /api/:owner/:repo/applied_transfer_projects.json')
    +

    HTTP 请求

    +

    POST /api/:owner/:repo/applied_transfer_projects.json

    +

    请求参数

    + + - + + + - + - - - + + + + + + + + + + + +
    参数必选默认 类型 字段说明
    loginowner string用户标识用户登录名
    idint申请idrepostring项目标识identifier
    owner_namestring迁移对象标识
    -

    返回字段说明:

    +

    返回字段说明

    @@ -3233,92 +5302,117 @@ Success — a happy kitten is an authenticated kitten! - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数
    id int申请id项目id
    status string申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝项目迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝
    time_ago string申请创建的时间项目迁移创建的时间
    project.id int申请项目的id迁移项目的id
    project.identifier string申请项目的标识迁移项目的标识
    project.name string申请项目的名称迁移项目的名称
    project.description string申请项目的描述迁移项目的描述
    project.is_public bool申请项目是否公开迁移项目是否公开
    project.owner.id bool申请项目拥有者id迁移项目拥有者id
    project.owner.type string申请项目拥有者类型迁移项目拥有者类型
    project.owner.name string申请项目拥有者昵称迁移项目拥有者昵称
    project.owner.login string申请项目拥有者标识迁移项目拥有者标识
    project.owner.image_url string申请项目拥有者头像迁移项目拥有者头像
    user.id int申请创建者的id迁移创建者的id
    user.type string申请创建者的类型迁移创建者的类型
    user.name string申请创建者的名称迁移创建者的名称
    user.login string申请创建者的标识迁移创建者的标识
    user.image_url string申请创建者头像迁移创建者头像
    owner.idint迁移接受者的id
    owner.typestring迁移接受者的类型
    owner.namestring迁移接受者的名称
    owner.loginstring迁移接受者的标识
    owner.image_urlstring迁移接受者头像
    @@ -3327,61 +5421,74 @@ Success — a happy kitten is an authenticated kitten!
    {
         "project": {
    -        "id": 74,
    -        "identifier": "hehuisssjssjjsjs",
    -        "name": "hehuisssjssjjsjs",
    -        "description": "wwww",
    -        "is_public": false,
    +        "id": 86,
    +        "identifier": "ceshi_repo1",
    +        "name": "测试项目啊1",
    +        "description": "二十多",
    +        "is_public": true,
             "owner": {
    -            "id": 10,
    -            "type": "User",
    -            "name": "testforge1",
    -            "login": "testforge1",
    -            "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
    +            "id": 52,
    +            "type": "Organization",
    +            "name": "身份卡手动阀",
    +            "login": "ceshi1",
    +            "image_url": "images/avatars/Organization/52?t=1618805056"
             }
         },
         "user": {
             "id": 6,
             "type": "User",
    -        "name": "何慧",
    +        "name": "yystopf",
             "login": "yystopf",
    -        "image_url": "images/avatars/User/6?t=1622513134"
    +        "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
         },
    -    "id": 7,
    -    "status": "accept",
    -    "created_at": "2021-06-09 16:41",
    -    "time_ago": "7分钟前"
    +    "owner": {
    +        "id": 9,
    +        "type": "Organization",
    +        "name": "测试组织",
    +        "login": "ceshi_org",
    +        "image_url": "images/avatars/Organization/9?t=1612706073"
    +    },
    +    "id": 4,
    +    "status": "common",
    +    "created_at": "2021-04-26 09:54",
    +    "time_ago": "1分钟前"
     }
    -

    用户拒绝申请

    -

    用户拒绝申请

    +

    取消迁移项目

    +

    迁移项目,edit接口is_transfering为true表示正在迁移

    示例:

    -
    curl -X POST http://localhost:3000/api/users/yystopf/applied_projects/2/refuse.json
    -
    await octokit.request('GET /api/users/:login/applied_projects/:id/refuse.json')
    -

    HTTP 请求

    -

    GET /api/users/:login/applied_projects/:id/refuse.json

    -

    请求字段说明:

    +
    curl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/cancel.json
    +
    await octokit.request('POST /api/:owner/:repo/applied_transfer_projects/cancel.json')
    +

    HTTP 请求

    +

    POST /api/:owner/:repo/applied_transfer_projects/cancel.json

    +

    请求参数

    + + - + + + - + - - - + + + + +
    参数必选默认 类型 字段说明
    loginowner string用户标识用户登录名
    idint申请idrepostring项目标识identifier
    -

    返回字段说明:

    +

    返回字段说明

    @@ -3392,92 +5499,117 @@ Success — a happy kitten is an authenticated kitten! - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - +
    参数
    id int申请id迁移id
    status string申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝
    time_ago string申请创建的时间迁移创建的时间
    project.id int申请项目的id迁移项目的id
    project.identifier string申请项目的标识迁移项目的标识
    project.name string申请项目的名称迁移项目的名称
    project.description string申请项目的描述迁移项目的描述
    project.is_public bool申请项目是否公开迁移项目是否公开
    project.owner.id bool申请项目拥有者id迁移项目拥有者id
    project.owner.type string申请项目拥有者类型迁移项目拥有者类型
    project.owner.name string申请项目拥有者昵称迁移项目拥有者昵称
    project.owner.login string申请项目拥有者标识迁移项目拥有者标识
    project.owner.image_url string申请项目拥有者头像迁移项目拥有者头像
    user.id int申请创建者的id迁移创建者的id
    user.type string申请创建者的类型迁移创建者的类型
    user.namestring迁移创建者的名称
    user.loginstring迁移创建者的标识
    user.image_urlstring迁移创建者头像
    owner.idint迁移接受者的id
    owner.typestring迁移接受者的类型
    user.nameowner.name string申请创建者的名称迁移接受者的名称
    user.loginowner.login string申请创建者的标识迁移接受者的标识
    user.image_urlowner.image_url string申请创建者头像迁移接受者头像
    @@ -3486,42 +5618,49 @@ Success — a happy kitten is an authenticated kitten!
    {
         "project": {
    -        "id": 74,
    -        "identifier": "hehuisssjssjjsjs",
    -        "name": "hehuisssjssjjsjs",
    -        "description": "wwww",
    -        "is_public": false,
    +        "id": 86,
    +        "identifier": "ceshi_repo1",
    +        "name": "测试项目啊1",
    +        "description": "二十多",
    +        "is_public": true,
             "owner": {
    -            "id": 10,
    -            "type": "User",
    -            "name": "testforge1",
    -            "login": "testforge1",
    -            "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
    +            "id": 52,
    +            "type": "Organization",
    +            "name": "身份卡手动阀",
    +            "login": "ceshi1",
    +            "image_url": "images/avatars/Organization/52?t=1618805056"
             }
         },
         "user": {
             "id": 6,
             "type": "User",
    -        "name": "何慧",
    +        "name": "yystopf",
             "login": "yystopf",
    -        "image_url": "images/avatars/User/6?t=1622513134"
    +        "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
         },
    -    "id": 7,
    -    "status": "accept",
    -    "created_at": "2021-06-09 16:41",
    -    "time_ago": "7分钟前"
    +    "owner": {
    +        "id": 9,
    +        "type": "Organization",
    +        "name": "测试组织",
    +        "login": "ceshi_org",
    +        "image_url": "images/avatars/Organization/9?t=1612706073"
    +    },
    +    "id": 4,
    +    "status": "common",
    +    "created_at": "2021-04-26 09:54",
    +    "time_ago": "1分钟前"
     }
    -

    Projects

    申请加入项目

    -

    申请加入项目

    +

    Repositories

    仓库详情

    +

    仓库详情

    示例:

    -
    curl -X POST http://localhost:3000/api/applied_projects.json
    -
    await octokit.request('POST /api/appliedr_projects.json')
    +
    curl -X GET http://localhost:3000/api/jasder/jasder_test.json
    +
    await octokit.request('GET /api/jasder/jasder_test.json')
     

    HTTP 请求

    -

    POST /api/applied_projects.json

    -

    请求参数

    +

    GET /api/:owner/:repo

    +

    请求参数:

    @@ -3532,31 +5671,21 @@ Success — a happy kitten is an authenticated kitten! - + - + - - + + - +
    参数
    applied_project.codeowner string邀请码用户登录名
    applied_project.rolerepo string项目权限,reporter: 报告者, developer: 开发者,manager:管理员项目标识identifier
    - -
    -

    请求的JSON示例

    -
    -
    {
    -  "applied_project": {
    -    "code": "1una34",
    -    "role": "developer"
    -  }
    -}
    -

    返回字段说明

    +

    返回字段说明:

    @@ -3567,92 +5696,128 @@ Success — a happy kitten is an authenticated kitten! - + - + - + - + - + - - - + + + - + - + - - - + + + - + - + +
    参数
    id int申请idid
    statusname string申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝项目名称
    time_agoidentifier string项目申请创建的时间项目标识
    project.idint申请项目的idis_publicboolean项目是否公开, true:公开,false:私有
    project.identifierdescription string申请项目的标识项目简介
    project.namestring申请项目的名称repo_idint仓库id
    project.descriptionrepo_identifier string申请项目的描述仓库标识
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +  "name": "ni项目",
    +  "identifier": "mirror_demo",
    +  "is_public": true,
    +  "description": "my first project mirror_demo",
    +  "repo_id": 75073,
    +  "repo_identifier": "mirror_demo"
    +}
    +

    仓库详情(简版)

    +

    仓库详情

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/jasder/jasder_test/simple.json
    +
    await octokit.request('GET /api/jasder/jasder_test/simple.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/simple

    +

    请求参数:

    + - - - + + + + + + - - - + + + + + - + + + - + +
    project.is_publicbool申请项目是否公开参数必选默认类型字段说明
    project.owner.idbool申请项目拥有者idownerstring用户登录名
    project.owner.typerepo string申请项目拥有者类型项目标识identifier
    +

    返回字段说明:

    + - - - + + + + - - - + + + - + - + - - - + + + - - - + + + - + - + - - - + + + - + - +
    project.owner.namestring申请项目拥有者昵称参数类型字段说明
    project.owner.loginstring申请项目拥有者标识idintid
    project.owner.image_urlname string申请项目拥有者头像项目名称
    user.idint申请创建者的ididentifierstring项目标识
    user.typestring申请创建者的类型is_publicboolean项目是否公开, true:公开,false:私有
    user.namedescription string申请创建者的名称项目简介
    user.loginstring申请创建者的标识repo_idint仓库id
    user.image_urlrepo_identifier string申请创建者头像仓库标识
    @@ -3660,46 +5825,24 @@ Success — a happy kitten is an authenticated kitten!

    返回的JSON示例:

    {
    -    "project": {
    -        "id": 74,
    -        "identifier": "hehuisssjssjjsjs",
    -        "name": "hehuisssjssjjsjs",
    -        "description": "wwww",
    -        "is_public": false,
    -        "owner": {
    -            "id": 10,
    -            "type": "User",
    -            "name": "testforge1",
    -            "login": "testforge1",
    -            "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
    -        }
    -    },
    -    "user": {
    -        "id": 6,
    -        "type": "User",
    -        "name": "何慧",
    -        "login": "yystopf",
    -        "image_url": "images/avatars/User/6?t=1622513134"
    -    },
    -    "id": 7,
    -    "status": "common",
    -    "created_at": "2021-06-09 16:41",
    -    "time_ago": "1分钟前"
    +  "name": "ni项目",
    +  "identifier": "mirror_demo",
    +  "is_public": true,
    +  "description": "my first project mirror_demo",
    +  "repo_id": 75073,
    +  "repo_identifier": "mirror_demo"
     }
    -

    获取项目列表

    -

    获取项目列表,也可以更加相关条件过滤搜素

    +

    仓库详情(新版)

    +

    仓库详情

    -

    示例:

    -
    -
    curl -X GET \
    --d "page=1" \
    --d "limit=5" \
    -http://localhost:3000/api/projects  | jq
    -
    await octokit.request('GET /api/projects')
    -

    HTTP 请求

    -

    GET api/projects

    -

    请求参数

    +

    示例:

    + +
    curl -X GET http://localhost:3000/api/yystopf/ceshi/detail.json
    +
    await octokit.request('GET /api/yystopf/ceshi/detail.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/detail

    +

    请求参数:

    @@ -3710,421 +5853,369 @@ http://localhost:3000/api/projects | jq - - - + + + - + - - - + + + - + +
    参数
    pagefalse1owner string页数,第几页用户登录名
    limitfalse15repo string每页多少条数据,默认15条项目标识identifier
    +

    返回字段说明:

    + - - - - - + + + + - - - + - + - - - + - + - - - - - + + + - - - - - + + + - - - + - + -
    sort_byfalsestring排序类型, 取值:updated_on、created_on、forked_count、praises_count; updated_on: 更新时间排序,created_on: 创建时间排序,forked_count: fork数据排序,praises_count: 点赞数量排序,默认为updated_on更新时间排序参数类型字段说明
    sort_directionfalsecontent string排序方式,取值为: desc、asc; desc: 降序排序, asc: 升序排序, 默认为:desc仓库简介
    searchfalsewebsite string按照项目名称搜索仓库网址
    category_idfalseint项目类别idreadmestringreadme文件|
    language_idfalseint项目语言ididentifierstring项目标识
    project_typefalsename string项目类型, 取值为:common、mirror; common:开源托管项目, mirror:开源镜像项目项目名称
    -

    返回字段说明

    - - - - + + + - - + - + - - - + + + - - - + + + - - - + + + - + - + - + - + - + - + - - - + + + + + + + + - + - + + + + + + - + - - - + + + - - - + + + - - - + + + - + - + - - - + + + - + - - - - - - + - + - + -
    参数类型字段说明issues_countint项目issue数量
    total_countpull_requests_count int项目总条数项目合并请求数量
    idstring项目idproject_identifierint项目标识
    namestring项目名称praises_countint项目点赞数量
    descriptionstring项目简介forked_countint项目复刻数量
    visitswatchers_count int流量数项目关注数量
    forked_countversions_count int被fork的数量项目里程碑数量
    praises_countversion_releases_count intstar数量项目发行版数量
    is_publicboolean是否公开, true:公开,false:未公开version_releasesed_countint项目发行版已发行数量
    permissionstring项目权限
    mirror_url string镜像url镜像地址
    last_update_timemirrorbool是否为镜像项目
    type int最后更新时间,为UNIX格式的时间戳项目类型 0 普通项目 1 普通镜像项目 2 同步镜像项目
    authorobject项目创建者open_devopsint是否开启devops
    -- namestring用户名,也是用户标识watchedbool是否关注
    categoryobject项目类别praisedbool是否点赞
    -- idstatus int项目类型id项目状态
    -- namestring项目类型名称forked_from_project_idintfork项目id
    languagefork_info object项目语言
    -- idint项目语言idfork项目信息
    -- namesize string项目语言名称仓库大小
    - -
    -

    返回的JSON示例:

    -
    -
    {
    -  "total_count": 3096,
    -  "projects": [
    -    {
    -      "id": 1400794,
    -      "repo_id": 1402452,
    -      "identifier": "cscw_2021_sponsor",
    -      "name": "Sponsor机制下的开源贡献",
    -      "description": "CSCW 2021 sponsor机制研究",
    -      "visits": 5,
    -      "praises_count": 0,
    -      "forked_count": 0,
    -      "is_public": true,
    -      "mirror_url": null,
    -      "type": 0,
    -      "last_update_time": 1611971671,
    -      "time_ago": "2天前",
    -      "forked_from_project_id": null,
    -      "open_devops": false,
    -      "platform": "forge",
    -      "author": {
    -        "name": "张迅晖",
    -        "login": "Nigel",
    -        "image_url": "images/avatars/User/3675?t=1611832880"
    -      },
    -      "category": {
    -        "id": 13,
    -        "name": "云计算和大数据"
    -      },
    -      "language": {
    -        "id": 34,
    -        "name": "Python3.6"
    -      }
    -    }
    -  ]
    -}
    -
    - -

    推荐项目

    -

    获取推荐项目列表

    - -
    -

    示例:

    -
    -
    curl -X GET \
    -http://localhost:3000/api/projects/recommend  | jq
    -
    await octokit.request('GET /api/projects/recommend.json')
    -

    HTTP 请求

    -

    GET api/projects/recommend

    -

    返回字段说明

    - - - - + + + - - - - + + + - + - + - - - + + + - + - + - - - + + + - - - + + + - - - + + + - - - + + + - + - + - + - - - - - - + - + - - - - - - + - + - + - + - + - - - + + + - + - + - + - - -
    参数类型字段说明ssh_urlstring项目ssh地址
    total_countint项目总条数clone_urlstring项目克隆地址
    iddefault_branch string项目id仓库默认分支
    namestring项目名称emptybool仓库是否为空
    descriptionfull_name string项目简介仓库全称
    visitsint流量数privatebool仓库是否为私有项目
    forked_countint被fork的数量license_namestring许可证名称
    praises_countintstar数量release_versions.list.namestring项目issue数量
    is_publicboolean是否公开, true:公开,false:未公开release_versions.list.tag_namestring发行版标签名称
    mirror_urlrelease_versions.list.created_at string镜像url发行版创建时间
    last_update_timerelease_versions.total_count int最后更新时间,为UNIX格式的时间戳
    authorobject项目创建者发行版数量
    -- namebranches.list.name string用户名,也是用户标识
    categoryobject项目类别分支名称
    -- idbranches.total_count int项目类型id分支数量
    -- nametags.list.name string项目类型名称标签名称
    languageobject项目语言tags.total_countint标签数量
    -- idcontributors.list.contributions int项目语言id贡献数量
    -- namecontributors.list.login string项目语言名称
    - -
    -

    返回的JSON示例:

    -
    -
    [
    -  {
    -    "id": 20,
    -    "repo_id": 2,
    -    "identifier": "PNAekinmH",
    -    "name": "FNILL",
    -    "visits": 13567,
    -    "author": {
    -      "name": "王一达",
    -      "login": "wangyida",
    -      "image_url": "avatars/User/b"
    -    },
    -    "category": {
    -      "id": 8,
    -      "name": "其他"
    -    }
    -  }
    -]
    -
    - -

    项目导航

    -

    获取项目导航信息

    - -
    -

    示例:

    -
    -
    curl -X GET \
    -http://localhost:3000/api/yystopf/ceshi/menu_list  | jq
    -
    await octokit.request('GET /api/yystopf/ceshi/menu_list')
    -

    HTTP 请求

    -

    GET api/:owner/:repo/menu_list

    -

    请求参数

    - - - - - - - + - - - - + - + - - - + - - -
    参数必选默认类型字段说明贡献者登录名
    ownercontributors.list.name string用户登录名贡献者用户名称
    repocontributors.list.image_url string项目标识identifier
    -

    返回字段说明

    - - - - - + - - - - + + +
    参数类型字段说明贡献者头像
    menu_namestring导航名称, home:主页,code:代码库,issues:易修,pulls:合并请求,devops:工作流,versions:里程碑,activity:动态,setting:仓库设置languagesobject项目语言占比

    返回的JSON示例:

    -
    [
    -    {
    -        "menu_name": "home"
    +
    {
    +    "content": "仓库简介",
    +    "website": "仓库网址",
    +    "readme": {
    +        "type": "file",
    +        "encoding": "base64",
    +        "size": 9,
    +        "name": "README.md",
    +        "path": "README.md",
    +        "content": "# ceshi\n\n",
    +        "sha": ""
         },
    -    {
    -        "menu_name": "code"
    +    "identifier": "ceshi",
    +    "name": "测试项目",
    +    "project_id": 2,
    +    "repo_id": 2,
    +    "issues_count": 0,
    +    "pull_requests_count": 0,
    +    "project_identifier": "ceshi",
    +    "praises_count": 0,
    +    "forked_count": 0,
    +    "watchers_count": 0,
    +    "versions_count": 0,
    +    "version_releases_count": 0,
    +    "version_releasesed_count": 0,
    +    "permission": "Reporter",
    +    "mirror_url": null,
    +    "mirror": false,
    +    "type": 0,
    +    "open_devops": false,
    +    "watched": false,
    +    "praised": false,
    +    "status": 1,
    +    "forked_from_project_id": 1,
    +    "fork_info": {
    +        "fork_form_name": "测试项目",
    +        "fork_project_user_login": "ceshi_org",
    +        "fork_project_identifier": "ceshi",
    +        "fork_project_user_name": "ceshi_org"
         },
    -    {
    -        "menu_name": "pulls"
    +    "size": "25.0 KB",
    +    "ssh_url": "virus@localhost:yystopf/ceshi.git",
    +    "clone_url": "http://localhost:10080/yystopf/ceshi.git",
    +    "default_branch": "master",
    +    "empty": false,
    +    "full_name": "yystopf/ceshi",
    +    "private": false,
    +    "license_name": "gnu-javamail-exception",
    +    "release_versions": {
    +        "list": [
    +            {
    +                "id": 2,
    +                "name": "vvvv",
    +                "tag_name": "v1.1",
    +                "created_at": "2019-07-18 10:16"
    +            }
    +        ],
    +        "total_count": 1
         },
    -    {
    -        "menu_name": "activity"
    -    }
    -]
    -

    项目主页

    -

    获取项目主页信息

    +
    "branches": { + "list": [ + { + "name": "master" + } + ], + "total_count": 1 + }, + "tags": { + "list": [ + { + "name": "v1.1" + }, + { + "name": "v1.0" + } + ], + "total_count": 2 + }, + "contributors": { + "list": [ + { + "contributions": 1, + "gid": 2, + "login": "yystopf", + "type": "User", + "name": "yystopf", + "image_url": "avatars/User/b" + } + ], + "total_count": 1 + }, + "languages": { + "HTML": "50.9%", + "Ruby": "25.6%", + "JavaScript": "21.4%", + "CSS": "1.3%", + "CoffeeScript": "0.7%", + "Shell": "0.1%" + } +} +

    仓库标签列表

    +

    仓库标签列表

    示例:

    -
    curl -X GET \
    -http://localhost:3000/api/jasder/forgeplus/about  | jq
    -
    await octokit.request('GET /api/jasder/forgeplus/about')
    -

    HTTP 请求

    -

    GET api/:owner/:repo/about

    -

    请求参数

    +
    curl -X GET http://localhost:3000/api/yystopf/csfjkkj/tags.json
    +
    await octokit.request('GET /api/yystopf/csfjkkj/tags.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/tags.json

    +

    请求参数:

    @@ -4148,8 +6239,22 @@ http://localhost:3000/api/jasder/forgeplus/about | jq + + + + + + + + + + + + + +
    参数string 项目标识identifier
    page1integer页码
    limit20integer每页个数
    -

    返回字段说明

    +

    返回字段说明:

    @@ -4158,153 +6263,164 @@ http://localhost:3000/api/jasder/forgeplus/about | jq - + + + + + + - + - + - + - - - + + + -
    参数
    identifieridint标签id
    name stringproject's identifier标签名称
    contentzipball_url string主页内容标签zip包下载地址
    attachmentsarray附件tarball_urlstring标签tar包下载地址
    - -
    -

    返回的JSON示例:

    -
    -
    {
    -  "content": "",
    -  "identifier": "forgeplus",
    -  attachments: []
    -}
    -
    - -

    项目模块信息

    -

    项目模块信息

    - -
    -

    示例:

    -
    -
    curl -X GET \
    -http://localhost:3000/api/yystopf/ceshi/project_units.json
    -
    await octokit.request('GET /api/yystopf/ceshi/project_units')
    -

    HTTP 请求

    -

    GET /api/yystopf/ceshi/project_units

    -

    返回字段说明:

    - - - - + + + - - + - + -
    参数类型字段说明taggerobject打标签的人
    typetime_ago string模块名称打标签的时间
    - -
    -

    返回的JSON示例:

    -
    -
    [
    -    {
    -        "type": "code"
    -    },
    -    {
    -        "type": "pulls"
    -    },
    -    {
    -        "type": "issues"
    -    }
    -]
    -

    更改项目模块展示

    -

    更改项目模块展示

    - -
    -

    示例:

    -
    -
    curl -X POST \
    --H  "accept: application/json" \
    --H  "Content-Type: application/json" \
    --d "{ \"unit_typs\": [\"code\", \"pulls\"]}" \
    -http://localhost:3000/api/yystopf/ceshi/project_units.json
    -
    await octokit.request('POST /api/yystopf/ceshi/project_units')
    -

    HTTP 请求

    -

    POST /api/yystopf/ceshi/project_units

    -

    请求参数

    - - - - - - + + + - - - - - - + + + -
    参数必选默认类型字段说明created_at_unixstring打标签的时间戳
    unit_typesarray项目模块内容, 支持以下参数:code:代码库,issues:易修,pulls:合并请求,devops:工作流,versions:里程碑messagestring标签信息
    -

    返回字段说明:

    - - - - + + + - - - - + + + - + - + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明commitobject标签最后一个commit
    statusint返回状态, 0: 表示操作成功commit.shastringcommit的id
    messagecommit.message string返回信息说明commit的提交信息
    commit.time_agostringcommit的提交时间
    commit.created_at_unixstringcommit的提交时间戳
    commit.committerobjectcommit的提交者
    commit.authorobjectcommit的作者

    返回的JSON示例:

    -
    {
    -    "status": 0,
    -    "message": "success"
    -}
    -

    创建项目

    -

    创建项目

    +
    [
    +    {
    +        "name": "v2.0.0",
    +        "id": "c7d0873ee41796d1a0e193063095ccf539a9bf31",
    +        "zipball_url": "http://localhost:3000/api/yystopf/csfjkkj/archive/v2.0.0.zip",
    +        "tarball_url": "http://localhost:3000/api/yystopf/csfjkkj/archive/v2.0.0.tar.gz",
    +        "tagger": {
    +            "id": 4,
    +            "login": "testforge1",
    +            "name": "testforge1",
    +            "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
    +        },
    +        "time_ago": "1天前",
    +        "created_at_unix": 1632376903,
    +        "message": "jdfkls",
    +        "commit": {
    +            "sha": "08fe383f1e5ebe2e2a384a8ea3ee890a758c7cd7",
    +            "message": "add\n",
    +            "time_ago": "1天前",
    +            "created_at_unix": 1632376186,
    +            "committer": {
    +                "id": 4,
    +                "login": "testforge1",
    +                "name": "testforge1",
    +                "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
    +            },
    +            "author": {
    +                "id": 4,
    +                "login": "testforge1",
    +                "name": "testforge1",
    +                "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
    +            }
    +        }
    +    },
    +    {
    +        "name": "v1.0.0",
    +        "id": "12168ad39c3ef201a445a2db181a3e43d50e40dd",
    +        "zipball_url": "http://localhost:3000/api/yystopf/csfjkkj/archive/v1.0.0.zip",
    +        "tarball_url": "http://localhost:3000/api/yystopf/csfjkkj/archive/v1.0.0.tar.gz",
    +        "tagger": {
    +            "id": null,
    +            "login": "viletyy",
    +            "name": "viletyy",
    +            "image_url": "system/lets/letter_avatars/2/V/39_141_222/120.png"
    +        },
    +        "time_ago": "10天前",
    +        "created_at_unix": 1631588042,
    +        "message": "dfks",
    +        "commit": {
    +            "sha": "5291b5e45a377c1f7710cc6647259887ed7aaccf",
    +            "message": "ADD file via upload\n",
    +            "time_ago": "21天前",
    +            "created_at_unix": 1630648417,
    +            "committer": {
    +                "id": null,
    +                "login": "yystopf",
    +                "name": "yystopf",
    +                "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
    +            },
    +            "author": {
    +                "id": null,
    +                "login": "yystopf",
    +                "name": "yystopf",
    +                "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
    +            }
    +        }
    +    }
    +]
    +

    编辑仓库信息

    +

    编辑仓库信息

    示例:

    -
    curl -X POST \
    --d "user_id=36401" \
    --d "name=hnfl_demo" \
    --d "description=my first project" \
    --d "repository_name=hnfl_demo" \
    --d "project_category_id=1" \
    --d "project_language_id=2" \
    --d "ignore_id=2" \
    --d "license_id=1" \
    -http://localhost:3000/api/projects.json
    -
    await octokit.request('GET /api/projects.json')
    -

    HTTP 请求

    -

    POST api/projects

    -

    请求参数

    +
    curl -X GET http://localhost:3000/api/jasder/jasder_test/edit.json
    +
    await octokit.request('GET /api/jasder/jasder_test/edit.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/edit.json

    +

    请求参数:

    @@ -4315,86 +6431,67 @@ http://localhost:3000/api/projects.json - - - - - - - - - - - - - - - + - + - + - + +
    参数
    user_idint用户id或者组织id
    namestring项目名称
    descriptionowner string项目描述用户登录名
    repository_namerepo string仓库名称, 只含有数字、字母、下划线不能以下划线开头和结尾,且唯一项目标识identifier
    +

    返回字段说明:

    + - - - - - + + + + - - - - - + + + - - - + - + - - - - - + + + - - - - - + + + -
    project_category_idint项目类别id参数类型字段说明
    project_language_idint项目语言ididentifierstring仓库标识
    ignore_idproject_id intgitignore相关id项目id
    license_idint开源许可证idproject_namestring项目名称
    privateboolean项目是否私有, true:为私有,false: 公开,默认为公开project_identifierstring项目标识
    -

    返回字段说明

    - - - - + + + - - + - + - - - + + + + + + + +
    参数类型字段说明project_descriptionstring项目简介
    idproject_category_id intid项目类别id
    namestring项目名称project_language_idint项目语言id
    privateboolean项目是否私有, true:为私有,false: 公开
    @@ -4402,27 +6499,32 @@ http://localhost:3000/api/projects.json

    返回的JSON示例:

    {
    -  "id": 3240,
    -  "name": "好项目"
    +  "identifier": "project",
    +  "project_id": 3263,
    +  "project_name": "项目",
    +  "project_identifier": "project identifier",
    +  "project_description": "project description",
    +  "project_category_id": 1,
    +  "project_language_id": 2,
    +  "private": false
     }
    -

    创建镜像项目

    -

    创建镜像项目

    +

    修改仓库信息

    +

    修改仓库信息

    示例:

    -
    curl -X POST \
    --d "user_id=36408" \
    --d "clone_addr=https://gitea.com/mx8090alex/golden.git" \
    --d "name=golden_mirror1" \
    --d "description=golden_mirror" \
    +
    curl -X PATCH \
    +-d "name=hnfl_demo" \
    +-d "description=my first project" \
     -d "project_category_id=1" \
     -d "project_language_id=2" \
    -http://localhost:3000/api/projects/migrate.json
    -
    await octokit.request('GET /api/projects/migrate.json')
    -

    HTTP 请求

    -

    POST api/projects/migrate.json

    -

    请求参数

    +-d "private=true" \ +http://localhost:3000/api/jasder/jasder_test.json +
    await octokit.request('PATCH /api/jasder/jasder_test.json')
    +

    HTTP 请求

    +

    PATCH /api/:owner/:repo

    +

    请求参数:

    @@ -4433,27 +6535,20 @@ http://localhost:3000/api/projects/migrate.json - + - + - + - - - - - - - @@ -4461,56 +6556,35 @@ http://localhost:3000/api/projects/migrate.json - - - - - - - - + - + - - - - - - - - - - - - - - - + - + - +
    参数
    user_idid int用户id或者组织id项目id
    name string 项目名称
    clone_addrstring镜像项目clone地址
    description 项目描述
    repository_namestring仓库名称, 只含有数字、字母、下划线不能以下划线开头和结尾,且唯一
    project_category_id int 项目类别id
    project_language_id int 项目语言id
    is_mirrorboolean是否设置为镜像, true:是, false:否,默认为否
    auth_usernamestring镜像源仓库的登录用户名
    auth_passworddefault_branch string镜像源仓库的登录秘密默认分支名称
    private boolean项目是否私有, true:为私有,false: 非私有,默认为公开项目是否私有, true:为私有,false: 公开,默认为公开
    -

    返回字段说明

    +

    返回字段说明:

    @@ -4524,126 +6598,34 @@ http://localhost:3000/api/projects/migrate.json - - - - -
    参数id
    namestring项目名称
    - -
    -

    返回的JSON示例:

    -
    -
    {
    -  "id": 3241,
    -  "name": "这是一个镜像项目"
    -}
    -

    同步镜像

    -

    手动同步镜像

    - -
    -

    示例:

    -
    -
    curl -X POST http://localhost:3000/api/repositories/1244/sync_mirror.json
    -
    await octokit.request('POST /api/repositories/1244/sync_mirror.json')
    -

    HTTP 请求

    -

    POST api/repositories/:id/sync_mirror.json

    -

    请求参数

    - - - - - - - - - - - - - - - - -
    参数必选默认类型字段说明
    idint仓库id
    -

    返回字段说明

    - - - - - - - - - - - - - - + - - -
    参数类型字段说明
    statusint状态码, 0:标识请求成功
    messageidentifier string服务端返回的信息说明
    - -
    -

    返回的JSON示例:

    -
    -
    {
    -  "status": 0,
    -  "message": "success"
    -}
    -

    Fork项目

    -

    fork项目

    - -
    -

    示例:

    -
    -
    curl -X POST http://localhost:3000/api/jasder/forgeplus/forks.json
    -
    await octokit.request('POST /api/jaser/jasder_test/forks.json')
    -

    HTTP 请求

    -

    POST api/:owner/:repo/forks.json

    -

    请求参数

    - - - - - - - + - - - - + - + - - - + - + -
    参数必选默认类型字段说明项目标识
    ownername string用户登录名项目名称
    repodescription string项目标识identifier项目简介
    -

    返回字段说明

    - - - - + + + - - + - + - - - + + +
    参数类型字段说明project_category_idint项目类别id
    idproject_language_id int项目id项目语言id
    identifierstring项目标识privateboolean
    @@ -4651,21 +6633,25 @@ http://localhost:3000/api/projects/migrate.json

    返回的JSON示例:

    {
    -  "id": 3290,
    -  "identifier": "newadm"
    +  "id": 3263,
    +  "identifier": "project identifier",
    +  "name": "project name",
    +  "description": "project description",
    +  "project_category_id": 1,
    +  "project_language_id": 2,
    +  "is_public": true
     }
    -

    用户管理的组织列表

    -

    用户管理的组织列表

    +

    删除仓库

    +

    删除仓库

    示例:

    -
    curl -X GET \
    -http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizations.json  | jq
    -
    await octokit.request('GET /api/:owner/:repo/applied_transfer_projects/organizations')
    -

    HTTP 请求

    -

    GET api/:owner/:repo/applied_transfer_projects/organizations

    -

    请求参数

    +
    curl -X DELETE http://localhost:3000/api/jasder/jasder_test.json
    +
    await octokit.request('DELETE /api/jasder/jasder_test.json')
    +

    HTTP 请求

    +

    PATCH /api/:owner/:repo

    +

    请求参数:

    @@ -4690,7 +6676,7 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat
    参数项目标识identifier
    -

    返回字段说明

    +

    返回字段说明:

    @@ -4699,24 +6685,14 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat - - - - - - - - + + + - + - - - - - - +
    参数
    namestring组织标识
    nicknamestring组织名称statusint返回状态, 0: 表示操作成功
    descriptionmessage string组织描述
    avatar_urlstring|组织头像返回信息说明
    @@ -4724,42 +6700,26 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat

    返回的JSON示例:

    {
    -    "total_count": 3,
    -    "organizations": [
    -        {
    -            "id": 9,
    -            "name": "ceshi_org",
    -            "nickname": "测试组织",
    -            "description": "测试组织",
    -            "avatar_url": "images/avatars/Organization/9?t=1612706073"
    -        },
    -        {
    -            "id": 51,
    -            "name": "ceshi",
    -            "nickname": "测试组织哈哈哈",
    -            "description": "23212312",
    -            "avatar_url": "images/avatars/Organization/51?t=1618800723"
    -        },
    -        {
    -            "id": 52,
    -            "name": "ceshi1",
    -            "nickname": "身份卡手动阀",
    -            "description": "1231手动阀是的",
    -            "avatar_url": "images/avatars/Organization/52?t=1618805056"
    -        }
    -    ]
    +  "status": 0,
    +  "message": "success"
     }
    -

    迁移项目

    -

    迁移项目,edit接口is_transfering为true表示正在迁移

    + + +

    添加仓库成员

    +

    仓库中添加成员操作

    示例:

    -
    curl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects.json
    -
    await octokit.request('POST /api/:owner/:repo/applied_transfer_projects.json')
    -

    HTTP 请求

    -

    POST /api/:owner/:repo/applied_transfer_projects.json

    -

    请求参数

    +
    curl -X POST \
    +-d "user_id=12" \
    +http://localhost:3000/api/jasder/jasder_test/collaborators.json
    +
    await octokit.request('POST /api/jasder/jasder_test/collaborators.json')
    +

    HTTP 请求

    +

    POST /api/:owner/:repo/collaborators.json

    +

    请求参数:

    @@ -4784,14 +6744,14 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat - + + - - +
    参数项目标识identifier
    owner_nameuser_id int string迁移对象标识用户id
    -

    返回字段说明

    +

    返回字段说明:

    @@ -4800,119 +6760,89 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + +
    参数
    idint项目id
    statusstring项目迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝
    time_agostring项目迁移创建的时间
    project.id int迁移项目的id
    project.identifierstring迁移项目的标识
    project.namestring迁移项目的名称
    project.descriptionstring迁移项目的描述
    project.is_publicbool迁移项目是否公开
    project.owner.idbool迁移项目拥有者id返回状态, 0: 表示操作成功
    project.owner.typemessage string迁移项目拥有者类型返回信息说明
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +  "status": 0,
    +  "message": "success"
    +}
    +
    + +

    删除仓库成员

    +

    仓库中删除成员操作

    + +
    +

    示例:

    +
    +
    curl -X DELETE \
    +-d "user_id=12" \
    +http://localhost:3000/api/jasder/jasder_test/collaborators.json
    +
    await octokit.request('DELETE /api/jasder/jasder_test/collaborators.json')
    +

    HTTP 请求

    +

    DELETE /api/:owner/:repo/collaborators.json

    +

    请求参数:

    + - - - + + + + + + - + + + - + - + + + - + - + + - - - - - - - - - - - - - - - - + + +
    project.owner.namestring迁移项目拥有者昵称参数必选默认类型字段说明
    project.owner.loginowner string迁移项目拥有者标识用户登录名
    project.owner.image_urlrepo string迁移项目拥有者头像项目标识identifier
    user.iduser_id int迁移创建者的id
    user.typestring迁移创建者的类型
    user.namestring迁移创建者的名称
    user.loginstring迁移创建者的标识用户id
    +

    返回字段说明:

    + - - - + + + + - + - - - - - - - - - - - - - - - - + - + - +
    user.image_urlstring迁移创建者头像参数类型字段说明
    owner.idstatus int迁移接受者的id
    owner.typestring迁移接受者的类型
    owner.namestring迁移接受者的名称
    owner.loginstring迁移接受者的标识返回状态, 0: 表示操作成功
    owner.image_urlmessage string迁移接受者头像返回信息说明
    @@ -4920,50 +6850,27 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat

    返回的JSON示例:

    {
    -    "project": {
    -        "id": 86,
    -        "identifier": "ceshi_repo1",
    -        "name": "测试项目啊1",
    -        "description": "二十多",
    -        "is_public": true,
    -        "owner": {
    -            "id": 52,
    -            "type": "Organization",
    -            "name": "身份卡手动阀",
    -            "login": "ceshi1",
    -            "image_url": "images/avatars/Organization/52?t=1618805056"
    -        }
    -    },
    -    "user": {
    -        "id": 6,
    -        "type": "User",
    -        "name": "yystopf",
    -        "login": "yystopf",
    -        "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
    -    },
    -    "owner": {
    -        "id": 9,
    -        "type": "Organization",
    -        "name": "测试组织",
    -        "login": "ceshi_org",
    -        "image_url": "images/avatars/Organization/9?t=1612706073"
    -    },
    -    "id": 4,
    -    "status": "common",
    -    "created_at": "2021-04-26 09:54",
    -    "time_ago": "1分钟前"
    +  "status": 0,
    +  "message": "success"
     }
    -

    取消迁移项目

    -

    迁移项目,edit接口is_transfering为true表示正在迁移

    + + +

    更改仓库成员角色(权限)

    +

    更改仓库成员角色

    示例:

    -
    curl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/cancel.json
    -
    await octokit.request('POST /api/:owner/:repo/applied_transfer_projects/cancel.json')
    -

    HTTP 请求

    -

    POST /api/:owner/:repo/applied_transfer_projects/cancel.json

    -

    请求参数

    +
    curl -X PUT \
    +-d "user_id=12" \
    +-d "role=Developer" \
    +http://localhost:3000/api/jasder/jasder_test/change_role.json
    +
    await octokit.request('PUT /api/jasder/jasder_test/change_role.json')
    +

    HTTP 请求

    +

    PUT /api/:owner/:repo/change_role.json

    +

    请求参数:

    @@ -4987,8 +6894,22 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat + + + + + + + + + + + + + +
    参数string 项目标识identifier
    user_idint用户id
    rolestring取值范围:"Manager", "Developer", "Reporter";分别为项目管理人员(拥有所有操作权限)、项目开发人员(只拥有读写权限)、项目报告人员(只拥有读权限)
    -

    返回字段说明

    +

    返回字段说明:

    @@ -4997,119 +6918,127 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat - - - - - - - - - - - - - - - - + - + - + +
    参数
    idint迁移id
    statusstring迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝
    time_agostring迁移创建的时间
    project.id int迁移项目的id返回状态, 0: 表示操作成功
    project.identifiermessage string迁移项目的标识返回信息说明
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +  "status": 0,
    +  "message": "success"
    +}
    +
    + +

    获取仓库成员列表

    +

    获取仓库成员列表

    + +
    +

    示例:

    +
    +
    curl -X GET \
    +-d "page=1" \
    +-d "limit=5" \
    +http://localhost:3000/api/jasder/jasder_test/collaborators.json
    +
    await octokit.request('GET /api/jasder/jasder_test/collaborators.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/collaborators.json

    +

    请求参数:

    + - - - + + + + + + - + + + - - - - - - - - - - - + - + + + - + - + + + - + - + + + - + +
    project.namestring迁移项目的名称参数必选默认类型字段说明
    project.descriptionowner string迁移项目的描述
    project.is_publicbool迁移项目是否公开
    project.owner.idbool迁移项目拥有者id用户登录名
    project.owner.typerepo string迁移项目拥有者类型项目标识identifier
    project.owner.namepage string迁移项目拥有者昵称页数,第几页
    project.owner.loginlimit string迁移项目拥有者标识每页多少条数据,默认15条
    +

    返回字段说明:

    + - - - + + + + - + - - - - - - + - - - + + + - - - + + + - + - - - - - - + - + - + - + - + - - - + + + - + - +
    project.owner.image_urlstring迁移项目拥有者头像参数类型字段说明
    user.idtotal_count int迁移创建者的id
    user.typestring迁移创建者的类型返回记录总条数
    user.namestring迁移创建者的名称membersarray项目成员信息
    user.loginstring迁移创建者的标识-- idint用户id
    user.image_url-- name string迁移创建者头像
    owner.idint迁移接受者的id用户名称
    owner.type-- login string迁移接受者的类型用户登录名/标识
    owner.name-- image_url string迁移接受者的名称用户头像
    owner.loginstring迁移接受者的标识-- is_ownerboolean是否是项目的拥有者,true:是, false:不是
    owner.image_url-- role string迁移接受者头像该用户在项目中的角色, Manager: 管理员(拥有操作权限); Developer:开发人员(只拥有读写权限); Reporter:报告人员(只拥有读权限)
    @@ -5117,50 +7046,43 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat

    返回的JSON示例:

    {
    -    "project": {
    -        "id": 86,
    -        "identifier": "ceshi_repo1",
    -        "name": "测试项目啊1",
    -        "description": "二十多",
    -        "is_public": true,
    -        "owner": {
    -            "id": 52,
    -            "type": "Organization",
    -            "name": "身份卡手动阀",
    -            "login": "ceshi1",
    -            "image_url": "images/avatars/Organization/52?t=1618805056"
    -        }
    -    },
    -    "user": {
    -        "id": 6,
    -        "type": "User",
    -        "name": "yystopf",
    -        "login": "yystopf",
    -        "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
    -    },
    -    "owner": {
    -        "id": 9,
    -        "type": "Organization",
    -        "name": "测试组织",
    -        "login": "ceshi_org",
    -        "image_url": "images/avatars/Organization/9?t=1612706073"
    +  "total_count": 2,
    +  "members": [
    +    {
    +      "id": 36401,
    +      "name": "name",
    +      "login": "login",
    +      "image_url": "avatars/User/b",
    +      "is_owner": true,
    +      "role": "Manager"
         },
    -    "id": 4,
    -    "status": "common",
    -    "created_at": "2021-04-26 09:54",
    -    "time_ago": "1分钟前"
    +    {
    +      "id": 36399,
    +      "name": "name",
    +      "login": "login",
    +      "image_url": "avatars/User/b",
    +      "is_owner": false,
    +      "role": "Developer"
    +    }
    +  ]
     }
    -

    Repositories

    仓库详情

    -

    仓库详情

    + + +

    获取仓库所有文件

    +

    获取仓库所有文件

    示例:

    -
    curl -X GET http://localhost:3000/api/jasder/jasder_test.json
    -
    await octokit.request('GET /api/jasder/jasder_test.json')
    -

    HTTP 请求

    -

    GET /api/:owner/:repo

    -

    请求参数:

    +
    curl -X GET \
    +-d "ref=develop" \
    +http://localhost:3000/api/yystopf/ceshi/files.json
    +
    await octokit.request('GET /api/yystopf/ceshi/files.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/files

    +

    请求参数:

    @@ -5184,8 +7106,22 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat + + + + + + + + + + + + + +
    参数string 项目标识identifier
    searchstring文件搜索关键词
    refstring分支名,默认为仓库默认分支
    -

    返回字段说明:

    +

    返回字段说明:

    @@ -5194,64 +7130,82 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat - - - - - - + - + - + - - - + + + - + - + - + - + - + - +
    参数
    idintid
    name string项目名称文件名称
    identifierpath string项目标识文件路径
    is_publicboolean项目是否公开, true:公开,false:私有shastring文件标识
    descriptiontype string项目简介文件类型
    repo_idsize int仓库id文件大小
    repo_identifierurl string仓库标识文件地址

    返回的JSON示例:

    -
    {
    -  "name": "ni项目",
    -  "identifier": "mirror_demo",
    -  "is_public": true,
    -  "description": "my first project mirror_demo",
    -  "repo_id": 75073,
    -  "repo_identifier": "mirror_demo"
    -}
    -

    仓库详情(简版)

    -

    仓库详情

    +
    [
    +    {
    +        "name": ".gitignore",
    +        "path": ".gitignore",
    +        "sha": "f83922d01ae60f6e637a1a2b9f08871b4f87dfc8",
    +        "type": "file",
    +        "size": 63,
    +        "url": "http://localhost:10080/api/v1/repos/yystopf/ceshi/contents/.gitignore?ref=master",
    +        "html_url": "http://localhost:10080/yystopf/ceshi/src/branch/master/.gitignore"
    +    },
    +    {
    +        "name": "LICENSE",
    +        "path": "LICENSE",
    +        "sha": "8f3b9ab0d08afd3a624d822e3971a2f42b3bc2b9",
    +        "type": "file",
    +        "size": 341,
    +        "url": "http://localhost:10080/api/v1/repos/yystopf/ceshi/contents/LICENSE?ref=master",
    +        "html_url": "http://localhost:10080/yystopf/ceshi/src/branch/master/LICENSE"
    +    },
    +    {
    +        "name": "README.md",
    +        "path": "README.md",
    +        "sha": "1bc8a60ac6ddc876ebc4b60fc68991435bfad93e",
    +        "type": "file",
    +        "size": 9,
    +        "url": "http://localhost:10080/api/v1/repos/yystopf/ceshi/contents/README.md?ref=master",
    +        "html_url": "http://localhost:10080/yystopf/ceshi/src/branch/master/README.md"
    +    }
    +]
    +

    获取仓库代码目录

    +

    获取仓库代码目录

    示例:

    -
    curl -X GET http://localhost:3000/api/jasder/jasder_test/simple.json
    -
    await octokit.request('GET /api/jasder/jasder_test/simple.json')
    -

    HTTP 请求

    -

    GET /api/:owner/:repo/simple

    -

    请求参数:

    +
    curl -X GET \
    +-d "ref=develop" \
    +http://localhost:3000//api/jasder/jasder_test/entries.json
    +
    await octokit.request('GET /api/jasder/jasder_test/entries.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/entries.json

    +

    请求参数:

    @@ -5275,8 +7229,15 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat + + + + + + +
    参数string 项目标识identifier
    refstring分支名称、tag名称或是提交记录id,默认为master分支
    -

    返回字段说明:

    +

    返回字段说明:

    @@ -5285,6 +7246,16 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat + + + + + + + + + + @@ -5292,32 +7263,32 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat - + - + - - - - - - + - + - + - + - + - + - + + + + + +
    参数
    last_commitobject
    -- commitobject
    id int id
    name string项目名称文件夹或文件名称
    identifierpath string项目标识
    is_publicboolean项目是否公开, true:公开,false:私有文件夹或文件相对路径
    descriptiontype string项目简介文件类型, file:文件,dir:文件目录
    repo_idsize int仓库id文件夹或文件大小 单位B
    repo_identifiercontent string仓库标识文件内容
    targetstring标签
    @@ -5325,24 +7296,64 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat

    返回的JSON示例:

    {
    -  "name": "ni项目",
    -  "identifier": "mirror_demo",
    -  "is_public": true,
    -  "description": "my first project mirror_demo",
    -  "repo_id": 75073,
    -  "repo_identifier": "mirror_demo"
    +  "last_commit": {
    +    "commit": {
    +      "sha": "3f2de4f78d2d7050486535082cd11cdfc9f3679e",
    +      "url": "http://localhost:3003//api/repositories/api-cloud-platform/commits/3f2de4f78d2d7050486535082cd11cdfc9f3679e",
    +      "message": "update README.md.",
    +      "author": {
    +        "name": "Gitee",
    +        "email": "noreply@gitee.com",
    +        "date": "2020-03-02T20:23:18+08:00"
    +      },
    +      "committer": {
    +        "name": "Gitee",
    +        "email": "noreply@gitee.com",
    +        "date": "2020-03-02T20:23:18+08:00"
    +      },
    +      "timestamp": 1583151798,
    +      "time_from_now": "3个月前"
    +    },
    +    "author": null,
    +    "committer": null
    +  },
    +  "entries": [
    +    {
    +      "name": "ace-gate",
    +      "path": "ace-gate",
    +      "sha": "c83f85fc63b14edcd6fc502eee9996f5a9993eca",
    +      "type": "dir",
    +      "size": 0,
    +      "content": null,
    +      "target": null,
    +      "commit": {
    +        "message": "v2.9 升级alibaba组件release版本\n",
    +        "sha": "6117eaab86f71115f42f2a46ff1683015cda798d",
    +        "created_at": "1970-01-01 08:00",
    +        "time_from_now": "1年前",
    +        "created_at_unix": null
    +      }
    +    }
    +  ]
     }
    -

    仓库详情(新版)

    -

    仓库详情

    + + +

    获取仓库代码子目录或者文件

    +

    获取仓库代码子目录或者文件

    示例:

    -
    curl -X GET http://localhost:3000/api/yystopf/ceshi/detail.json
    -
    await octokit.request('GET /api/yystopf/ceshi/detail.json')
    -

    HTTP 请求

    -

    GET /api/:owner/:repo/detail

    -

    请求参数:

    +
    curl -X GET \
    +-d "ref=master" \
    +-d "filepath=file" \
    +http://localhost:3000//api/jasder/jasder_test/sub_entries.json
    +
    await octokit.request('GET /api/jasder/jasder_test/sub_entries.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/sub_entries.json

    +

    请求参数:

    @@ -5354,251 +7365,321 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat - - - - - - - - - - - - -
    参数
    ownerstring用户登录名
    repostring项目标识identifier
    -

    返回字段说明:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - + + + + + - - - + + + + + - - - + + + + + +
    参数类型字段说明
    contentstring仓库简介
    websitestring仓库网址
    readmestringreadme文件|
    identifierstring项目标识
    name string项目名称用户登录名
    issues_countint项目issue数量repostring项目标识identifier
    pull_requests_countint项目合并请求数量filepathstring文件夹、文件的相对路径
    project_identifierint项目标识refstring分支名称、tag名称或是提交记录id,默认为master分支
    +

    返回字段说明:

    + - - - + + + + - + - + - - - + + + - - - + + + - - - + + + - + - + - + - + - + - + - - - + + + - - - + + + - - - + + + - - - + + + +
    praises_countint项目点赞数量参数类型字段说明
    forked_countid int项目复刻数量id
    watchers_countint项目关注数量namestring文件夹或文件名称
    versions_countint项目里程碑数量pathstring文件夹或文件相对路径
    version_releases_countint项目发行版数量typestring文件类型, file:文件,dir:文件目录
    version_releasesed_countsize int项目发行版已发行数量文件夹或文件大小 单位KB
    permissioncontent string项目权限文件内容,
    mirror_urltarget string镜像地址标签
    mirrorbool是否为镜像项目urlstring文件访问链接,带分支
    typeint项目类型 0 普通项目 1 普通镜像项目 2 同步镜像项目html_urlstring文件访问链接,未标识分支
    open_devopsint是否开启devopsgit_urlstring文件夹或文件的git仓库访问链接
    watchedbool是否关注download_urlstring文件下载、文件内容访问链接
    + +
    +

    返回的JSON示例:

    +
    +
    [
    +  {
    +    "name": "build.rc",
    +    "path": "lib/build.rc",
    +    "type": "",
    +    "size": 1268,
    +    "content": null,
    +    "target": null,
    +    "url": "http://localhost:3003/api/v1/repos/18816895620/mirror_demo/contents/lib/build.rc?ref=master",
    +    "html_url": "http://localhost:3003/18816895620/mirror_demo/src/branch/master/lib/build.rc",
    +    "git_url": "http://localhost:3003/api/v1/repos/18816895620/mirror_demo/git/blobs/191fcf1a63b3777e2977fcede7dd5309efdd70fe",
    +    "download_url": null
    +  }
    +]
    +
    + +

    获取仓库README文件

    +

    获取仓库README文件

    + +
    +

    示例:

    +
    +
    curl -X GET \
    +-d "ref=master" \
    +-d "filepath=lib" \
    +http://localhost:3000/api/yystopf/csfjkkj/readme.json
    +
    await octokit.request('GET /api/yystopf/csfjkkj/readme.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/readme.json

    +

    请求参数:

    + - - - + + + + + + - - - + + + + + - - - + + + + + - - - + + + + + - + + + - + +
    praisedbool是否点赞参数必选默认类型字段说明
    statusint项目状态ownerstring用户登录名
    forked_from_project_idintfork项目idrepostring项目标识identifier
    fork_infoobjectfork项目信息refstring分支名称、tag名称或是提交记录id,默认为默认分支
    sizefilepath string仓库大小子目录名称,默认为空
    +

    返回字段说明:

    + - - - + + + + - + - + - + - + - - - + + + - + - + - - - + + + - + - + - + - + +
    ssh_urlstring项目ssh地址参数类型字段说明
    clone_urltype string项目克隆地址文件类型, file:文件,dir:文件目录
    default_branchencoding string仓库默认分支编码
    emptybool仓库是否为空sizeint文件夹或文件大小 单位B
    full_namename string仓库全称文件夹或文件名称
    privatebool仓库是否为私有项目pathstring文件夹或文件相对路径
    license_namecontent string许可证名称文件内容
    release_versions.list.namesha string项目issue数量文件commitid
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "type": "file",
    +    "encoding": "base64",
    +    "size": 24,
    +    "name": "README.md",
    +    "path": "lib/README.md",
    +    "content": "ZGZhc2RhZGpmIGRrZnNsCgpzZGZkZnMK",
    +    "sha": "860962cd21c60b1a9e07d723080c87c32c18d44a"
    +}
    +
    + +

    获取仓库贡献者

    +

    获取仓库贡献者

    + +
    +

    示例:

    +
    +
    curl -X GET \
    +-d "ref=master" \
    +-d "filepath=lib" \
    +http://localhost:3000/api/yystopf/csfjkkj/contributors.json
    +
    await octokit.request('GET /api/yystopf/csfjkkj/contributors.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/contributors.json

    +

    请求参数:

    + - - - + + + + + + - + + + - + - - - + + + + + - + + + - + - - - + + + + + +
    release_versions.list.tag_namestring发行版标签名称参数必选默认类型字段说明
    release_versions.list.created_atowner string发行版创建时间用户登录名
    release_versions.total_countint发行版数量repostring项目标识identifier
    branches.list.nameref string分支名称分支名称、tag名称或是提交记录id,默认为整个仓库
    branches.total_countint分支数量filepathstring子目录名称,默认为空
    +

    返回字段说明:

    + - - - + + + + - - - + + + - - + + - + - + - + - + - + - + - - - + + +
    tags.list.namestring标签名称参数类型字段说明
    tags.total_countint标签数量total_countinteger贡献者数量
    contributors.list.contributionsintcontributionsinteger 贡献数量
    contributors.list.loginlogin string贡献者登录名用户登录名
    contributors.list.nametype string贡献者用户名称用户类型
    contributors.list.image_urlname string贡献者头像用户昵称
    languagesobject项目语言占比image_urlstring用户头像
    @@ -5606,116 +7687,40 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat

    返回的JSON示例:

    {
    -    "content": "仓库简介",
    -    "website": "仓库网址",
    -    "readme": {
    -        "type": "file",
    -        "encoding": "base64",
    -        "size": 9,
    -        "name": "README.md",
    -        "path": "README.md",
    -        "content": "# ceshi\n\n",
    -        "sha": ""
    -    },
    -    "identifier": "ceshi",
    -    "name": "测试项目",
    -    "project_id": 2,
    -    "repo_id": 2,
    -    "issues_count": 0,
    -    "pull_requests_count": 0,
    -    "project_identifier": "ceshi",
    -    "praises_count": 0,
    -    "forked_count": 0,
    -    "watchers_count": 0,
    -    "versions_count": 0,
    -    "version_releases_count": 0,
    -    "version_releasesed_count": 0,
    -    "permission": "Reporter",
    -    "mirror_url": null,
    -    "mirror": false,
    -    "type": 0,
    -    "open_devops": false,
    -    "watched": false,
    -    "praised": false,
    -    "status": 1,
    -    "forked_from_project_id": 1,
    -    "fork_info": {
    -        "fork_form_name": "测试项目",
    -        "fork_project_user_login": "ceshi_org",
    -        "fork_project_identifier": "ceshi",
    -        "fork_project_user_name": "ceshi_org"
    -    },
    -    "size": "25.0 KB",
    -    "ssh_url": "virus@localhost:yystopf/ceshi.git",
    -    "clone_url": "http://localhost:10080/yystopf/ceshi.git",
    -    "default_branch": "master",
    -    "empty": false,
    -    "full_name": "yystopf/ceshi",
    -    "private": false,
    -    "license_name": "gnu-javamail-exception",
    -    "release_versions": {
    -        "list": [
    -            {
    -                "id": 2,
    -                "name": "vvvv",
    -                "tag_name": "v1.1",
    -                "created_at": "2019-07-18 10:16"
    -            }
    -        ],
    -        "total_count": 1
    -    },
    -    "branches": {
    -        "list": [
    -            {
    -                "name": "master"
    -            }
    -        ],
    -        "total_count": 1
    -    },
    -    "tags": {
    -        "list": [
    -            {
    -                "name": "v1.1"
    -            },
    -            {
    -                "name": "v1.0"
    -            }
    -        ],
    -        "total_count": 2
    -    },
    -    "contributors": {
    -        "list": [
    -            {
    -                "contributions": 1,
    -                "gid": 2,
    -                "login": "yystopf",
    -                "type": "User",
    -                "name": "yystopf",
    -                "image_url": "avatars/User/b"
    -            }
    -        ],
    -        "total_count": 1
    -    },
    -    "languages": {
    -        "HTML": "50.9%",
    -        "Ruby": "25.6%",
    -        "JavaScript": "21.4%",
    -        "CSS": "1.3%",
    -        "CoffeeScript": "0.7%",
    -        "Shell": "0.1%"
    -    }
    +    "contributors": [
    +        {
    +            "contributions": 5,
    +            "login": "testforge2",
    +            "type": "User",
    +            "name": "testforge2",
    +            "image_url": "system/lets/letter_avatars/2/T/236_177_85/120.png"
    +        },
    +        {
    +            "contributions": 79,
    +            "login": "yystopf",
    +            "type": "User",
    +            "name": "yystopf",
    +            "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
    +        }
    +    ],
    +    "total_count": 2
     }
    -

    编辑仓库信息

    -

    编辑仓库信息

    + + +

    获取仓库webhooks列表

    +

    获取仓库webhooks列表

    示例:

    -
    curl -X GET http://localhost:3000/api/jasder/jasder_test/edit.json
    -
    await octokit.request('GET /api/jasder/jasder_test/edit.json')
    -

    HTTP 请求

    -

    GET /api/:owner/:repo/edit.json

    -

    请求参数:

    +
    curl -X GET \
    +http://localhost:3000/api/yystopf/ceshi/webhooks.json
    +
    await octokit.request('GET /api/yystopf/ceshi/webhooks.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/webhooks.json

    +

    请求参数:

    @@ -5740,7 +7745,7 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat
    参数项目标识identifier
    -

    返回字段说明:

    +

    返回字段说明:

    @@ -5749,44 +7754,39 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat - - - - - - + - + - + - + - + - + - - - + + + - - - + + + - - - + + + - - - + + +
    参数
    identifierstring仓库标识
    project_idid int项目idid
    project_nameurl string项目名称地址
    project_identifierhttp_method string项目标识请求方式
    project_descriptionstring项目简介is_activebool是否激活
    project_category_idint项目类别idtypestring类型
    project_language_idint项目语言idlast_statusstring最后一次推送的状态
    privateboolean项目是否私有, true:为私有,false: 公开create_timestring创建时间
    @@ -5794,32 +7794,62 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat

    返回的JSON示例:

    {
    -  "identifier": "project",
    -  "project_id": 3263,
    -  "project_name": "项目",
    -  "project_identifier": "project identifier",
    -  "project_description": "project description",
    -  "project_category_id": 1,
    -  "project_language_id": 2,
    -  "private": false
    +    "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

    示例:

    -
    curl -X PATCH \
    --d "name=hnfl_demo" \
    --d "description=my first project" \
    --d "project_category_id=1" \
    --d "project_language_id=2" \
    --d "private=true" \
    -http://localhost:3000/api/jasder/jasder_test.json
    -
    await octokit.request('PATCH /api/jasder/jasder_test.json')
    -

    HTTP 请求

    -

    PATCH /api/:owner/:repo

    -

    请求参数:

    +
    curl -X GET \
    +http://localhost:3000/api/yystopf/ceshi/webhooks/3/edit.json
    +
    await octokit.request('GET /api/yystopf/ceshi/webhooks/3/edit.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/webhooks/:id/edit.json

    +

    请求参数:

    @@ -5830,56 +7860,28 @@ http://localhost:3000/api/jasder/jasder_test.json - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - + - - + + - - + +
    参数
    idowner int项目id
    namestring项目名称
    description string项目描述
    project_category_idint项目类别id
    project_language_idint项目语言id用户登录名
    default_branchrepo string默认分支名称项目标识identifier
    privateid boolean项目是否私有, true:为私有,false: 公开,默认为公开integerwebhook ID
    -

    返回字段说明:

    +

    返回字段说明:

    @@ -5893,101 +7895,142 @@ http://localhost:3000/api/jasder/jasder_test.json - + - + - + - + - + - + - - - + + + - - - + + + - - - + + + -
    参数id
    identifierurl string项目标识地址
    namecontent_type string项目名称POST Content Type
    descriptionhttp_method string项目简介请求方式
    project_category_idint项目类别idsecretstring
    project_language_idint项目语言idis_activebool是否激活
    privatebooleantypestring类型
    - -
    -

    返回的JSON示例:

    -
    -
    {
    -  "id": 3263,
    -  "identifier": "project identifier",
    -  "name": "project name",
    -  "description": "project description",
    -  "project_category_id": 1,
    -  "project_language_id": 2,
    -  "is_public": true
    -}
    -

    删除仓库

    -

    删除仓库

    - -
    -

    示例:

    -
    -
    curl -X DELETE http://localhost:3000/api/jasder/jasder_test.json
    -
    await octokit.request('DELETE /api/jasder/jasder_test.json')
    -

    HTTP 请求

    -

    PATCH /api/:owner/:repo

    -

    请求参数:

    - - - - - - + + + - - - - + - + - - - + - + + + + + +
    参数必选默认类型字段说明last_statusstring最后一次推送的状态, waiting 等待,fail 失败,succeed 成功
    ownerbranch_filter string用户登录名分支过滤
    repoevents string项目标识identifier触发条件
    create_timestring创建时间
    -

    返回字段说明:

    + - - + - - - + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明含义
    statusint返回状态, 0: 表示操作成功create创建分支或标签
    messagestring返回信息说明delete分支或标签删除
    fork仓库被fork
    pushgit仓库推送
    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版本发布
    @@ -5995,26 +8038,53 @@ http://localhost:3000/api/jasder/jasder_test.json

    返回的JSON示例:

    {
    -  "status": 0,
    -  "message": "success"
    +    "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"
    +    ]
     }
     
    -