| @@ -205,9 +205,10 @@ class AccountsController < ApplicationController | |||
| return | |||
| end | |||
| LimitForbidControl::UserLogin.new(@user).clear | |||
| successful_authentication(@user) | |||
| sync_pwd_to_gitea!(@user, {password: params[:password].to_s}) # TODO用户密码未同步 | |||
| # session[:user_id] = @user.id | |||
| end | |||
| @@ -0,0 +1,46 @@ | |||
| class Api::V1::ProjectTopicsController < Api::V1::BaseController | |||
| def index | |||
| @project_topics = ProjectTopic | |||
| @project_topics = @project_topics.ransack(name_cont: params[:keyword]) if params[:keyword].present? | |||
| @project_topics = @project_topics.includes(:projects) | |||
| @project_topics = kaminary_select_paginate(@project_topics) | |||
| end | |||
| def create | |||
| ActiveRecord::Base.transaction do | |||
| @project = Project.find_by_id(create_params[:project_id]) | |||
| return render_not_found unless @project.present? | |||
| return render_error("请输入项目搜索标签名称.") unless create_params[:name].present? | |||
| @project_topic = ProjectTopic.find_or_create_by!(name: create_params[:name].downcase) | |||
| @project_topic_ralate = @project_topic.project_topic_ralates.find_or_create_by!(project_id: create_params[:project_id]) | |||
| if @project_topic.present? && @project_topic_ralate.present? | |||
| render_ok | |||
| else | |||
| render_error("项目关联搜索标签失败.") | |||
| end | |||
| end | |||
| end | |||
| def destroy | |||
| ActiveRecord::Base.transaction do | |||
| @project = Project.find_by_id(create_params[:project_id]) | |||
| return render_not_found unless @project.present? | |||
| @project_topic = ProjectTopic.find_by_id(params[:id]) | |||
| @project_topic_ralate = @project_topic.project_topic_ralates.find_by(project_id: @project.id) | |||
| if @project_topic_ralate.destroy! | |||
| render_ok | |||
| else | |||
| render_error("项目取消关联搜索标签失败.") | |||
| end | |||
| end | |||
| end | |||
| private | |||
| def create_params | |||
| params.permit(:project_id, :name) | |||
| end | |||
| end | |||
| @@ -1,7 +1,7 @@ | |||
| module Api::ProjectHelper | |||
| extend ActiveSupport::Concern | |||
| def load_project | |||
| def load_project | |||
| namespace = params[:owner] | |||
| repo = params[:repo] | |||
| @@ -14,7 +14,7 @@ module Api::ProjectHelper | |||
| else | |||
| logger.info "###########:project not found" | |||
| @project = nil | |||
| render_not_found and return | |||
| tip_exception(404, '您访问的页面不存在或已被删除') | |||
| end | |||
| @project | |||
| end | |||
| @@ -8,15 +8,20 @@ class InstallationsController < ApplicationController | |||
| end | |||
| def index | |||
| @install_bots = BotInstall.where(:installer_id => current_user.id) | |||
| @install_bots = BotInstall.where(bot_id: get_bot_id).group(:installer_id) | |||
| end | |||
| def show | |||
| @install_bot = BotInstall.find params[:id] | |||
| @install_bot = BotInstall.find_by(bot_id: get_bot_id, installer_id: params[:id]) || BotInstall.find_by(id: params[:id]) | |||
| tip_exception "参数installer_id错误" if @install_bot.blank? | |||
| end | |||
| def repositories | |||
| @install_bots = BotInstall.where(:installer_id => current_user.id) | |||
| # 与github差异,所以取安装用户和bot对应所有的仓库 | |||
| # 必须使用access_tokens获取到bot的token才能查询 | |||
| tip_exception "无效Token" if current_user.platform != "bot" | |||
| bot = Bot.find_by(uid: current_user.id) | |||
| @install_bots = BotInstall.where(bot_id: bot.id).where(installer_id: params[:id]) | |||
| end | |||
| def update_secret | |||
| @@ -57,11 +62,13 @@ class InstallationsController < ApplicationController | |||
| @install_bot.update_attributes!(state: 0) | |||
| render_ok | |||
| end | |||
| def unsuspended | |||
| @install_bot = BotInstall.find params[:id] | |||
| @install_bot.update_attributes!(state: 1) | |||
| render_ok | |||
| end | |||
| def auth_active | |||
| begin | |||
| @bot = Bot.find params[:id] | |||
| @@ -86,7 +93,8 @@ class InstallationsController < ApplicationController | |||
| end | |||
| def access_tokens | |||
| @install_bot = BotInstall.find params[:id] | |||
| @install_bot = BotInstall.find_by(bot_id: get_bot_id, installer_id: params[:id]) || BotInstall.find_by(id: params[:id]) | |||
| tip_exception "参数installer_id错误" if @install_bot.blank? | |||
| @bot = @install_bot.bot | |||
| @application = Doorkeeper::Application.find_by(uid: @bot.client_id, secret: @bot.client_secret) | |||
| tip_exception("该Bot未激活") if @application.blank? | |||
| @@ -101,5 +109,19 @@ class InstallationsController < ApplicationController | |||
| render_ok(token: @access_token.token) | |||
| end | |||
| private | |||
| def get_bot_id | |||
| header = request.authorization | |||
| pattern = /^Bearer /i | |||
| token = header.gsub(pattern, "") | |||
| decoded_token = JWT.decode token, nil, false | |||
| # 前面已验证token有效期和正确性 | |||
| decoded_token[0]["iss"] | |||
| rescue JWT::DecodeError | |||
| Rails.logger.error "jwt token decode error:#{token}" | |||
| tip_exception("无效Token") | |||
| end | |||
| end | |||
| @@ -34,7 +34,7 @@ class ProjectsController < ApplicationController | |||
| def index | |||
| scope = current_user.logged? ? Projects::ListQuery.call(params, current_user.id) : Projects::ListQuery.call(params) | |||
| @projects = kaminari_paginate(scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units)) | |||
| @projects = kaminari_paginate(scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units, :project_topics)) | |||
| # @projects = paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units) | |||
| category_id = params[:category_id] | |||
| @@ -130,6 +130,13 @@ class ProjectsController < ApplicationController | |||
| # TODO: | |||
| # 临时特殊处理修改website、lesson_url操作方法 | |||
| if project_params.has_key?("website") | |||
| if params[:project_topic_names].present? && params[:project_topic_names].is_a?(Array) | |||
| ProjectTopicRalate.where(project: @project).destroy_all | |||
| params[:project_topic_names].each do |name| | |||
| project_topic = ProjectTopic.find_or_create_by!(name: name.downcase) | |||
| project_topic.project_topic_ralates.find_or_create_by!(project: @project) | |||
| end | |||
| end | |||
| @project.update(project_params) | |||
| elsif project_params.has_key?("default_branch") | |||
| @project.update(project_params) | |||
| @@ -241,9 +241,9 @@ class RepositoriesController < ApplicationController | |||
| def readme | |||
| if params[:filepath].present? | |||
| result = Gitea::Repository::Readme::DirService.call(@owner.login, @repository.identifier, params[:filepath], params[:ref], current_user&.gitea_token) | |||
| result = Gitea::Repository::Readme::DirService.call(@owner.login, @repository.identifier, params[:filepath], params[:ref], @owner&.gitea_token) | |||
| else | |||
| result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token) | |||
| result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], @owner&.gitea_token) | |||
| end | |||
| @path = GiteaService.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/" | |||
| @readme = result[:status] === :success ? result[:body] : nil | |||
| @@ -4,7 +4,7 @@ class Projects::CreateForm < BaseForm | |||
| :blockchain, :blockchain_token_all, :blockchain_init_token | |||
| validates :user_id, :name, :repository_name, presence: true | |||
| validates :repository_name, format: { with: CustomRegexp::REPOSITORY_NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" } | |||
| validates :repository_name, format: { with: CustomRegexp::REPOSITORY_NAME_REGEX, multiline: true, message: "项目标识只能包含数字,字母,下划线(_),中划线(-),英文句号(.),必须以数字和字母开头,不能以下划线/中划线开头和结尾" } | |||
| validates :name, length: { maximum: 50 } | |||
| validates :repository_name, length: { maximum: 100 } | |||
| @@ -3,7 +3,7 @@ class Projects::MigrateForm < BaseForm | |||
| :project_language_id, :clone_addr, :private, :is_mirror, :auth_username, :auth_password, :owner | |||
| validates :user_id, :name, :repository_name, :clone_addr, presence: true | |||
| validates :repository_name, format: { with: CustomRegexp::REPOSITORY_NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" } | |||
| validates :repository_name, format: { with: CustomRegexp::REPOSITORY_NAME_REGEX, multiline: true, message: "项目标识只能包含数字,字母,下划线(_),中划线(-),英文句号(.),必须以数字和字母开头,不能以下划线/中划线开头和结尾" } | |||
| validates :clone_addr, format: { with: CustomRegexp::URL_REGEX, multiline: true, message: "地址格式不正确" } | |||
| validates :name, length: { maximum: 50 } | |||
| validates :repository_name, length: { maximum: 100 } | |||
| @@ -3,7 +3,7 @@ class Projects::UpdateForm < BaseForm | |||
| validates :name, presence: true | |||
| validates :name, length: { maximum: 50 } | |||
| validates :description, length: { maximum: 200 } | |||
| validates :identifier, format: { with: CustomRegexp::REPOSITORY_NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" } | |||
| validates :identifier, format: { with: CustomRegexp::REPOSITORY_NAME_REGEX, multiline: true, message: '项目标识只能包含数字,字母,下划线(_),中划线(-),英文句号(.),必须以数字和字母开头,不能以下划线/中划线开头和结尾' } | |||
| validate do | |||
| check_project_category(project_category_id) | |||
| @@ -25,7 +25,7 @@ module Gitea | |||
| def run | |||
| Contents::CreateForm.new(valid_params).validate! | |||
| result = Gitea::Repository::Entries::CreateService.call(token, | |||
| owner, @params[:identifier], @params[:filepath], file_params) | |||
| owner, @params[:identifier], file_path, file_params) | |||
| if result[:status] == :success | |||
| @result = result[:body] | |||
| @@ -50,9 +50,17 @@ module Gitea | |||
| @result = response | |||
| end | |||
| def file_path | |||
| if @params[:base64_filepath].present? | |||
| Base64.decode64(params[:base64_filepath]) | |||
| else | |||
| @params[:filepath] | |||
| end | |||
| end | |||
| def valid_params | |||
| { | |||
| filepath: @params[:filepath], | |||
| filepath: file_path, | |||
| branch: @params[:branch], | |||
| new_branch: @params[:new_branch] | |||
| } | |||
| @@ -24,7 +24,7 @@ module Gitea | |||
| def run | |||
| Contents::DeleteForm.new(valid_params).validate! | |||
| response = Gitea::Repository::Entries::DeleteService.new(token, owner, @params[:identifier], @params[:filepath], file_params).call | |||
| response = Gitea::Repository::Entries::DeleteService.new(token, owner, @params[:identifier], file_path, file_params).call | |||
| render_result(response) | |||
| rescue Exception => exception | |||
| fail!(exception.message) | |||
| @@ -45,9 +45,17 @@ module Gitea | |||
| end | |||
| end | |||
| def file_path | |||
| if @params[:base64_filepath].present? | |||
| Base64.decode64(params[:base64_filepath]) | |||
| else | |||
| @params[:filepath] | |||
| end | |||
| end | |||
| def valid_params | |||
| { | |||
| filepath: @params[:filepath], | |||
| filepath: file_path, | |||
| sha: @params[:sha] | |||
| } | |||
| end | |||
| @@ -24,7 +24,7 @@ module Gitea | |||
| def run | |||
| Contents::UpdateForm.new(valid_params).validate! | |||
| response = Gitea::Repository::Entries::UpdateService.new(token, owner, @params[:identifier], @params[:filepath], file_params).call | |||
| response = Gitea::Repository::Entries::UpdateService.new(token, owner, @params[:identifier], file_path, file_params).call | |||
| render_result(response) | |||
| rescue Exception => exception | |||
| fail!(exception.message) | |||
| @@ -45,9 +45,25 @@ module Gitea | |||
| end | |||
| end | |||
| def file_path | |||
| if @params[:base64_filepath].present? | |||
| Base64.decode64(params[:base64_filepath]) | |||
| else | |||
| @params[:filepath] | |||
| end | |||
| end | |||
| def from_file_path | |||
| if @params[:base64_from_path].present? | |||
| Base64.decode64(params[:base64_from_path]) | |||
| else | |||
| @params[:from_path] | |||
| end | |||
| end | |||
| def valid_params | |||
| { | |||
| filepath: @params[:filepath], | |||
| filepath: file_path, | |||
| branch: @params[:branch], | |||
| new_branch: @params[:new_branch], | |||
| sha: @params[:sha] | |||
| @@ -59,7 +75,7 @@ module Gitea | |||
| branch: @params[:branch], | |||
| sha: @params[:sha], | |||
| new_branch: @params[:new_branch], | |||
| from_path: @params[:from_path], | |||
| from_path: from_file_path, | |||
| message: @params[:message], | |||
| content: Base64.encode64(@params[:content]) | |||
| ).compact | |||
| @@ -10,6 +10,6 @@ module CustomRegexp | |||
| IP = /^((\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/ | |||
| URL_REGEX = /\A(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?\z/i | |||
| REPOSITORY_NAME_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾 | |||
| REPOSITORY_NAME_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9\-\_\.]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾 | |||
| MD_REGEX = /^.+(\.[m|M][d|D])$/ | |||
| end | |||
| @@ -36,7 +36,7 @@ class Bot < ApplicationRecord | |||
| def self.decode_jwt_token(token) | |||
| decoded_token = JWT.decode token, nil, false | |||
| return [nil, "Token已过期"] if Time.now.to_i - 60 - decoded_token[0]["exp"].to_i > 0 | |||
| return [nil, "Token已过期"] if Time.now.to_i - 10*60 - decoded_token[0]["exp"].to_i > 0 | |||
| bot = Bot.find_by(id: decoded_token[0]["iss"]) | |||
| return [nil, "Token不存在"] if bot.blank? | |||
| rsa_private = OpenSSL::PKey::RSA.new(bot.private_key) | |||
| @@ -6,6 +6,7 @@ module Matchable | |||
| scope :with_project_language, ->(language_id) { where(project_language_id: language_id) unless language_id.blank? } | |||
| scope :with_project_type, ->(project_type) { where(project_type: project_type) if Project.project_types.include?(project_type) } | |||
| scope :by_name_or_identifier, ->(search) { where("name like :search or identifier LIKE :search", :search => "%#{search.split(" ").join('|')}%") unless search.blank? } | |||
| scope :with_project_topic, ->(topic_id) {joins(:project_topics).where(project_topics: {id: topic_id}) unless topic_id.blank?} | |||
| end | |||
| end | |||
| @@ -93,9 +93,14 @@ module ProjectOperable | |||
| team_user.destroy! if team_user | |||
| end | |||
| # 安装bot后的权限 | |||
| def is_install_bot?(user) | |||
| user.platform == "bot" && BotInstall.joins(:bot).where(bot: { uid: user.id }).where(store_id: self.id).exists? | |||
| end | |||
| def member?(user_id) | |||
| if owner.is_a?(User) | |||
| members.exists?(user_id: user_id) | |||
| members.exists?(user_id: user_id) || is_install_bot?(User.find_by(id: user_id)) | |||
| elsif owner.is_a?(Organization) | |||
| members.exists?(user_id: user_id) || team_projects.joins(team: :team_users).where(team_users: {user_id: user_id}).present? | |||
| else | |||
| @@ -126,6 +126,8 @@ class Project < ApplicationRecord | |||
| has_many :webhooks, class_name: "Gitea::Webhook", primary_key: :gpid, foreign_key: :repo_id | |||
| has_many :user_trace_tasks, dependent: :destroy | |||
| has_many :project_invite_links, dependent: :destroy | |||
| has_many :project_topic_ralates, dependent: :destroy | |||
| has_many :project_topics, through: :project_topic_ralates | |||
| after_create :incre_user_statistic, :incre_platform_statistic | |||
| after_save :check_project_members | |||
| before_save :set_invite_code, :reset_unmember_followed, :set_recommend_and_is_pinned, :reset_cache_data | |||
| @@ -380,7 +382,13 @@ class Project < ApplicationRecord | |||
| user = Owner.find_by_login namespace_path | |||
| user = User.new(login: namespace_path) if user.nil? | |||
| project = user&.projects&.find_by(identifier: identifier) || Project.find_by(identifier: "#{namespace_path}/#{identifier}") | |||
| if identifier.end_with?('.json') | |||
| project = user&.projects&.find_by(identifier: identifier) || Project.find_by(identifier: "#{namespace_path}/#{identifier}") | |||
| identifier = identifier.sub(/.*\K.json/, '') | |||
| project = user&.projects&.find_by(identifier: identifier) || Project.find_by(identifier: "#{namespace_path}/#{identifier}") | |||
| else | |||
| project = user&.projects&.find_by(identifier: identifier) || Project.find_by(identifier: "#{namespace_path}/#{identifier}") | |||
| end | |||
| return nil if project.blank? | |||
| [project, user] | |||
| @@ -0,0 +1,25 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: project_topics | |||
| # | |||
| # id :integer not null, primary key | |||
| # user_id :integer | |||
| # name :string(255) | |||
| # position :integer default("0") | |||
| # projects_count :integer default("0") | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_project_topics_on_user_id (user_id) | |||
| # | |||
| class ProjectTopic < ApplicationRecord | |||
| belongs_to :user, optional: true | |||
| has_many :project_topic_ralates, dependent: :destroy | |||
| has_many :projects, through: :project_topic_ralates | |||
| validates :name, uniqueness: { case_sensitive: false } | |||
| end | |||
| @@ -0,0 +1,22 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: project_topic_ralates | |||
| # | |||
| # id :integer not null, primary key | |||
| # project_topic_id :integer | |||
| # project_id :integer | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_project_topic_ralates_on_project_id (project_id) | |||
| # index_project_topic_ralates_on_project_topic_id (project_topic_id) | |||
| # | |||
| class ProjectTopicRalate < ApplicationRecord | |||
| belongs_to :project_topic, counter_cache: :projects_count | |||
| belongs_to :project | |||
| end | |||
| @@ -182,6 +182,7 @@ class User < Owner | |||
| has_many :assigned_issues, through: :issue_assigners, source: :issue | |||
| has_many :issue_participants, foreign_key: :participant_id | |||
| has_many :participant_issues, through: :issue_participants, source: :issue | |||
| has_many :project_topics | |||
| # Groups and active users | |||
| scope :active, lambda { where(status: [STATUS_ACTIVE, STATUS_EDIT_INFO]) } | |||
| scope :like, lambda { |keywords| | |||
| @@ -67,7 +67,7 @@ class Projects::ListMyQuery < ApplicationQuery | |||
| keywords = params[:search].to_s.each_char.select { |c| c.bytes.first < 240 }.join('') | |||
| q = projects.ransack(name_or_identifier_cont: keywords) | |||
| scope = q.result.includes(:project_category, :project_language,:owner, :repository, :has_pinned_users) | |||
| scope = q.result.includes(:project_category, :project_language,:owner, :repository, :has_pinned_users, :project_topics) | |||
| sort = Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : "updated_on" | |||
| sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : "desc" | |||
| @@ -3,7 +3,7 @@ class Projects::ListQuery < ApplicationQuery | |||
| attr_reader :params, :current_user_id | |||
| sort_columns :updated_on, :created_on, :forked_count, :praises_count, default_by: :updated_on, default_direction: :desc | |||
| sort_columns :updated_on, :created_on, :forked_count, :praises_count, default_by: :updated_on, default_direction: :desc, default_table: 'projects' | |||
| def initialize(params, current_user_id=nil) | |||
| @params = params | |||
| @@ -32,6 +32,7 @@ class Projects::ListQuery < ApplicationQuery | |||
| collection = by_project_type(collection) | |||
| collection = by_project_category(collection) | |||
| collection = by_project_language(collection) | |||
| collection = by_project_topic(collection) | |||
| collection | |||
| end | |||
| @@ -74,6 +75,10 @@ class Projects::ListQuery < ApplicationQuery | |||
| (params[:pinned].present? && params[:category_id].present?) ? items.pinned : items | |||
| end | |||
| def by_project_topic(items) | |||
| items.with_project_topic(params[:topic_id]) | |||
| end | |||
| # 优化排序 | |||
| def optimize_sorting(relations, sort_by) | |||
| if sort_by == "updated_on" | |||
| @@ -0,0 +1,4 @@ | |||
| json.total_count @project_topics.total_count | |||
| json.project_topics @project_topics.each do |topic| | |||
| json.(topic, :id, :name, :projects_count) | |||
| end | |||
| @@ -3,7 +3,12 @@ json.type webhook["type"] | |||
| json.content_type webhook['config']['content_type'] | |||
| json.http_method webhook['config']['http_method'] | |||
| json.url webhook['config']['url'] | |||
| json.events webhook['events'] | |||
| event = webhook.events | |||
| if event["send_everything"] | |||
| json.events event["events"].keys.collect{|i| %w(pull_request issues).include?(i) ? i + "_only" : i} | |||
| else | |||
| json.events event["events"].select{|k, v| v}.keys.collect{|i| %w(pull_request issues).include?(i) ? i + "_only" : i} | |||
| end | |||
| json.active webhook['active'] | |||
| json.branch_filter webhook['branch_filter'] | |||
| json.created_at format_time(webhook['created_at'].to_time) | |||
| @@ -2,10 +2,15 @@ json.status 0 | |||
| json.message "success" | |||
| json.data do | |||
| json.array! @install_bots do |install_bot| | |||
| json.installation_id install_bot.id | |||
| json.extract! install_bot.bot, :id, :name | |||
| json.bot_id install_bot.bot.id | |||
| json.bot_name install_bot.bot.name | |||
| json.extract! install_bot, :id, :bot_id, :installer_id, :create_time, :update_time | |||
| json.bot_name install_bot&.bot&.name | |||
| json.account do | |||
| user = User.find_by(id: install_bot.installer_id) | |||
| if user.present? | |||
| json.partial! "api/v1/users/simple_user", locals: {user: user} | |||
| else | |||
| json.nil! | |||
| end | |||
| end | |||
| end | |||
| end | |||
| @@ -1,5 +1,13 @@ | |||
| json.partial! "commons/success" | |||
| json.extract! @install_bot, :id, :bot_id, :installer_id, :state, :create_time, :update_time | |||
| json.bot_name @install_bot.bot.name | |||
| json.extract! @install_bot, :id, :bot_id, :installer_id, :create_time, :update_time | |||
| json.bot_name @install_bot&.bot&.name | |||
| json.account do | |||
| user = User.find_by(id: @install_bot.installer_id) | |||
| if user.present? | |||
| json.partial! "api/v1/users/simple_user", locals: { user: user } | |||
| else | |||
| json.nil! | |||
| end | |||
| end | |||
| @@ -50,3 +50,6 @@ json.language do | |||
| json.name project.project_language.name | |||
| end | |||
| end | |||
| json.topics project.project_topics.each do |topic| | |||
| json.(topic, :id, :name) | |||
| end | |||
| @@ -48,4 +48,7 @@ json.projects @projects do |project| | |||
| json.name project.project_language.name | |||
| end | |||
| end | |||
| json.topics project.project_topics.each do |topic| | |||
| json.(topic, :id, :name) | |||
| end | |||
| end | |||
| @@ -6,4 +6,7 @@ json.project_category_id @project.project_category_id | |||
| json.project_language_id @project.project_language_id | |||
| json.is_public @project.is_public | |||
| json.website @project.website | |||
| json.lesson_url @project.lesson_url | |||
| json.lesson_url @project.lesson_url | |||
| json.topics @project.project_topics.each do |topic| | |||
| json.(topic, :id, :name) | |||
| end | |||
| @@ -5,7 +5,7 @@ json.create_time Time.at(@webhook.created_unix).strftime("%Y-%m-%d %H:%M:%S") | |||
| event = @webhook.events | |||
| json.branch_filter event["branch_filter"] | |||
| if event["send_everything"] | |||
| json.events event["events"].keys.collect{|i| i == "pull_request" ? i + "_only" : i} | |||
| json.events event["events"].keys.collect{|i| %w(pull_request issues).include?(i) ? i + "_only" : i} | |||
| else | |||
| json.events event["events"].select{|k, v| v}.keys.collect{|i| i == "pull_request" ? i + "_only" : i} | |||
| json.events event["events"].select{|k, v| v}.keys.collect{|i| %w(pull_request issues).include?(i) ? i + "_only" : i} | |||
| end | |||
| @@ -21,6 +21,9 @@ json.mirror_url @project&.repository.remote_mirror_url | |||
| json.mirror @project&.repository.mirror_url.present? | |||
| json.type @project.numerical_for_project_type | |||
| json.open_devops @project.open_devops? | |||
| json.topics @project.project_topics.each do |topic| | |||
| json.(topic, :id, :name) | |||
| end | |||
| unless @project.common? | |||
| json.mirror_status @repository.mirror_status | |||
| @@ -467,7 +467,7 @@ Rails.application.routes.draw do | |||
| namespace :traces do | |||
| resources :trace_users, only: [:create] | |||
| scope "/:owner/:repo" do | |||
| scope "/:owner/:repo", constraints: { repo: /[^\/]+/ } do | |||
| resource :projects, path: '/', only: [:index] do | |||
| member do | |||
| post :tasks | |||
| @@ -480,7 +480,7 @@ Rails.application.routes.draw do | |||
| end | |||
| # Project Area START | |||
| scope "/:owner/:repo" do | |||
| scope "/:owner/:repo",constraints: { repo: /[^\/]+/ } do | |||
| scope do | |||
| get( | |||
| '/activity', | |||
| @@ -1080,7 +1080,7 @@ Rails.application.routes.draw do | |||
| end | |||
| resources :installations, only: [] do | |||
| get :repositories, on: :collection | |||
| get :repositories, on: :member | |||
| end | |||
| root 'main#index' | |||
| @@ -2,7 +2,7 @@ defaults format: :json do | |||
| namespace :api do | |||
| namespace :v1 do | |||
| scope ':owner' do | |||
| resource :users, path: '/', only: [:show, :update, :edit, :destroy] do | |||
| resource :users, path: '/', only: [:update, :edit, :destroy] do | |||
| collection do | |||
| get :send_email_vefify_code | |||
| post :check_password | |||
| @@ -18,9 +18,9 @@ defaults format: :json do | |||
| resources :feedbacks, only: [:create] | |||
| end | |||
| scope ':repo' do | |||
| scope ':repo', constraints: { repo: /[^\/]+/ } do | |||
| # projects | |||
| resource :projects, path: '/', only: [:show, :update, :edit, :destroy] do | |||
| resource :projects, path: '/', only: [:show, :update, :edit] do | |||
| collection do | |||
| get :compare | |||
| get :blame | |||
| @@ -97,6 +97,7 @@ defaults format: :json do | |||
| end | |||
| resources :projects, only: [:index] | |||
| resources :project_topics, only: [:index, :create, :destroy] | |||
| end | |||
| @@ -0,0 +1,19 @@ | |||
| class CreateProjectTopics < ActiveRecord::Migration[5.2] | |||
| def change | |||
| create_table :project_topics do |t| | |||
| t.references :user | |||
| t.string :name | |||
| t.integer :position, default: 0 | |||
| t.integer :projects_count, default: 0 | |||
| t.timestamps | |||
| end | |||
| create_table :project_topic_ralates do |t| | |||
| t.belongs_to :project_topic, index: true | |||
| t.belongs_to :project, index: true | |||
| t.timestamps | |||
| end | |||
| end | |||
| end | |||
| @@ -719,7 +719,7 @@ | |||
| http://localhost:3000/api/licenses | |||
| </code></pre></div><div class="highlight"><pre class="highlight javascript tab-javascript"><code><span class="k">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">GET /api/licenses</span><span class="dl">'</span><span class="p">)</span> | |||
| </code></pre></div><h3 id='http-request'>HTTP Request</h3> | |||
| <p><code>GET https://forgeplus.trustie.net/api/licenses.json</code></p> | |||
| <p><code>GET https://www.gitlink.org.cn/api/licenses.json</code></p> | |||
| <h3 id='1f9ac54b15'>请求参数</h3> | |||
| <table><thead> | |||
| <tr> | |||
| @@ -0,0 +1,32 @@ | |||
| require 'openssl' | |||
| require 'jwt' # https://rubygems.org/gems/jwt | |||
| # Private key contents | |||
| private_pem = File.read("/Users/xxq/Documents/gitlink-webhook.2022-06-09.private-key.pem") | |||
| private_key = OpenSSL::PKey::RSA.new(private_pem) | |||
| # Generate the JWT | |||
| payload = { | |||
| # issued at time, 60 seconds in the past to allow for clock drift | |||
| iat: Time.now.to_i - 60, | |||
| # JWT expiration time (10 minute maximum) | |||
| exp: Time.now.to_i + (10 * 60), | |||
| # GitHub App's identifier | |||
| iss: "209248" | |||
| } | |||
| jwt = JWT.encode(payload, private_key, "RS256") | |||
| puts jwt | |||
| # puts OpenSSL::PKey::RSA.new(private_key33).public_key.to_s | |||
| # | |||
| # rsa_private = OpenSSL::PKey::RSA.new(private_key33) | |||
| # rsa_public = rsa_private.public_key | |||
| # | |||
| # | |||
| # puts decoded_token[0] | |||
| # puts decoded_token[0]["iss"] | |||
| # serialized_private_key = OpenSSL::PKey::RSA::generate(2048).to_s | |||
| @@ -2,12 +2,35 @@ require 'openssl' | |||
| require 'jwt' # https://rubygems.org/gems/jwt | |||
| # Private key contents | |||
| private_pem = File.read("/Users/xxq/Documents/gitlink-webhook.2022-06-09.private-key.pem") | |||
| private_key22="-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEApOebWmRV/ooNq5Ks04YnDU7pEezGShGvaiF0cIvn9jvmYHu0\nFialojvJV3VpB6xE6QBPXZ0Pi1lokZ9dMx8F5UWNx9WA7wf7xK3hAJLNml+GeewF\nou8vk/Ry7n6diLxETNVd7YzPvztn5qaMp/DXa+65i11H8a/XXqR7kCVnCevVlufh\nNr/Dp6dW31W8TInnDQasJFMZ8GY7f+tCwLXNc0M8p+TeDp9xmXHOrEB+S/mgbUOF\nXgRr6icbMmlT9bsAxYHrrDkcVxJhs0hq5vD3BaoK06gcZEnN7/HVNzgVSOYNsVNh\n9006cMgDSOwc9F8aulP7cr8k74INq1xswoGs9wIDAQABAoIBAHayc2NkF3YJXv+h\nqx7yUEfHBgKuAKiuBCqLfCnKuqPFx/So9h5/oPeeuzVlwL0SJePlIjuK4vZ128v9\n/vLeILtADmbJ6m2jvHh8hBmKkc3Ndplp50C5k/CWoufCYZhbk3oOlvZ3Rc4rb4VZ\nWqNDu3voMMv8z91KqeZo1LwUAA/l9mU++zLkRA6qOuWGBJFsM8YpshzxL5lzRUb3\n7y+YJDyUZztfzKwr6pqm1n9B2e6e+znCw1vMZXp2TbUrpvrrXSxlgdNuK68SkZX6\nTdZUD8y0viwaioRVf3vR+e/Bf7yZannGdvcmVGs0A7dq9QgHkakqNHiRkQgwviSq\nbjBo7dECgYEAzekeP5j/dAPkv9X4qnmZ4du/+ZgrQrJckDD/JuNIBmQT9m286l4P\nmb2TBcWkswVOZaS5Qy2bN/69rwIbcdvbaROGBCabn3ATK4fSzmUk31M2rRKYZqaU\nMi0W2g2YtSRg+bV6S7aFXa98j5+JlqJeDZQoRuvL68ooq5WzFWmfYnkCgYEAzQTi\n4USqz2z66BfU+v2rchzK8URxnv7EW/CG3XFRsG+1UXCyEIct2L7rzvC7r7+jjS4s\ngmV3Civ1sckGMwikLzxFtUZ1LUBakZp/mmipIzxcHOeBsRdHei8BFvMNqveg1JpO\ncY/Fp+wEkSNLhfkb/IXRw0iwFalBRnyo4BJbLu8CgYBrQ7E6OB169jxHotNzGv2K\npssO3rJKgFev1ZZVT7jJe4Dasrfi7zT5RcQ9EYSGrZD1aiYIVM2zEcUGUfayDXHy\n/vSlXOdc2ylhV9P9KLtYiyTEbBdwAf7ZVJu+465VTqol6t/WaTJ4Z15gAx/NlK+i\nKzgAGf2Uyy78k3NDCE67IQKBgFwM0pUUEKEbLDhi4uRiWsTcep4C/gTGHIGvJ85r\nH6NZNI7BS6GyH/qOFjAO1CYfpB4yWhed2Om/PQw61sa5HYZ7yEyQuvG7UC7JsHsy\nfKZuZmkv5IIPkq8gRZv5OuzFS/fI5GmGhNdVV+OWdkVLyK4Do1/L1guTt9QfCm+4\nrioPAoGBALGr8aUAbz/A611M/bLnk04UYfV+M34/hCf6/rKiBHdQoIHOriSC9Nv7\nyhE5axTdmIWMxfbyb3vHJ5MizZkD/Qj0VDuMkyS2+3TepI6tySQE3YQeWnCMJI9i\nuoCZ31GBui4+W5udbx8NOVsJfXUQn/OAoOn6WuMNPdgB45KXcktj\n-----END RSA PRIVATE KEY-----\n" | |||
| private_key33= "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAq/wUH8N+5fzj3hArKY7ChC591R/uKyeNM/BbsR2OGxO5F1CE\nozy6thtPult96Gm9oK3sMpLdYCze2YgozgteFO1Ft0o1GEJ1A4SinOKzeixLpFy0\n5N9t+iz7Xa7jC/1E3uy/s2WvSYCS9NnK2Uj3DQOH8BUWfkvyTtt91a2pplbPCV3T\nw1PykAcDWIFXVJJCMtYd2x+DukSWKHRsYbBCMtVZhEVuKmTE3FBTDVu9sN3b7uLL\n5RzHUg13QZZr9OvMNR3nUZl6yDxw+wD4anDrtpL9C+tFjhMyqsyYpYWwcJm65YiD\ny7Ps24IdcLB4iOxJE91fu+MnicvyBrtEjoBP/wIDAQABAoIBAAcKc9x1CW3q8300\n1j+GS6pTqO0fuIVlwh8dOPPATQAIx6wPrM5t/wrThWkQs8/e/Fdmp2POpWd5jsoD\nDACbcIeUyyTc0d2jYtz5AhtAIK7gv1wEO5efGgaC7ut/7GWiQb6KnLKAeDOfIuUJ\nQYexuAN9YIRQqLIU89+MltM3n9liZTMuPWRFJcitaDytXa10TCe5RUqHGZf49pi7\njCgk0x7jDYqbIzsqOu741P8My/gkAjKPkRnjaj3o6MrwHzIlc4t/6mKbaPnywywk\n6roYMqmytgueA9wxFcj74ekBQAaXsu4xRkbZXxjcBtIvTId5IHHK5Z/r3fgE9K3J\nOuzzJ1kCgYEA3uu/pUjJbKegOsgSdu3cO/NvRV2YsRD4NUgtiMCEakE4VQXRK5pf\nV8xqQeH/rLjZf5aP5xe8n25krh/c+m0ezOMyu5MmhoxaWCPWIezsaKJXOcvsOxIu\n2sJ9GRMXabyuDuSdL7ZGYMpLhRclXYLyPCz7wzN445IluTHuD3lJ2qUCgYEAxYFh\ncVD73yNZn9BN1DSGWpfPtLqOKIdG+xi/ypCSGpJG0QCJRFi7R1qJOxFtJNI8DRiD\nZapPEGLVd/KY5NzBGZBfNQt4DQH9qR4l43c6NNkisWA9rvXvCDqXKmBq7wfpnkYr\n4Ul2hXYmsPJjP8e0BfG54PaSu3BDBMJMtcgDktMCgYBvzyDdnwdgVyc3tHgGbMFk\n1HHAAfT/ArrrxpsIFz+TJ8lAY92JGDGwENhO2TLrCAAXTYY5657w/GbFKzgj5y1m\nqKIekOzm2WjLApZ5h6L/zEUhuRVwf2s+0AP82qWIpFlNIP9yGeNs0qpUQ8q6/13O\nLuXL/3on8nq3S8LSwgv3/QKBgDfM+g7d5ouAnU29uH9/54Wo5pIVMxzYO4Gt2GIO\nvnirYz6hfCbHOwJJ3gPGRKPmkfjROC59E6F5iv48mF3w0M28MGn4N47VRSmGzwWZ\nJeTQhDDBFCxeZ45Xn2Xln9Cw15xUDwmzi7zhSMUtdkUK0x3q0a1xfLtgWE775lhl\njjzpAoGAdUXFW1elfjXpfIYZf7vUV7MPquKL7qAcopd96XBszhOn7g+ibzem+wgt\n1UTSeOBESYANHeJk2MuWPSRXk/FlQETVIcPAEp0kxbwQE+7YEdrMVeDcIe5lPwGD\n+WuS3kg0MPgUrXZXn74gcwWmSIOyHfqXULqOxWE25uU2icdV+2w=\n-----END RSA PRIVATE KEY-----\n" | |||
| private_key = OpenSSL::PKey::RSA.new(private_key33) | |||
| # puts private_key.to_json | |||
| # private_pem = File.read("/Users/xxq/Documents/gitlink-webhook.2022-06-09.private-key.pem") | |||
| private_pem = "-----BEGIN RSA PRIVATE KEY----- | |||
| MIIEowIBAAKCAQEAy3rJwhzC9K5f4Rc6KwYQKvdsUsbzrKdENBCjGq0kZ8LJltw+ | |||
| TRuLVp0hwrWXS9msadjBnzatYneBrqnggj87pU0Mf/f6KkTmIsFH2gDKNMN6hRg/ | |||
| JW5XXveexGa9dZIQHPKOmp9dC+YpLHOPRO0NsYJM/JoQ4rWRhtMvq0zR/fJDuXyt | |||
| xLehfH4N4ppVbdRGaK+FIKNCjHTmL/3jZ0b2J9D4Al9R5QWlEmRPa4ZFCOZNUMXK | |||
| nplNu2FE/ZjyKjE+f0MAn8hcSVR25XiQZ6StnxdyPvhXYNNU7yVJGTfCK/oTchTr | |||
| Tfk4bKIkct38datffjlhKM45VabhUfIStNr5uwIDAQABAoIBADSMgGBmBx8jjVVX | |||
| J0mHJlPCVDJIeROkmuOLTGQORPGbB26zcE9/houWxuo+9VS8YV9wgAh7GWntjQsr | |||
| ifR5GhFFha3iv7N82aYuHj05qP7ZYOHQcjZbearn7hOwqMsdLpYbOiLKd0Akb4uw | |||
| SFa3laq7CODPdP7nfy6/iXcGvtCC84E8XwDuUcFbzX8lLq0UI6HvdnySYOvglO3P | |||
| txqr76dc61nLEIu945uVKndzu65sWRfC397JG+hgYFUwikbiFPAVVH4jsiCzMAW0 | |||
| oaTp6zQiE+h7HTGGHhnxFmnynmuq01E+wfxNvf8NHteBIMJA8g/gGHdpp+8E85V7 | |||
| bcF8+fECgYEA5cs8nZHfrTdjqZB6vgY1sCVsuuHtmEACu3zbJu9dr21ps6bomnDW | |||
| Lw9Dx4tYSHPcL3zlu913qvXCnXsUGtjKeIVTHCdnwCnDTEam6GsDZab9zcmJ8Gkn | |||
| wqsjr/Cy7pjwxqT3nFRUGaJmrFUUfPQuNmSYWQbRiRIQirGY2JlZLvMCgYEA4q9Q | |||
| j8cwq5i1blWuyam8bx3efHBHMiMpJL/clc03//ddpsiae8GXZgRySJx9YPAYS+X/ | |||
| ApVfnB/en+xidCIg8P2rRjRW/GgW3Y6rjLUMIFMD/0ObezX/dp2AVHUdOdlZ7z+k | |||
| 4Ba8TZ8u5sfUO7USTxFl+fextLdBDSetOw/GDBkCgYAsYSjuwYpyWJ0t1VJvOqHJ | |||
| yCCMoy+Q1OPyM7XbeiUcyUO9x4FquloTMp6DfjzpmT6wCS4RLz96TAZvBaMnYDES | |||
| P6WCbXXTHf2y0H5RqsE4M50WzlKOlLByHz1AMHtOK0ltA9UyYvLvFHdB1xii3UHD | |||
| jYACyZdUIqIBNzVut4cK0wKBgBNbHOnp/EHqvDM7pb0afTiPuFuvyqSBVBYLO+6e | |||
| o1V77cc8AdTnZuITJx8EHcCVP73bWbcCwjM2lW/aY12/PEjXoDRSa8sJqEoq0IMn | |||
| Qm3QKNs3DqOqrLGYKUkM5v31jTRcnttzlYibOwoBriGbCIEv3yFFASuJKkjRRn1w | |||
| j1yhAoGBALqwZzRAle7t2jEWOyLaJeHUoSiZJv5dHOMRog7k5H24uTEKf3JCovQO | |||
| wZd4cIA4oXD/3b5cK3H2e5YjhBunsMRhRBLgz8FD4y69cKxbcgPvtjhkwYRB0/cy | |||
| 21dHe2o7HfuZgUuh0kOlT0e326gQPIIlwuCBEAq6LzWrg8nd9Loz | |||
| -----END RSA PRIVATE KEY-----" | |||
| private_key = OpenSSL::PKey::RSA.new(private_pem) | |||
| # Generate the JWT | |||
| payload = { | |||
| @@ -16,27 +39,15 @@ payload = { | |||
| # JWT expiration time (10 minute maximum) | |||
| exp: Time.now.to_i + (10 * 60), | |||
| # GitHub App's identifier | |||
| iss: "782" | |||
| iss: "803" | |||
| } | |||
| jwt = JWT.encode(payload, private_key, "RS256") | |||
| puts jwt | |||
| # puts OpenSSL::PKey::RSA.new(private_key33).public_key.to_s | |||
| # | |||
| rsa_private = OpenSSL::PKey::RSA.new(private_key33) | |||
| rsa_public = rsa_private.public_key | |||
| # decoded_token = JWT.decode jwt, nil, false | |||
| begin | |||
| decoded_token = JWT.decode jwt, rsa_private, true, { algorithm: 'RS256' } | |||
| rescue JWT::DecodeError | |||
| puts "jwt is not mmmmmm" | |||
| end | |||
| # | |||
| puts decoded_token[0] | |||
| puts decoded_token[0]["iss"] | |||
| # serialized_private_key = OpenSSL::PKey::RSA::generate(2048).to_s | |||
| decoded_token = JWT.decode "eyJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE2ODA1OTkzNDAsImV4cCI6MTY4MDYwMDAwMCwiaXNzIjoiODAzIn0.wOCCMVbrulTVCIKDvmLtxCkTLtWJMGmY2mIZgfdJcKcixqZek1y_9YD7wF07wqP6qTjQaNiSDjdJSDGzO_Qi3qQT_2BaR6EWBUIcbaNz5GTHKLcOW4SFWj13OFJwjom6egz489b6qA3MPXmliWYR6F5zlLu1jlXjaWVvUZAy0AuAdmWiSocdjurt_giEIDefiRcPu_NbccWG-mAwa9wV9ja2PoZUJyHlzXR6rioLIO1rtw5bIX3E4YNPde9EkEK1eYLmedmhKfwlgX2CgdGodSHPg5Vro09XWiGaJkBwoi1T41BLVsb5hxqQc3DLrz1ZFFY1vXEkxIw4BIXitpk3kg", nil, false | |||
| puts decoded_token | |||
| puts Time.now.to_i - 60 - decoded_token[0]["exp"].to_i > 0 | |||