From 99fbecc8345b66197650970c2f1ee568bc430ce5 Mon Sep 17 00:00:00 2001 From: xiaoxiaoqiong Date: Fri, 11 Mar 2022 14:06:12 +0800 Subject: [PATCH 001/208] =?UTF-8?q?gitea=20=E8=AF=B7=E6=B1=82=E5=BB=B6?= =?UTF-8?q?=E9=95=BF=E5=88=B020=E5=88=86=E9=92=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/services/gitea/client_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/gitea/client_service.rb b/app/services/gitea/client_service.rb index 7cdfdd49..b0c842db 100644 --- a/app/services/gitea/client_service.rb +++ b/app/services/gitea/client_service.rb @@ -82,7 +82,7 @@ class Gitea::ClientService < ApplicationService req.headers['Content-Type'] = 'application/json' req.response :logger # 显示日志 req.adapter Faraday.default_adapter - req.options.timeout = 100 # open/read timeout in seconds + req.options.timeout = 1200 # open/read timeout in seconds req.options.open_timeout = 10 # connection open timeout in seconds if token.blank? req.basic_auth(username, secret) From bc639488aa641450ab15622e0b327c3afc906fd3 Mon Sep 17 00:00:00 2001 From: yystopf Date: Tue, 22 Mar 2022 10:45:14 +0800 Subject: [PATCH 002/208] fix: issues filter error --- app/controllers/issues_controller.rb | 2 +- app/controllers/pull_requests_controller.rb | 2 +- app/services/issues/list_query_service.rb | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index ad53a4b3..b546ea71 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -24,7 +24,7 @@ class IssuesController < ApplicationController @filter_issues = @all_issues @filter_issues = @filter_issues.where.not(status_id: IssueStatus::CLOSED) if params[:status_type].to_i == IssueStatus::ADD @filter_issues = @filter_issues.where(status_id: IssueStatus::CLOSED) if params[:status_type].to_i == IssueStatus::SOLVING - @filter_issues = @filter_issues.where("subject LIKE ? OR description LIKE ? ", "%#{params[:search]}%", "%#{params[:search]}%") if params[:search].present? + @filter_issues = @filter_issues.where("issues.subject LIKE ? OR issues.description LIKE ? ", "%#{params[:search]}%", "%#{params[:search]}%") if params[:search].present? @open_issues = @all_issues.where.not(status_id: IssueStatus::CLOSED) @close_issues = @all_issues.where(status_id: IssueStatus::CLOSED) scopes = Issues::ListQueryService.call(issues,params.delete_if{|k,v| v.blank?}, "Issue") diff --git a/app/controllers/pull_requests_controller.rb b/app/controllers/pull_requests_controller.rb index 5500ed79..189c5c6b 100644 --- a/app/controllers/pull_requests_controller.rb +++ b/app/controllers/pull_requests_controller.rb @@ -16,7 +16,7 @@ class PullRequestsController < ApplicationController issues = issues.where(is_private: false) unless current_user.present? && (current_user.admin? || @project.member?(current_user)) @all_issues = issues.distinct @filter_issues = @all_issues - @filter_issues = @filter_issues.where("subject LIKE ? OR description LIKE ? ", "%#{params[:search]}%", "%#{params[:search]}%") if params[:search].present? + @filter_issues = @filter_issues.where("issues.subject LIKE ? OR issues.description LIKE ? ", "%#{params[:search]}%", "%#{params[:search]}%") if params[:search].present? @open_issues = @filter_issues.joins(:pull_request).where(pull_requests: {status: PullRequest::OPEN}) @close_issues = @filter_issues.joins(:pull_request).where(pull_requests: {status: PullRequest::CLOSED}) @merged_issues = @filter_issues.joins(:pull_request).where(pull_requests: {status: PullRequest::MERGED}) diff --git a/app/services/issues/list_query_service.rb b/app/services/issues/list_query_service.rb index f85e2cb9..def9644d 100644 --- a/app/services/issues/list_query_service.rb +++ b/app/services/issues/list_query_service.rb @@ -27,12 +27,12 @@ class Issues::ListQueryService < ApplicationService issues = issues.joins(:pull_request).where(pull_requests: {status: 1}) end - if search_name.present? - issues = issues.where("subject LIKE ? OR description LIKE ? ", "%#{search_name}%", "%#{search_name}%") - end + # if search_name.present? + # issues = issues.where("issues.subject LIKE ? OR issues.description LIKE ? ", "%#{search_name}%", "%#{search_name}%") + # end if start_time&.present? || end_time&.present? - issues = issues.where("start_date between ? and ?",start_time&.present? ? start_time.to_date : Time.now.to_date, end_time&.present? ? end_time.to_date : Time.now.to_date) + issues = issues.where("issues.start_date between ? and ?",start_time&.present? ? start_time.to_date : Time.now.to_date, end_time&.present? ? end_time.to_date : Time.now.to_date) end issues = issues.where(author_id: params[:author_id]) if params[:author_id].present? && params[:author_id].to_s != "all" From d0d906d04c4e7c97ec9c602bbcbd578b92e4a157 Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 28 Mar 2022 11:05:21 +0800 Subject: [PATCH 003/208] fix: readme image regex error rescued --- app/helpers/repositories_helper.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index c6924185..e6e7e87f 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -57,6 +57,7 @@ module RepositoriesHelper total_images = ss + ss_src if total_images.length > 0 total_images.each do |s| + begin image_title = /\"(.*?)\"/ r_content = s[0] remove_title = r_content.to_s.scan(image_title) @@ -75,6 +76,9 @@ module RepositoriesHelper new_r_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw?filepath=#{r_content}&ref=#{ref}"].join end content = content.gsub(/#{r_content}/, new_r_content) + rescue + next + end end end @@ -96,7 +100,9 @@ module RepositoriesHelper def decode64_content(entry, owner, repo, ref, path=nil) if is_readme?(entry['type'], entry['name']) + Rails.logger.info("entry===#{entry["type"]} #{entry["name"]}") content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content'] + Rails.logger.info("content===#{content}") readme_render_decode64_content(content, owner, repo, ref) else file_type = File.extname(entry['name'].to_s)[1..-1] From 747646cd9d4bdd214dc8312e02b72d925468fffc Mon Sep 17 00:00:00 2001 From: yystopf Date: Wed, 30 Mar 2022 16:19:32 +0800 Subject: [PATCH 004/208] add: trace client and api completed --- app/libs/trace.rb | 21 +++ app/services/trace/add_user_service.rb | 37 ++++++ app/services/trace/check_result_service.rb | 33 +++++ app/services/trace/check_service.rb | 36 ++++++ app/services/trace/client_service.rb | 143 +++++++++++++++++++++ app/services/trace/login_service.rb | 29 +++++ app/services/trace/pdf_report_service.rb | 26 ++++ app/services/trace/reload_check_service.rb | 25 ++++ config/configuration.yml.example | 4 + 9 files changed, 354 insertions(+) create mode 100644 app/libs/trace.rb create mode 100644 app/services/trace/add_user_service.rb create mode 100644 app/services/trace/check_result_service.rb create mode 100644 app/services/trace/check_service.rb create mode 100644 app/services/trace/client_service.rb create mode 100644 app/services/trace/login_service.rb create mode 100644 app/services/trace/pdf_report_service.rb create mode 100644 app/services/trace/reload_check_service.rb diff --git a/app/libs/trace.rb b/app/libs/trace.rb new file mode 100644 index 00000000..b1c3c476 --- /dev/null +++ b/app/libs/trace.rb @@ -0,0 +1,21 @@ +module Trace + class << self + def trace_config + trace_config = {} + + begin + config = Rails.application.config_for(:configuration).symbolize_keys! + trace_config = config[:trace].symbolize_keys! + raise 'trace config missing' if trace_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} + trace_config = {} + end + + trace_config + end + end +end \ No newline at end of file diff --git a/app/services/trace/add_user_service.rb b/app/services/trace/add_user_service.rb new file mode 100644 index 00000000..9c7b12b4 --- /dev/null +++ b/app/services/trace/add_user_service.rb @@ -0,0 +1,37 @@ +# 代码溯源 添加用户 +class Trace::AddUserService < Trace::ClientService + + # 用户名 密码 单位 手机号 邮箱 昵称 + attr_accessor :username, :password, :unit, :telnumber, :email, :name + + def initialize(username, password, unit, telnumber, email, name) + @username = username + @password = password + @unit = unit + @telnumber = telnumber + @email = email + @name = name + end + + def call + result = post(url, request_params) + response = render_response(result) + end + + private + + def request_params + { + username: username, + password: password, + unit: unit, + telnumber: telnumber, + email: email, + name: name + } + end + + def url + "/user/addccfuser".freeze + end +end \ No newline at end of file diff --git a/app/services/trace/check_result_service.rb b/app/services/trace/check_result_service.rb new file mode 100644 index 00000000..f1dd61ab --- /dev/null +++ b/app/services/trace/check_result_service.rb @@ -0,0 +1,33 @@ +# 代码溯源 查询检测结果 +class Trace::CheckResultService < Trace::ClientService + + attr_accessor :token, :project_name, :file_name, :page_num, :page_size + + def initialize(token, project_name=nil, file_name=nil, page_num=1, page_size=15) + @token = token + @project_name = project_name + @file_name = file_name + @page_num = page_num + @page_size = page_size + end + + def call + result = authed_get(token, url, request_params) + reponse = render_response(result) + end + + private + def request_params + { + product_name: project_name, + file_name: file_name, + pageNum: page_num, + pageSize: page_size, + } + end + + def url + "/user/checkresult".freeze + end +end + diff --git a/app/services/trace/check_service.rb b/app/services/trace/check_service.rb new file mode 100644 index 00000000..841627b5 --- /dev/null +++ b/app/services/trace/check_service.rb @@ -0,0 +1,36 @@ +# 代码溯源 开始检测 +class Trace::CheckService < Trace::ClientService + + attr_accessor :token, :project, :if_branch, :branch_tag + + def initialize(token, project, if_branch, branch_tag) + @token = token + @project = project + @if_branch = if_branch + @branch_tag = branch_tag + end + + def call + result = authed_post(token, url, request_params) + reponse = render_response(result) + end + + private + def request_params + repo = Gitea::Repository::GetService.call(project&.owner&.login, project&.identifier) + { + product_name: project&.name, + product_type: project&.category&.name, + code_type: project&.language&.name, + product_desc: project&.description, + git_url: repo['clone_url'], + if_branch: if_branch, + branch_tag: branch_tag + } + end + + def url + "/user/check".freeze + end +end + diff --git a/app/services/trace/client_service.rb b/app/services/trace/client_service.rb new file mode 100644 index 00000000..ff6b7281 --- /dev/null +++ b/app/services/trace/client_service.rb @@ -0,0 +1,143 @@ +class Trace::ClientService < ApplicationService + + def post(url, params={}) + puts "[trace][POST] request params: #{params}" + conn.post do |req| + req.url = full_url(url) + req.body = params[:data].to_json + end + end + + def authed_post(token, url, params={}) + puts "[trace][POST] request params: #{params}" + puts "[trace][POST] request token: #{token}" + conn.post do |req| + req.url = full_url(url) + req.body = params[:data].to_json + req.headers['Authorization'] = token + end + end + + def get(url, params={}) + puts "[trace][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 authed_get(token, url, params={}) + puts "[trace][GET] request params: #{params}" + puts "[trace][GET] request token: #{token}" + conn.get do |req| + req.url full_url(url, 'get') + params.each_pair do |key, value| + req.params["#{key}"] = value + end + req.headers['Authorization'] = token + end + end + + def delete(url, params={}) + puts "[trace][DELETE] request params: #{params}" + conn.delete do |req| + req.url full_url(url) + req.body = params[:data].to_json + end + end + + def authed_delete(token, url, params={}) + puts "[trace][DELETE] request params: #{params}" + puts "[trace][DELETE] request token: #{token}" + conn.delete do |req| + req.url full_url(url) + req.body = params[:data].to_json + req.headers['Authorization'] = token + end + end + + def patch(url, params={}) + puts "[trace][PATCH] request params: #{params}" + conn.patch do |req| + req.url full_url(url) + req.body = params[:data].to_json + end + end + + def authed_patch(token, url, params={}) + puts "[trace][PATCH] request params: #{params}" + puts "[trace][PATCH] request token: #{token}" + conn.patch do |req| + req.url full_url(url) + req.body = params[:data].to_json + req.headers['Authorization'] = token + end + end + + def put(url, params={}) + puts "[trace][PUT] request params: #{params}" + conn.put do |req| + req.url full_url(url) + req.body = params[:data].to_json + end + end + + def authed_put(token, url, params={}) + puts "[trace][PUT] request params: #{params}" + puts "[trace][PUT] request token: #{token}" + conn.put do |req| + req.url full_url(url) + req.body = params[:data].to_json + req.headers['Authorization'] = token + end + 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 + Trace.trace_config[:base_url] + end + + def domain + Trace.trace_config[: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 "[trace] request url: #{url}" + return url + end + + def log_error(status, body) + puts "[trace] status: #{status}" + puts "[trace] body: #{body}" + end + + def render_response(response) + status = response.status + body = JSON.parse(response&.body) + + log_error(status, body) + + return [body["code"], body["Data"], body["Error"]] + end +end \ No newline at end of file diff --git a/app/services/trace/login_service.rb b/app/services/trace/login_service.rb new file mode 100644 index 00000000..b245ea73 --- /dev/null +++ b/app/services/trace/login_service.rb @@ -0,0 +1,29 @@ +# 代码溯源 用户登录 +class Trace::LoginService < Trace::ClientService + + # 用户名 密码 + attr_accessor :username, :password + + def initialize(username, password) + @username = username + @password = password + end + + def call + result = post(url, request_params) + response = render_response(result) + end + + private + + def request_params + { + username: username, + password: password, + } + end + + def url + "/user/login".freeze + end +end \ No newline at end of file diff --git a/app/services/trace/pdf_report_service.rb b/app/services/trace/pdf_report_service.rb new file mode 100644 index 00000000..e91a78b3 --- /dev/null +++ b/app/services/trace/pdf_report_service.rb @@ -0,0 +1,26 @@ +# 代码溯源 导出pdf +class Trace::PdfReportService < Trace::ClientService + + attr_accessor :token, :task_id + + def initialize(token, task_id) + @token = token + @task_id = task_id + end + + def call + result = authed_get(token, url, request_params) + response = render_response(result) + end + + private + def request_params + { + task_id: task_id + } + end + + def url + "/user/pdfreport".freeze + end +end \ No newline at end of file diff --git a/app/services/trace/reload_check_service.rb b/app/services/trace/reload_check_service.rb new file mode 100644 index 00000000..b2c4a0ff --- /dev/null +++ b/app/services/trace/reload_check_service.rb @@ -0,0 +1,25 @@ +# 代码溯源 重新检测 +class Trace::ReloadCheckService < Trace::ClientService + + attr_accessor :token, :fake_project_id + def initialize(token, fake_project_id) + @token = token + @fake_project_id = fake_project_id + end + + def call + result = authed_post(token, url, request_params) + response = render_response(result) + end + + private + def request_params + { + project_id: fake_project_id + } + end + + def url + '/user/reloadcheck'.freeze + end +end \ No newline at end of file diff --git a/config/configuration.yml.example b/config/configuration.yml.example index b5653012..61e66635 100644 --- a/config/configuration.yml.example +++ b/config/configuration.yml.example @@ -62,6 +62,10 @@ default: &default write_domain: '' read_domain: '' base_url: '' + + trace: + domain: '' + base_url: '' production: <<: *default From 4bbb843edfb6143eaad79e320446ec4e24f2e788 Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 31 Mar 2022 12:54:39 +0800 Subject: [PATCH 005/208] add: trace users and get trace token --- app/models/trace_user.rb | 58 +++++++++++++++++++ app/models/user.rb | 17 ++++++ .../20220331031530_create_trace_users.rb | 17 ++++++ spec/models/trace_user_spec.rb | 5 ++ 4 files changed, 97 insertions(+) create mode 100644 app/models/trace_user.rb create mode 100644 db/migrate/20220331031530_create_trace_users.rb create mode 100644 spec/models/trace_user_spec.rb diff --git a/app/models/trace_user.rb b/app/models/trace_user.rb new file mode 100644 index 00000000..e8964156 --- /dev/null +++ b/app/models/trace_user.rb @@ -0,0 +1,58 @@ +# == Schema Information +# +# Table name: trace_users +# +# id :integer not null, primary key +# user_id :integer +# username :string(255) +# password :string(255) +# unit :string(255) +# telnumber :string(255) +# email :string(255) +# name :string(255) +# token :text(65535) +# expired_at :datetime +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_trace_users_on_user_id (user_id) +# + +# 代码溯源 用户 +class TraceUser < ApplicationRecord + + belongs_to :user + + + def build_self_data + return if user.nil? + + self.username = user.login + self.password = SecureRandom.hex + self.unit = user.custom_department.blank? ? 'GitLink' : user.custom_department + self.telnumber = user.phone.blank? ? '13800000000' : user.phone + self.email = user.mail + self.name = user.nickname.blank? ? user.login : user.nickname + + self + end + + + def build_token + return if username.blank? || password.blank? || unit.blank? || telnumber.blank? || email.blank? || name.blank? + + response = Trace::AddUserService.call(username, password, unit, telnumber, email, name) + self.token = response[1]['token'] + self.expired_at = Time.now + 1.hours + end + + def refresh_token + return if username.blank? || password.blank? || unit.blank? || telnumber.blank? || email.blank? || name.blank? + + response = Trace::LoginService.call(username, password) + self.token = response[1]['token'] + self.expired_at = Time.now + 1.hours + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 5ca3356b..903b60c3 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -173,6 +173,7 @@ class User < Owner has_many :system_notification_histories has_many :system_notifications, through: :system_notification_histories + has_one :trace_user, dependent: :destroy # Groups and active users scope :active, lambda { where(status: STATUS_ACTIVE) } @@ -776,6 +777,22 @@ class User < Owner self.nickname.present? && self.mail.present? end + def trace_token + if trace_user.present? + if trace_user.expired_at < Time.now + trace_user.refresh_token + trace_user.save + end + else + trace_user = TraceUser.new + trace_user.build_self_data + trace_user.build_token + trace_user.save + end + + trace_user.token + end + protected def validate_password_length # 管理员的初始密码是5位 diff --git a/db/migrate/20220331031530_create_trace_users.rb b/db/migrate/20220331031530_create_trace_users.rb new file mode 100644 index 00000000..5c8b532c --- /dev/null +++ b/db/migrate/20220331031530_create_trace_users.rb @@ -0,0 +1,17 @@ +class CreateTraceUsers < ActiveRecord::Migration[5.2] + def change + create_table :trace_users do |t| + t.references :user + t.string :username + t.string :password + t.string :unit + t.string :telnumber + t.string :email + t.string :name + t.text :token + t.datetime :expired_at + + t.timestamps + end + end +end diff --git a/spec/models/trace_user_spec.rb b/spec/models/trace_user_spec.rb new file mode 100644 index 00000000..ddcc25e4 --- /dev/null +++ b/spec/models/trace_user_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe TraceUser, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end From 9ced678f910f2b56d79ae2f1ecc7e638a2083831 Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 31 Mar 2022 13:03:54 +0800 Subject: [PATCH 006/208] fix: user trace token --- app/models/user.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 903b60c3..d661ab59 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -783,14 +783,16 @@ class User < Owner trace_user.refresh_token trace_user.save end + return trace_user.token else - trace_user = TraceUser.new - trace_user.build_self_data - trace_user.build_token - trace_user.save - end + tu = TraceUser.new + tu.user = self + tu.build_self_data + tu.build_token + tu.save - trace_user.token + return tu.token + end end protected From ecfc39565ca4da5c9e9b22958cc962421f4e5826 Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 31 Mar 2022 13:18:38 +0800 Subject: [PATCH 007/208] fix --- app/services/trace/client_service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/services/trace/client_service.rb b/app/services/trace/client_service.rb index ff6b7281..df9a4321 100644 --- a/app/services/trace/client_service.rb +++ b/app/services/trace/client_service.rb @@ -3,7 +3,7 @@ class Trace::ClientService < ApplicationService def post(url, params={}) puts "[trace][POST] request params: #{params}" conn.post do |req| - req.url = full_url(url) + req.url full_url(url) req.body = params[:data].to_json end end @@ -12,7 +12,7 @@ class Trace::ClientService < ApplicationService puts "[trace][POST] request params: #{params}" puts "[trace][POST] request token: #{token}" conn.post do |req| - req.url = full_url(url) + req.url full_url(url) req.body = params[:data].to_json req.headers['Authorization'] = token end From db4031b8bfcd1cfc6664b7eae35c7151e6639665 Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 31 Mar 2022 14:24:47 +0800 Subject: [PATCH 008/208] fix --- app/services/trace/add_user_service.rb | 2 +- app/services/trace/check_service.rb | 2 +- app/services/trace/login_service.rb | 2 +- app/services/trace/reload_check_service.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/services/trace/add_user_service.rb b/app/services/trace/add_user_service.rb index 9c7b12b4..0099f6e9 100644 --- a/app/services/trace/add_user_service.rb +++ b/app/services/trace/add_user_service.rb @@ -14,7 +14,7 @@ class Trace::AddUserService < Trace::ClientService end def call - result = post(url, request_params) + result = post(url, {data: request_params}) response = render_response(result) end diff --git a/app/services/trace/check_service.rb b/app/services/trace/check_service.rb index 841627b5..d31bbcf0 100644 --- a/app/services/trace/check_service.rb +++ b/app/services/trace/check_service.rb @@ -11,7 +11,7 @@ class Trace::CheckService < Trace::ClientService end def call - result = authed_post(token, url, request_params) + result = authed_post(token, url, {data: request_params}) reponse = render_response(result) end diff --git a/app/services/trace/login_service.rb b/app/services/trace/login_service.rb index b245ea73..9f59d8e1 100644 --- a/app/services/trace/login_service.rb +++ b/app/services/trace/login_service.rb @@ -10,7 +10,7 @@ class Trace::LoginService < Trace::ClientService end def call - result = post(url, request_params) + result = post(url, {data: request_params}) response = render_response(result) end diff --git a/app/services/trace/reload_check_service.rb b/app/services/trace/reload_check_service.rb index b2c4a0ff..62530af1 100644 --- a/app/services/trace/reload_check_service.rb +++ b/app/services/trace/reload_check_service.rb @@ -8,7 +8,7 @@ class Trace::ReloadCheckService < Trace::ClientService end def call - result = authed_post(token, url, request_params) + result = authed_post(token, url, {data: request_params}) response = render_response(result) end From 882a50cf56bd54720283bfbdbbd65b9303dc7eb2 Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 31 Mar 2022 16:04:48 +0800 Subject: [PATCH 009/208] add: setting navbar varible --- app/controllers/settings_controller.rb | 12 ++++++++++++ app/views/settings/show.json.jbuilder | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index b2dc2e90..15da47a3 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -1,6 +1,7 @@ class SettingsController < ApplicationController def show @old_projects_url = nil + get_navbar get_add_menu get_common_menu get_personal_menu @@ -8,6 +9,17 @@ class SettingsController < ApplicationController end private + def get_navbar + @navbar = [] + default_laboratory.navbar.each do |site| + hash = {} + site.each {|k, v| + hash.merge!("#{k}": get_site_url(k, v)) + } + @navbar << hash + end + end + def get_add_menu @add = [] Site.add.select(:id, :name, :url, :key).to_a.map(&:serializable_hash).each do |site| diff --git a/app/views/settings/show.json.jbuilder b/app/views/settings/show.json.jbuilder index 0e6e1606..b9319e4e 100644 --- a/app/views/settings/show.json.jbuilder +++ b/app/views/settings/show.json.jbuilder @@ -18,7 +18,7 @@ json.setting do # # end - nav_bar = default_setting.navbar + # nav_bar = default_setting.navbar # if User.current.logged? # nav_bar[2]["link"] = "https://forgeplus.trustie.net/users/#{current_user.login}/projects" @@ -39,7 +39,7 @@ json.setting do json.moop_cases_banner_url default_setting.moop_cases_banner_url&.[](1..-1) json.oj_banner_url default_setting.oj_banner_url&.[](1..-1) - json.navbar nav_bar + json.navbar @navbar json.footer default_setting.footer From 35369189382027f6960f81477f9fc39b5459219c Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 31 Mar 2022 16:18:16 +0800 Subject: [PATCH 010/208] fix --- app/controllers/settings_controller.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index 15da47a3..e1a5be1d 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -10,13 +10,10 @@ class SettingsController < ApplicationController private def get_navbar - @navbar = [] - default_laboratory.navbar.each do |site| - hash = {} - site.each {|k, v| - hash.merge!("#{k}": get_site_url(k, v)) - } - @navbar << hash + @navbar = default_laboratory.navbar + if User.current.logged? + pernal_index = {"name"=>"个人主页", "link"=>get_site_url("link", "/current_user"), "hidden"=>false} + @navbar << pernal_index end end From 70559744432c9a2b109b42d93e00f7a432f1ca3b Mon Sep 17 00:00:00 2001 From: yystopf Date: Wed, 6 Apr 2022 17:04:31 +0800 Subject: [PATCH 011/208] fix --- app/services/issues/list_query_service.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/services/issues/list_query_service.rb b/app/services/issues/list_query_service.rb index def9644d..73f0b438 100644 --- a/app/services/issues/list_query_service.rb +++ b/app/services/issues/list_query_service.rb @@ -27,9 +27,9 @@ class Issues::ListQueryService < ApplicationService issues = issues.joins(:pull_request).where(pull_requests: {status: 1}) end - # if search_name.present? - # issues = issues.where("issues.subject LIKE ? OR issues.description LIKE ? ", "%#{search_name}%", "%#{search_name}%") - # end + if search_name.present? + issues = issues.where("issues.subject LIKE ? OR issues.description LIKE ? ", "%#{search_name}%", "%#{search_name}%") + end if start_time&.present? || end_time&.present? issues = issues.where("issues.start_date between ? and ?",start_time&.present? ? start_time.to_date : Time.now.to_date, end_time&.present? ? end_time.to_date : Time.now.to_date) From dec264d1693bbb70f572f7e61458570e44251901 Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 7 Apr 2022 16:28:40 +0800 Subject: [PATCH 012/208] fix: sync user pwd --- app/controllers/concerns/login_helper.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/controllers/concerns/login_helper.rb b/app/controllers/concerns/login_helper.rb index d8f2445f..e608b783 100644 --- a/app/controllers/concerns/login_helper.rb +++ b/app/controllers/concerns/login_helper.rb @@ -108,7 +108,11 @@ module LoginHelper def sync_pwd_to_gitea!(user, hash={}) return true if user.is_sync_pwd? - sync_params = { email: user.mail } + sync_params = { + login_name: user.name, + source_id: 0, + email: user.mail + } interactor = Gitea::User::UpdateInteractor.call(user.login, sync_params.merge(hash)) if interactor.success? Rails.logger.info "########_ login is #{user.login} sync_pwd_to_gitea success _########" From 551f333261895546a1e7eeacf0af46bd74a5569d Mon Sep 17 00:00:00 2001 From: yystopf Date: Fri, 8 Apr 2022 14:52:56 +0800 Subject: [PATCH 013/208] fix --- app/controllers/settings_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index e1a5be1d..80edd1e5 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -12,7 +12,7 @@ class SettingsController < ApplicationController def get_navbar @navbar = default_laboratory.navbar if User.current.logged? - pernal_index = {"name"=>"个人主页", "link"=>get_site_url("link", "/current_user"), "hidden"=>false} + pernal_index = {"name"=>"个人主页", "link"=>get_site_url("url", "/current_user"), "hidden"=>false} @navbar << pernal_index end end From eac7331070371acb0599745d204edbea01b506ba Mon Sep 17 00:00:00 2001 From: yystopf Date: Fri, 8 Apr 2022 15:03:40 +0800 Subject: [PATCH 014/208] fix: settings need prefix --- app/controllers/settings_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index 80edd1e5..95224353 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -12,7 +12,7 @@ class SettingsController < ApplicationController def get_navbar @navbar = default_laboratory.navbar if User.current.logged? - pernal_index = {"name"=>"个人主页", "link"=>get_site_url("url", "/current_user"), "hidden"=>false} + pernal_index = {"name"=>"个人主页", "link"=>get_site_url("url", "#{Rails.application.config_for(:configuration)['platform_url']}/current_user"), "hidden"=>false} @navbar << pernal_index end end From 910ae4855688441b48414d1a635462a7043c51ec Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 11 Apr 2022 10:50:18 +0800 Subject: [PATCH 015/208] fix: exception to ex --- app/libs/notice.rb | 2 +- app/libs/trace.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/libs/notice.rb b/app/libs/notice.rb index 93d5cb42..18fdb070 100644 --- a/app/libs/notice.rb +++ b/app/libs/notice.rb @@ -7,7 +7,7 @@ module Notice config = Rails.application.config_for(:configuration).symbolize_keys! notice_config = config[:notice].symbolize_keys! raise 'notice config missing' if notice_config.blank? - rescue => exception + rescue => ex raise ex if Rails.env.production? puts %Q{\033[33m [warning] gitea config or configuration.yml missing, diff --git a/app/libs/trace.rb b/app/libs/trace.rb index b1c3c476..3ec3b56a 100644 --- a/app/libs/trace.rb +++ b/app/libs/trace.rb @@ -7,7 +7,7 @@ module Trace config = Rails.application.config_for(:configuration).symbolize_keys! trace_config = config[:trace].symbolize_keys! raise 'trace config missing' if trace_config.blank? - rescue => exception + rescue => ex raise ex if Rails.env.production? puts %Q{\033[33m [warning] gitea config or configuration.yml missing, From 0076468a150eb7e9ac9f63f31eb9b89509bdcd54 Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 11 Apr 2022 15:30:28 +0800 Subject: [PATCH 016/208] fix: trace client request data --- app/services/trace/client_service.rb | 56 +++++++--------------------- 1 file changed, 13 insertions(+), 43 deletions(-) diff --git a/app/services/trace/client_service.rb b/app/services/trace/client_service.rb index df9a4321..298c2cf0 100644 --- a/app/services/trace/client_service.rb +++ b/app/services/trace/client_service.rb @@ -2,20 +2,14 @@ class Trace::ClientService < ApplicationService def post(url, params={}) puts "[trace][POST] request params: #{params}" - conn.post do |req| - req.url full_url(url) - req.body = params[:data].to_json - end + conn.post(full_url(url), params[:data]) end def authed_post(token, url, params={}) puts "[trace][POST] request params: #{params}" puts "[trace][POST] request token: #{token}" - conn.post do |req| - req.url full_url(url) - req.body = params[:data].to_json - req.headers['Authorization'] = token - end + conn.headers['Authorization'] = token + conn.post(full_url(url), params[:data]) end def get(url, params={}) @@ -42,67 +36,43 @@ class Trace::ClientService < ApplicationService def delete(url, params={}) puts "[trace][DELETE] request params: #{params}" - conn.delete do |req| - req.url full_url(url) - req.body = params[:data].to_json - end + conn.delete(full_url(url), params[:data]) end def authed_delete(token, url, params={}) puts "[trace][DELETE] request params: #{params}" puts "[trace][DELETE] request token: #{token}" - conn.delete do |req| - req.url full_url(url) - req.body = params[:data].to_json - req.headers['Authorization'] = token - end + conn.headers['Authorization'] = token + conn.delete(full_url(url), params[:data]) end def patch(url, params={}) puts "[trace][PATCH] request params: #{params}" - conn.patch do |req| - req.url full_url(url) - req.body = params[:data].to_json - end + conn.patch(full_url(url), params[:data]) end def authed_patch(token, url, params={}) puts "[trace][PATCH] request params: #{params}" puts "[trace][PATCH] request token: #{token}" - conn.patch do |req| - req.url full_url(url) - req.body = params[:data].to_json - req.headers['Authorization'] = token - end + conn.headers['Authorization'] = token + conn.patch(full_url(url), params[:data]) end def put(url, params={}) puts "[trace][PUT] request params: #{params}" - conn.put do |req| - req.url full_url(url) - req.body = params[:data].to_json - end + conn.put(full_url(url), params[:data]) end def authed_put(token, url, params={}) puts "[trace][PUT] request params: #{params}" puts "[trace][PUT] request token: #{token}" - conn.put do |req| - req.url full_url(url) - req.body = params[:data].to_json - req.headers['Authorization'] = token - end + conn.headers['Authorization'] = token + conn.put(full_url(url), params[:data]) 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 ||= Faraday.new(url: domain) @client end From 720c073f32a8f616ee35b2bf9e208a3ae00a8445 Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 11 Apr 2022 15:48:47 +0800 Subject: [PATCH 017/208] fix --- app/services/trace/client_service.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/services/trace/client_service.rb b/app/services/trace/client_service.rb index 298c2cf0..6dbe87d5 100644 --- a/app/services/trace/client_service.rb +++ b/app/services/trace/client_service.rb @@ -70,11 +70,8 @@ class Trace::ClientService < ApplicationService conn.put(full_url(url), params[:data]) end - private def conn - @client ||= Faraday.new(url: domain) - - @client + Faraday.new(url: domain) end def base_url From 2dfd69ae212ffa4e164c920073a22d2b30380ec0 Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 11 Apr 2022 16:08:50 +0800 Subject: [PATCH 018/208] fix: response data result --- app/services/trace/client_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/trace/client_service.rb b/app/services/trace/client_service.rb index 6dbe87d5..72ffa8ca 100644 --- a/app/services/trace/client_service.rb +++ b/app/services/trace/client_service.rb @@ -105,6 +105,6 @@ class Trace::ClientService < ApplicationService log_error(status, body) - return [body["code"], body["Data"], body["Error"]] + return [body["code"], body["data"], body["error"]] end end \ No newline at end of file From 8f5bb0e2d9b8c146cb42f8f84e642eb59c5344b5 Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 18 Apr 2022 10:52:48 +0800 Subject: [PATCH 019/208] fix: organization gitea_token read by associations --- app/models/organization.rb | 4 ++++ app/services/organizations/create_service.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/models/organization.rb b/app/models/organization.rb index 40c676e0..56351a41 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -83,6 +83,10 @@ class Organization < Owner after_save :reset_cache_data + def gitea_token + team_users.joins(:team).where(teams: {authorize: "owner"}).take&.user&.gitea_token + end + def reset_cache_data Cache::V2::OwnerCommonService.new(self.id).reset end diff --git a/app/services/organizations/create_service.rb b/app/services/organizations/create_service.rb index a430ed6f..26377919 100644 --- a/app/services/organizations/create_service.rb +++ b/app/services/organizations/create_service.rb @@ -47,7 +47,7 @@ class Organizations::CreateService < ApplicationService end def create_org_and_extension - @organization = Organization.build(params[:name], params[:nickname], user.gitea_token) + @organization = Organization.build(params[:name], params[:nickname]) org_extension = OrganizationExtension.build(organization.id, description, website, location, repo_admin_change_team_access, visibility, max_repo_creation) From fc3cf01d3f6f730d9ae82b5da0a9ee66164d50ed Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 18 Apr 2022 13:49:08 +0800 Subject: [PATCH 020/208] add: get user image action --- app/controllers/users_controller.rb | 7 +++++++ app/helpers/application_helper.rb | 9 +++++++++ app/views/users/get_user_info.json.jbuilder | 2 +- config/routes.rb | 1 + 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 00ea926f..acc82661 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -97,6 +97,13 @@ class UsersController < ApplicationController render_error(-1, '头像修改失败!') end + def get_image + return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id]) + return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id) + + redirect_to Rails.application.config_for(:configuration)['platform_url'] + "/" + url_to_avatar(@user).to_s + end + def me @user = current_user end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index f2a9f93b..e7f8611e 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -147,6 +147,15 @@ module ApplicationHelper end end + def url_to_avatar_with_platform_url(source) + platform_url = Rails.application.config_for(:configuration)['platform_url'] + if platform_url + return Rails.application.config_for(:configuration)['platform_url'] + "/" + url_to_avatar(source).to_s + else + return url_to_avatar(source).to_s + end + end + # 主页banner图 def banner_img(source_type) if File.exist?(disk_filename(source_type, "banner")) diff --git a/app/views/users/get_user_info.json.jbuilder b/app/views/users/get_user_info.json.jbuilder index 2f96c696..5fa4df16 100644 --- a/app/views/users/get_user_info.json.jbuilder +++ b/app/views/users/get_user_info.json.jbuilder @@ -22,4 +22,4 @@ json.city @user.city json.custom_department @user.custom_department json.description @user.description json.(@user, :show_email, :show_department, :show_location) -json.message_unread_total @message_unread_total +json.message_unread_total @message_unread_total diff --git a/config/routes.rb b/config/routes.rb index 7e280820..da63dab6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -214,6 +214,7 @@ Rails.application.routes.draw do get :fan_users get :hovercard put :update_image + get :get_image end collection do post :following From 088c617cc12be188a51c5840d82cfbc237e741ce Mon Sep 17 00:00:00 2001 From: yystopf Date: Tue, 26 Apr 2022 17:15:59 +0800 Subject: [PATCH 021/208] add: name query for branches --- app/controllers/projects_controller.rb | 2 +- app/controllers/pull_requests_controller.rb | 4 ++-- app/controllers/version_releases_controller.rb | 2 +- app/services/branches/list_service.rb | 7 ++++--- .../gitea/repository/branches/list_name_service.rb | 7 ++++--- app/services/gitea/repository/branches/list_service.rb | 7 ++++--- 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index be9a93d9..b14b105b 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -88,7 +88,7 @@ class ProjectsController < ApplicationController return @branches = [] unless @project.forge? # result = Gitea::Repository::Branches::ListService.call(@owner, @project.identifier) - result = Gitea::Repository::Branches::ListNameService.call(@owner, @project.identifier) + result = Gitea::Repository::Branches::ListNameService.call(@owner, @project.identifier, params[:name]) @branches = result.is_a?(Hash) ? (result.key?(:status) ? [] : result["branch_name"]) : result end diff --git a/app/controllers/pull_requests_controller.rb b/app/controllers/pull_requests_controller.rb index 189c5c6b..0ac7a714 100644 --- a/app/controllers/pull_requests_controller.rb +++ b/app/controllers/pull_requests_controller.rb @@ -29,7 +29,7 @@ class PullRequestsController < ApplicationController end def new - @all_branches = Branches::ListService.call(@owner, @project) + @all_branches = Branches::ListService.call(@owner, @project, params[:branch_name]) @is_fork = @project.forked_from_project_id.present? @projects_names = [{ project_user_login: @owner.try(:login), @@ -50,7 +50,7 @@ class PullRequestsController < ApplicationController end def get_branches - branch_result = Branches::ListService.call(@owner, @project) + branch_result = Branches::ListService.call(@owner, @project, params[:name]) render json: branch_result # return json: branch_result end diff --git a/app/controllers/version_releases_controller.rb b/app/controllers/version_releases_controller.rb index 2d7546a1..dd59098f 100644 --- a/app/controllers/version_releases_controller.rb +++ b/app/controllers/version_releases_controller.rb @@ -14,7 +14,7 @@ class VersionReleasesController < ApplicationController def new #获取所有的分支 @all_branches = [] - get_all_branches = Gitea::Repository::Branches::ListService.new(@user, @repository.try(:identifier)).call + get_all_branches = Gitea::Repository::Branches::ListService.new(@user, @repository.try(:identifier), params[:branch_name]).call if get_all_branches && get_all_branches.size > 0 get_all_branches.each do |b| @all_branches.push(b["name"]) diff --git a/app/services/branches/list_service.rb b/app/services/branches/list_service.rb index a3b77a7b..7b47658e 100644 --- a/app/services/branches/list_service.rb +++ b/app/services/branches/list_service.rb @@ -1,17 +1,18 @@ class Branches::ListService < ApplicationService - attr_reader :user, :project + attr_reader :user, :project, :name - def initialize(user, project) + def initialize(user, project, name=nil) @user = user @project = project + @name = name end def call all_branches = [] user_name = user.try(:show_real_name) identifier = project.repository.try(:identifier) - get_all_branches = Gitea::Repository::Branches::ListService.new(user, identifier).call + get_all_branches = Gitea::Repository::Branches::ListService.new(user, identifier, name).call all_branches = branch_lists(user_name,user.try(:login), identifier, get_all_branches) if get_all_branches && get_all_branches.size > 0 return all_branches end diff --git a/app/services/gitea/repository/branches/list_name_service.rb b/app/services/gitea/repository/branches/list_name_service.rb index 716ee464..c005c835 100644 --- a/app/services/gitea/repository/branches/list_name_service.rb +++ b/app/services/gitea/repository/branches/list_name_service.rb @@ -1,9 +1,10 @@ class Gitea::Repository::Branches::ListNameService < Gitea::ClientService - attr_reader :user, :repo + attr_reader :user, :repo, :name - def initialize(user, repo) + def initialize(user, repo, name=nil) @user = user @repo = repo + @name = name end def call @@ -13,7 +14,7 @@ class Gitea::Repository::Branches::ListNameService < Gitea::ClientService private def params - Hash.new.merge(token: user.gitea_token) + Hash.new.merge(token: user.gitea_token, name: name) end def url diff --git a/app/services/gitea/repository/branches/list_service.rb b/app/services/gitea/repository/branches/list_service.rb index f4296509..7722ecd0 100644 --- a/app/services/gitea/repository/branches/list_service.rb +++ b/app/services/gitea/repository/branches/list_service.rb @@ -1,9 +1,10 @@ class Gitea::Repository::Branches::ListService < Gitea::ClientService - attr_reader :user, :repo + attr_reader :user, :repo, :name - def initialize(user, repo) + def initialize(user, repo, name=nil) @user = user @repo = repo + @name = name end def call @@ -13,7 +14,7 @@ class Gitea::Repository::Branches::ListService < Gitea::ClientService private def params - Hash.new.merge(token: user.gitea_token) + Hash.new.merge(token: user.gitea_token, name: name) end def url From 396ac55429d37e72d56fd505258e9900f2c4f7e7 Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 28 Apr 2022 10:42:55 +0800 Subject: [PATCH 022/208] add: user super description --- app/controllers/users_controller.rb | 1 + app/models/user.rb | 2 +- app/models/user_extension.rb | 57 +++++++++---------- app/views/users/get_user_info.json.jbuilder | 3 +- app/views/users/show.json.jbuilder | 1 + ...dd_super_description_to_user_extensions.rb | 6 ++ 6 files changed, 39 insertions(+), 31 deletions(-) create mode 100644 db/migrate/20220428015313_add_super_description_to_user_extensions.rb diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index acc82661..532f29ba 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -313,6 +313,7 @@ class UsersController < ApplicationController :occupation, :technical_title, :school_id, :department_id, :province, :city, :custom_department, :identity, :student_id, :description, + :show_super_description, :super_description, :show_email, :show_location, :show_department] ) end diff --git a/app/models/user.rb b/app/models/user.rb index d661ab59..fa940c29 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -187,7 +187,7 @@ class User < Owner attr_accessor :password, :password_confirmation delegate :description, :gender, :department_id, :school_id, :location, :location_city, - :show_email, :show_location, :show_department, + :show_email, :show_location, :show_department, :super_description, :show_super_description, :technical_title, :province, :city, :custom_department, to: :user_extension, allow_nil: true before_save :update_hashed_password, :set_lastname diff --git a/app/models/user_extension.rb b/app/models/user_extension.rb index ee208af7..379243bf 100644 --- a/app/models/user_extension.rb +++ b/app/models/user_extension.rb @@ -2,35 +2,34 @@ # # Table name: user_extensions # -# id :integer not null, primary key -# user_id :integer not null -# birthday :date -# brief_introduction :string(255) -# gender :integer -# location :string(255) -# occupation :string(255) -# work_experience :integer -# zip_code :integer -# created_at :datetime not null -# updated_at :datetime not null -# technical_title :string(255) -# identity :integer -# student_id :string(255) -# teacher_realname :string(255) -# student_realname :string(255) -# location_city :string(255) -# school_id :integer -# description :string(255) default("") -# department_id :integer -# honor :text(65535) -# edu_background :integer -# edu_entry_year :integer -# province :string(255) -# city :string(255) -# custom_department :string(255) -# show_email :boolean default("0") -# show_location :boolean default("0") -# show_department :boolean default("0") +# id :integer not null, primary key +# user_id :integer not null +# birthday :date +# brief_introduction :string(255) +# gender :integer +# location :string(255) +# occupation :string(255) +# work_experience :integer +# zip_code :integer +# created_at :datetime not null +# updated_at :datetime not null +# technical_title :string(255) +# identity :integer +# student_id :string(255) +# teacher_realname :string(255) +# student_realname :string(255) +# location_city :string(255) +# school_id :integer +# description :string(255) default("") +# department_id :integer +# province :string(255) +# city :string(255) +# custom_department :string(255) +# show_email :boolean default("0") +# show_location :boolean default("0") +# show_department :boolean default("0") +# super_description :text(4294967295) +# show_super_description :boolean # # Indexes # diff --git a/app/views/users/get_user_info.json.jbuilder b/app/views/users/get_user_info.json.jbuilder index 5fa4df16..8fcadb1c 100644 --- a/app/views/users/get_user_info.json.jbuilder +++ b/app/views/users/get_user_info.json.jbuilder @@ -21,5 +21,6 @@ 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) +json.super_description @user.super_description +json.(@user, :show_email, :show_department, :show_location, :show_super_description) json.message_unread_total @message_unread_total diff --git a/app/views/users/show.json.jbuilder b/app/views/users/show.json.jbuilder index 1c9e7793..f35705ce 100644 --- a/app/views/users/show.json.jbuilder +++ b/app/views/users/show.json.jbuilder @@ -13,4 +13,5 @@ json.email @user.show_email ? @user.mail : nil json.province @user.show_location ? @user.province : nil json.city @user.show_location ? @user.city : nil json.custom_department @user.show_department ? @user.custom_department : nil +json.super_description @user.show_super_description ? @user.super_description : nil json.description @user.description \ No newline at end of file diff --git a/db/migrate/20220428015313_add_super_description_to_user_extensions.rb b/db/migrate/20220428015313_add_super_description_to_user_extensions.rb new file mode 100644 index 00000000..07651677 --- /dev/null +++ b/db/migrate/20220428015313_add_super_description_to_user_extensions.rb @@ -0,0 +1,6 @@ +class AddSuperDescriptionToUserExtensions < ActiveRecord::Migration[5.2] + def change + add_column :user_extensions, :super_description, :text, :limit => 4294967295 + add_column :user_extensions, :show_super_description, :boolean + end +end From ec3a556a6f31fb162d47db88c88ba0e3f99f2c34 Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 28 Apr 2022 11:03:42 +0800 Subject: [PATCH 023/208] fix: default value for show super description --- app/models/user_extension.rb | 2 +- .../20220428015313_add_super_description_to_user_extensions.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/user_extension.rb b/app/models/user_extension.rb index 379243bf..c1046ac2 100644 --- a/app/models/user_extension.rb +++ b/app/models/user_extension.rb @@ -29,7 +29,7 @@ # show_location :boolean default("0") # show_department :boolean default("0") # super_description :text(4294967295) -# show_super_description :boolean +# show_super_description :boolean default("0") # # Indexes # diff --git a/db/migrate/20220428015313_add_super_description_to_user_extensions.rb b/db/migrate/20220428015313_add_super_description_to_user_extensions.rb index 07651677..3e00416e 100644 --- a/db/migrate/20220428015313_add_super_description_to_user_extensions.rb +++ b/db/migrate/20220428015313_add_super_description_to_user_extensions.rb @@ -1,6 +1,6 @@ class AddSuperDescriptionToUserExtensions < ActiveRecord::Migration[5.2] def change add_column :user_extensions, :super_description, :text, :limit => 4294967295 - add_column :user_extensions, :show_super_description, :boolean + add_column :user_extensions, :show_super_description, :boolean, default: false end end From 3abd6037d99e16931f6ad44ece25d004ec28cf9e Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 28 Apr 2022 11:12:16 +0800 Subject: [PATCH 024/208] fix: show super description aways desplay --- app/views/users/show.json.jbuilder | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/users/show.json.jbuilder b/app/views/users/show.json.jbuilder index f35705ce..dfeb45cf 100644 --- a/app/views/users/show.json.jbuilder +++ b/app/views/users/show.json.jbuilder @@ -14,4 +14,5 @@ json.province @user.show_location ? @user.province : nil json.city @user.show_location ? @user.city : nil json.custom_department @user.show_department ? @user.custom_department : nil json.super_description @user.show_super_description ? @user.super_description : nil +json.show_super_description @user.show_super_description json.description @user.description \ No newline at end of file From f15cd44dff87a2df8072133140fa7f2c787a4c13 Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 28 Apr 2022 18:53:33 +0800 Subject: [PATCH 025/208] add: compare add paginate --- app/controllers/compare_controller.rb | 12 ++++++++++++ app/helpers/repositories_helper.rb | 1 - app/views/compare/show.json.jbuilder | 4 ++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/controllers/compare_controller.rb b/app/controllers/compare_controller.rb index 382e7506..1aab7adb 100644 --- a/app/controllers/compare_controller.rb +++ b/app/controllers/compare_controller.rb @@ -9,6 +9,10 @@ class CompareController < ApplicationController load_compare_params compare @merge_status, @merge_message = get_merge_message + @page_size = page_size <= 0 ? 1 : page_size + @page_limit = page_limit <=0 ? 15 : page_limit + @page_offset = (@page_size -1) * @page_limit + Rails.logger.info("+========#{@page_size}-#{@page_limit}-#{@page_offset}") end private @@ -53,4 +57,12 @@ class CompareController < ApplicationController def gitea_compare(base, head) Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, Addressable::URI.escape(base), Addressable::URI.escape(head), current_user.gitea_token) end + + def page_size + params.fetch(:page, 1).to_i + end + + def page_limit + params.fetch(:limit, 15).to_i + end end diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index e6e7e87f..b8bfc31c 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -36,7 +36,6 @@ module RepositoriesHelper end def render_cache_commit_author(author_json) - Rails.logger.info author_json['Email'] if author_json["name"].present? && author_json["email"].present? return find_user_in_redis_cache(author_json['name'], author_json['email']) end diff --git a/app/views/compare/show.json.jbuilder b/app/views/compare/show.json.jbuilder index deca4103..26a7cb76 100644 --- a/app/views/compare/show.json.jbuilder +++ b/app/views/compare/show.json.jbuilder @@ -1,7 +1,7 @@ -json.commits_count @compare_result['Commits']&.size +json.commits_count @compare_result['CommitsCount'] # json.commits @compare_result['Commits'], partial: 'pull_requests/commit', as: :commit json.commits do - json.array! @compare_result['Commits'] do |commit| + json.array! @compare_result['Commits'][@page_offset...(@page_offset + @page_limit)] do |commit| json.author do json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['Committer']), name: commit['Committer']['Name'] } end From 7c88ea3d6713065f29421d025288947a5194b11b Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 5 May 2022 10:00:02 +0800 Subject: [PATCH 026/208] add: quit repo --- app/controllers/projects_controller.rb | 21 ++++++++++++++++++++- config/routes.rb | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index b14b105b..c4e440af 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -13,6 +13,8 @@ class ProjectsController < ApplicationController def menu_list menu = [] + user_is_admin = current_user.admin? || @project.manager?(current_user) + menu.append(menu_hash_by_name("home")) menu.append(menu_hash_by_name("code")) if @project.has_menu_permission("code") menu.append(menu_hash_by_name("issues")) if @project.has_menu_permission("issues") @@ -22,7 +24,8 @@ class ProjectsController < ApplicationController menu.append(menu_hash_by_name("wiki")) if @project.has_menu_permission("wiki") && @project.forge? menu.append(menu_hash_by_name("resources")) if @project.has_menu_permission("resources") && @project.forge? menu.append(menu_hash_by_name("activity")) - menu.append(menu_hash_by_name("settings")) if (current_user.admin? || @project.manager?(current_user)) && @project.forge? + menu.append(menu_hash_by_name("settings")) if user_is_admin && @project.forge? + menu.append(menu_hash_by_name("quit")) if !user_is_admin && @project.member(current_user.id) && @project.forge? render json: menu end @@ -177,6 +180,22 @@ class ProjectsController < ApplicationController tip_exception(e.message) end + def quit + user_is_admin = current_user.admin? || @project.manager?(current_user) + if !user_is_admin && @project.member(current_user.id) && @project.forge? + ActiveRecord::Base.transaction do + Projects::DeleteMemberInteractor.call(@project.owner, @project, current_user) + SendTemplateMessageJob.perform_later('ProjectMemberLeft', current_user.id, current_user.id, @project.id) if Site.has_notice_menu? + render_ok + end + else + render_forbidden('你不能退出该仓库') + end + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + def watch_users watchers = @project.watchers.includes(:user).order("watchers.created_at desc").distinct @watchers_count = watchers.size diff --git a/config/routes.rb b/config/routes.rb index da63dab6..01ff4fa3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -436,6 +436,7 @@ Rails.application.routes.draw do get :stargazers, to: 'projects#praise_users' get :forks, to: 'projects#fork_users' match :about, :via => [:get, :put, :post] + post :quit end end From 55c74de8e46c59ec353593fe7bd92543c2688d26 Mon Sep 17 00:00:00 2001 From: xiaoxiaoqiong Date: Thu, 5 May 2022 10:41:02 +0800 Subject: [PATCH 027/208] =?UTF-8?q?fork=5Fusers=E5=8A=A0=E5=85=A5=E7=BB=84?= =?UTF-8?q?=E7=BB=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/fork_user.rb | 2 +- app/services/pull_requests/create_service.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/fork_user.rb b/app/models/fork_user.rb index bddf8f75..4177392e 100644 --- a/app/models/fork_user.rb +++ b/app/models/fork_user.rb @@ -17,7 +17,7 @@ class ForkUser < ApplicationRecord belongs_to :project - belongs_to :user + belongs_to :owner belongs_to :fork_project, class_name: 'Project', foreign_key: :fork_project_id after_create :incre_project_common, :incre_user_statistic, :incre_platform_statistic diff --git a/app/services/pull_requests/create_service.rb b/app/services/pull_requests/create_service.rb index 703bb5de..258d0e31 100644 --- a/app/services/pull_requests/create_service.rb +++ b/app/services/pull_requests/create_service.rb @@ -157,7 +157,7 @@ class PullRequests::CreateService < ApplicationService raise "head参数不能为空" if @params[:head].blank? raise "base参数不能为空" if @params[:base].blank? raise "fork_project_id参数错误" if is_original && !@project.forked_projects.pluck(:id).include?(@params[:fork_project_id].to_i) - raise "merge_user_login参数错误" if is_original && @project.fork_users.joins(:user).where(users: {login: @params[:merge_user_login]}).blank? + raise "merge_user_login参数错误" if is_original && @project.fork_users.joins(:owner).where(users: {login: @params[:merge_user_login]}).blank? raise "分支内容相同,无需创建合并请求" if @params[:head] === @params[:base] && !is_original raise "合并请求已存在" if @project&.pull_requests.where(head: @params[:head], base: @params[:base], status: 0, is_original: is_original, fork_project_id: @params[:fork_project_id]).present? raise @pull_issue.errors.full_messages.join(", ") unless pull_issue.valid? From ae7d0d13296e595a9a3e2fa52b627d6d41d023f0 Mon Sep 17 00:00:00 2001 From: yystopf Date: Fri, 6 May 2022 09:56:22 +0800 Subject: [PATCH 028/208] add: issues description and journals notes validate --- app/controllers/issues_controller.rb | 4 ++-- app/controllers/journals_controller.rb | 11 +++++++++-- app/forms/issues/create_form.rb | 4 ++-- app/forms/issues/update_form.rb | 4 +++- app/forms/journals/create_form.rb | 7 +++++++ app/forms/journals/update_form.rb | 8 ++++++++ config/locales/forms/create_issuse_form.zh-CN.yml | 8 +++++++- 7 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 app/forms/journals/create_form.rb create mode 100644 app/forms/journals/update_form.rb diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index b546ea71..e21c087b 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -109,7 +109,7 @@ class IssuesController < ApplicationController def create issue_params = issue_send_params(params) - Issues::CreateForm.new({subject:issue_params[:subject]}).validate! + Issues::CreateForm.new(issue_params.slice(:subject, :description)).validate! @issue = Issue.new(issue_params) if @issue.save! SendTemplateMessageJob.perform_later('IssueAssigned', current_user.id, @issue&.id) if Site.has_notice_menu? @@ -223,7 +223,7 @@ class IssuesController < ApplicationController normal_status(-1, "不允许修改为关闭状态") else issue_params = issue_send_params(params).except(:issue_classify, :author_id, :project_id) - Issues::UpdateForm.new({subject:issue_params[:subject]}).validate! + Issues::UpdateForm.new(issue_params.slice(:subject, :description)).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)) if Site.has_notice_menu? diff --git a/app/controllers/journals_controller.rb b/app/controllers/journals_controller.rb index 8fbe4692..2dd1fef6 100644 --- a/app/controllers/journals_controller.rb +++ b/app/controllers/journals_controller.rb @@ -23,6 +23,7 @@ class JournalsController < ApplicationController normal_status(-1, "评论内容不能为空") else ActiveRecord::Base.transaction do + Journals::CreateForm.new({notes: notes.to_s.strip}).validate! journal_params = { journalized_id: @issue.id , journalized_type: "Issue", @@ -53,6 +54,9 @@ class JournalsController < ApplicationController end end end + rescue Exception => exception + puts exception.message + normal_status(-1, exception.message) end def destroy @@ -70,7 +74,8 @@ class JournalsController < ApplicationController def update content = params[:content] - if content.present? + if content.present? + Journals::UpdateForm.new({notes: notes.to_s.strip}).validate! if @journal.update_attribute(:notes, content) normal_status(0, "更新成功") else @@ -79,7 +84,9 @@ class JournalsController < ApplicationController else normal_status(-1, "评论的内容不能为空") end - + rescue Exception => exception + puts exception.message + normal_status(-1, exception.message) end def get_children_journals diff --git a/app/forms/issues/create_form.rb b/app/forms/issues/create_form.rb index 602775ff..7ab942bc 100644 --- a/app/forms/issues/create_form.rb +++ b/app/forms/issues/create_form.rb @@ -1,11 +1,11 @@ class Issues::CreateForm include ActiveModel::Model - attr_accessor :subject + attr_accessor :subject, :description validates :subject, presence: { message: "不能为空" } validates :subject, length: { maximum: 200, too_long: "不能超过200个字符" } - + validates :description, length: { maximum: 65535, too_long: "不能超过65535个字符"} end diff --git a/app/forms/issues/update_form.rb b/app/forms/issues/update_form.rb index 64acdfb5..1aa971e9 100644 --- a/app/forms/issues/update_form.rb +++ b/app/forms/issues/update_form.rb @@ -1,10 +1,12 @@ class Issues::UpdateForm include ActiveModel::Model - attr_accessor :subject + attr_accessor :subject, :description validates :subject, presence: { message: "不能为空" } validates :subject, length: { maximum: 200, too_long: "不能超过200个字符" } + validates :description, length: { maximum: 65535, too_long: "不能超过65535个字符"} + end \ No newline at end of file diff --git a/app/forms/journals/create_form.rb b/app/forms/journals/create_form.rb new file mode 100644 index 00000000..8b96bc64 --- /dev/null +++ b/app/forms/journals/create_form.rb @@ -0,0 +1,7 @@ +class Journals::CreateForm + include ActiveModel::Model + + attr_accessor :notes + + validates :notes, length: { maximum: 65535, too_long: "不能超过65535个字符"} +end diff --git a/app/forms/journals/update_form.rb b/app/forms/journals/update_form.rb new file mode 100644 index 00000000..74f8fa6f --- /dev/null +++ b/app/forms/journals/update_form.rb @@ -0,0 +1,8 @@ +class Journals::UpdateForm + include ActiveModel::Model + + attr_accessor :notes + + validates :notes, length: { maximum: 65535, too_long: "不能超过65535个字符"} + +end \ No newline at end of file diff --git a/config/locales/forms/create_issuse_form.zh-CN.yml b/config/locales/forms/create_issuse_form.zh-CN.yml index 643c6866..bebab7e2 100644 --- a/config/locales/forms/create_issuse_form.zh-CN.yml +++ b/config/locales/forms/create_issuse_form.zh-CN.yml @@ -3,5 +3,11 @@ attributes: issues/create_form: subject: 标题 + description: 描述 issues/update_form: - subject: 标题 \ No newline at end of file + subject: 标题 + description: 描述 + journals/create_form: + notes: 评论 + journals/update_form: + notes: 评论 \ No newline at end of file From 6aa3321d17e33a563a373e0c2d0bf32ff73216f1 Mon Sep 17 00:00:00 2001 From: yystopf Date: Fri, 6 May 2022 14:29:05 +0800 Subject: [PATCH 029/208] fix: pr title description validate --- app/controllers/pull_requests_controller.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/pull_requests_controller.rb b/app/controllers/pull_requests_controller.rb index 0ac7a714..bb040784 100644 --- a/app/controllers/pull_requests_controller.rb +++ b/app/controllers/pull_requests_controller.rb @@ -58,6 +58,7 @@ class PullRequestsController < ApplicationController def create # return normal_status(-1, "您不是目标分支开发者,没有权限,请联系目标分支作者.") unless @project.operator?(current_user) ActiveRecord::Base.transaction do + Issues::CreateForm.new({subject: params[:title], description: params}).validate! @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"]) @@ -89,7 +90,7 @@ class PullRequestsController < ApplicationController else ActiveRecord::Base.transaction do begin - return normal_status(-1, "title不能超过255个字符") if params[:title].length > 255 + Issues::UpdateForm.new({subject: params[:title], description: params[:body]}).validate! merge_params @issue&.issue_tags_relates&.destroy_all if params[:issue_tag_ids].blank? From 000197b0d1b723932fbd47bec1fb866cf8449d66 Mon Sep 17 00:00:00 2001 From: yystopf Date: Fri, 6 May 2022 17:25:34 +0800 Subject: [PATCH 030/208] fix: issues and pull_request length calculate --- app/controllers/issues_controller.rb | 4 ++-- app/controllers/journals_controller.rb | 4 ++-- app/controllers/pull_requests_controller.rb | 4 ++-- app/models/journal.rb | 2 +- app/models/pull_request.rb | 6 +++--- ..._change_issues_description_and_journals_notes_column.rb | 7 +++++++ 6 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 db/migrate/20220506083813_change_issues_description_and_journals_notes_column.rb diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index e21c087b..ee0b5eff 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -109,7 +109,7 @@ class IssuesController < ApplicationController def create issue_params = issue_send_params(params) - Issues::CreateForm.new(issue_params.slice(:subject, :description)).validate! + Issues::CreateForm.new({subject: issue_params[:subject], description: issue_params[:description].b}).validate! @issue = Issue.new(issue_params) if @issue.save! SendTemplateMessageJob.perform_later('IssueAssigned', current_user.id, @issue&.id) if Site.has_notice_menu? @@ -223,7 +223,7 @@ class IssuesController < ApplicationController normal_status(-1, "不允许修改为关闭状态") else issue_params = issue_send_params(params).except(:issue_classify, :author_id, :project_id) - Issues::UpdateForm.new(issue_params.slice(:subject, :description)).validate! + Issues::UpdateForm.new({subject: issue_params[:subject], description: issue_params[:description].b}).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)) if Site.has_notice_menu? diff --git a/app/controllers/journals_controller.rb b/app/controllers/journals_controller.rb index 2dd1fef6..7c067659 100644 --- a/app/controllers/journals_controller.rb +++ b/app/controllers/journals_controller.rb @@ -23,7 +23,7 @@ class JournalsController < ApplicationController normal_status(-1, "评论内容不能为空") else ActiveRecord::Base.transaction do - Journals::CreateForm.new({notes: notes.to_s.strip}).validate! + Journals::CreateForm.new({notes: notes.to_s.strip.b}).validate! journal_params = { journalized_id: @issue.id , journalized_type: "Issue", @@ -75,7 +75,7 @@ class JournalsController < ApplicationController def update content = params[:content] if content.present? - Journals::UpdateForm.new({notes: notes.to_s.strip}).validate! + Journals::UpdateForm.new({notes: notes.to_s.strip.b}).validate! if @journal.update_attribute(:notes, content) normal_status(0, "更新成功") else diff --git a/app/controllers/pull_requests_controller.rb b/app/controllers/pull_requests_controller.rb index bb040784..bf5646a4 100644 --- a/app/controllers/pull_requests_controller.rb +++ b/app/controllers/pull_requests_controller.rb @@ -58,7 +58,7 @@ class PullRequestsController < ApplicationController def create # return normal_status(-1, "您不是目标分支开发者,没有权限,请联系目标分支作者.") unless @project.operator?(current_user) ActiveRecord::Base.transaction do - Issues::CreateForm.new({subject: params[:title], description: params}).validate! + Issues::CreateForm.new({subject: params[:title], description: params[:body].b}).validate! @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"]) @@ -90,7 +90,7 @@ class PullRequestsController < ApplicationController else ActiveRecord::Base.transaction do begin - Issues::UpdateForm.new({subject: params[:title], description: params[:body]}).validate! + Issues::UpdateForm.new({subject: params[:title], description: params[:body].b}).validate! merge_params @issue&.issue_tags_relates&.destroy_all if params[:issue_tag_ids].blank? diff --git a/app/models/journal.rb b/app/models/journal.rb index a1834ae2..5bf05b0d 100644 --- a/app/models/journal.rb +++ b/app/models/journal.rb @@ -6,7 +6,7 @@ # journalized_id :integer default("0"), not null # journalized_type :string(30) default(""), not null # user_id :integer default("0"), not null -# notes :text(65535) +# notes :text(4294967295) # created_on :datetime not null # private_notes :boolean default("0"), not null # parent_id :integer diff --git a/app/models/pull_request.rb b/app/models/pull_request.rb index 7338a1d7..e36af832 100644 --- a/app/models/pull_request.rb +++ b/app/models/pull_request.rb @@ -3,8 +3,8 @@ # Table name: pull_requests # # id :integer not null, primary key -# pull_request_id :integer -# gpid :integer +# gitea_id :integer +# gitea_number :integer # user_id :integer # created_at :datetime not null # updated_at :datetime not null @@ -12,7 +12,7 @@ # project_id :integer # title :string(255) # milestone :integer -# body :text(65535) +# body :text(4294967295) # head :string(255) # base :string(255) # issue_id :integer diff --git a/db/migrate/20220506083813_change_issues_description_and_journals_notes_column.rb b/db/migrate/20220506083813_change_issues_description_and_journals_notes_column.rb new file mode 100644 index 00000000..a8b934c7 --- /dev/null +++ b/db/migrate/20220506083813_change_issues_description_and_journals_notes_column.rb @@ -0,0 +1,7 @@ +class ChangeIssuesDescriptionAndJournalsNotesColumn < ActiveRecord::Migration[5.2] + def change + change_column :issues, :description, :text, :limit => 4294967295 + change_column :journals, :notes, :text, :limit => 4294967295 + change_column :pull_requests, :body, :text, :limit => 4294967295 + end +end From a6cee63d1780d353b16db4643c6c334d2942f284 Mon Sep 17 00:00:00 2001 From: yystopf Date: Sat, 7 May 2022 11:57:58 +0800 Subject: [PATCH 031/208] fix: change owner must fix foreign_key --- app/controllers/projects_controller.rb | 2 +- app/models/fork_user.rb | 2 +- app/views/projects/fork_users.json.jbuilder | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index c4e440af..8bc2fb47 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -209,7 +209,7 @@ class ProjectsController < ApplicationController end def fork_users - fork_users = @project.fork_users.includes(:user, :project, :fork_project).order("fork_users.created_at desc").distinct + fork_users = @project.fork_users.includes(:owner, :project, :fork_project).order("fork_users.created_at desc").distinct @forks_count = fork_users.size @fork_users = paginate(fork_users) end diff --git a/app/models/fork_user.rb b/app/models/fork_user.rb index 4177392e..2d74af4a 100644 --- a/app/models/fork_user.rb +++ b/app/models/fork_user.rb @@ -17,7 +17,7 @@ class ForkUser < ApplicationRecord belongs_to :project - belongs_to :owner + belongs_to :owner, class_name: 'Owner', foreign_key: :user_id belongs_to :fork_project, class_name: 'Project', foreign_key: :fork_project_id after_create :incre_project_common, :incre_user_statistic, :incre_platform_statistic diff --git a/app/views/projects/fork_users.json.jbuilder b/app/views/projects/fork_users.json.jbuilder index f5147e19..730576c1 100644 --- a/app/views/projects/fork_users.json.jbuilder +++ b/app/views/projects/fork_users.json.jbuilder @@ -1,7 +1,7 @@ json.count @forks_count json.users do json.array! @fork_users.each do |f| - user = f.user.present? ? f.user : Organization.find_by(id: f.user_id) + user = f.owner.present? ? f.owner : Organization.find_by(id: f.user_id) json.id f.fork_project.id json.identifier f.fork_project.identifier json.name "#{user.try(:show_real_name)}/#{f.fork_project.try(:name)}" From 50dc5cce5dd0dd5e0a4588bd148c15e35af9a412 Mon Sep 17 00:00:00 2001 From: yystopf Date: Sat, 7 May 2022 14:06:48 +0800 Subject: [PATCH 032/208] fix --- app/controllers/issues_controller.rb | 4 ++-- app/controllers/journals_controller.rb | 4 ++-- app/controllers/pull_requests_controller.rb | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index ee0b5eff..ddb0facd 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -109,7 +109,7 @@ class IssuesController < ApplicationController def create issue_params = issue_send_params(params) - Issues::CreateForm.new({subject: issue_params[:subject], description: issue_params[:description].b}).validate! + Issues::CreateForm.new({subject: issue_params[:subject], description: issue_params[:description].blank? ? issue_params[:description] : issue_params[:description].b}).validate! @issue = Issue.new(issue_params) if @issue.save! SendTemplateMessageJob.perform_later('IssueAssigned', current_user.id, @issue&.id) if Site.has_notice_menu? @@ -223,7 +223,7 @@ class IssuesController < ApplicationController normal_status(-1, "不允许修改为关闭状态") else issue_params = issue_send_params(params).except(:issue_classify, :author_id, :project_id) - Issues::UpdateForm.new({subject: issue_params[:subject], description: issue_params[:description].b}).validate! + Issues::UpdateForm.new({subject: issue_params[:subject], description: issue_params[:description].blank? ? issue_params[:description] : issue_params[:description].b}).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)) if Site.has_notice_menu? diff --git a/app/controllers/journals_controller.rb b/app/controllers/journals_controller.rb index 7c067659..6dc1e29c 100644 --- a/app/controllers/journals_controller.rb +++ b/app/controllers/journals_controller.rb @@ -23,7 +23,7 @@ class JournalsController < ApplicationController normal_status(-1, "评论内容不能为空") else ActiveRecord::Base.transaction do - Journals::CreateForm.new({notes: notes.to_s.strip.b}).validate! + Journals::CreateForm.new({notes: notes.to_s.strip.blank? ? notes.to_s.strip : notes.to_s.strip.b}).validate! journal_params = { journalized_id: @issue.id , journalized_type: "Issue", @@ -75,7 +75,7 @@ class JournalsController < ApplicationController def update content = params[:content] if content.present? - Journals::UpdateForm.new({notes: notes.to_s.strip.b}).validate! + Journals::UpdateForm.new({notes: notes.to_s.strip.blank? ? notes.to_s.strip : notes.to_s.strip.b}).validate! if @journal.update_attribute(:notes, content) normal_status(0, "更新成功") else diff --git a/app/controllers/pull_requests_controller.rb b/app/controllers/pull_requests_controller.rb index bf5646a4..f45d1f35 100644 --- a/app/controllers/pull_requests_controller.rb +++ b/app/controllers/pull_requests_controller.rb @@ -58,7 +58,7 @@ class PullRequestsController < ApplicationController def create # return normal_status(-1, "您不是目标分支开发者,没有权限,请联系目标分支作者.") unless @project.operator?(current_user) ActiveRecord::Base.transaction do - Issues::CreateForm.new({subject: params[:title], description: params[:body].b}).validate! + Issues::CreateForm.new({subject: params[:title], description: params[:body].blank? ? params[:body] : params[:body].b}).validate! @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"]) @@ -90,7 +90,7 @@ class PullRequestsController < ApplicationController else ActiveRecord::Base.transaction do begin - Issues::UpdateForm.new({subject: params[:title], description: params[:body].b}).validate! + Issues::UpdateForm.new({subject: params[:title], description: params[:body].blank? ? params[:body] : params[:body].b}).validate! merge_params @issue&.issue_tags_relates&.destroy_all if params[:issue_tag_ids].blank? From 77620f77358a402ed257dd144055b5ed512c3079 Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 16 May 2022 16:59:25 +0800 Subject: [PATCH 033/208] add: download and import user template xlsx --- .../admins/import_users_controller.rb | 2 +- .../admins/new_import_user_from_excel.rb | 15 ++++ .../admins/import_user_from_excel_service.rb | 71 ++++++++++++++++++ app/views/admins/users/index.html.erb | 4 +- public/导入用户模板.xlsx | Bin 0 -> 9230 bytes 5 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 app/imports/admins/new_import_user_from_excel.rb create mode 100644 app/services/admins/import_user_from_excel_service.rb create mode 100644 public/导入用户模板.xlsx diff --git a/app/controllers/admins/import_users_controller.rb b/app/controllers/admins/import_users_controller.rb index 5d8a60ce..4575b08d 100644 --- a/app/controllers/admins/import_users_controller.rb +++ b/app/controllers/admins/import_users_controller.rb @@ -2,7 +2,7 @@ class Admins::ImportUsersController < Admins::BaseController def create return render_error('请上传正确的文件') if params[:file].blank? || !params[:file].is_a?(ActionDispatch::Http::UploadedFile) - result = Admins::ImportUserService.call(params[:file].to_io) + result = Admins::ImportUserFromExcelService.call(params[:file].to_io) render_ok(result) rescue Admins::ImportUserService::Error => ex render_error(ex) diff --git a/app/imports/admins/new_import_user_from_excel.rb b/app/imports/admins/new_import_user_from_excel.rb new file mode 100644 index 00000000..b9a452cf --- /dev/null +++ b/app/imports/admins/new_import_user_from_excel.rb @@ -0,0 +1,15 @@ +class Admins::NewImportUserFromExcel < BaseImportXlsx + UserData = Struct.new(:login, :email, :password, :nickname) + + def read_each(&block) + sheet.each_row_streaming(pad_cells: true, offset: 1) do |row| + data = row.map(&method(:cell_value))[0..3] + block.call UserData.new(*data) + end + end + + private + def cell_value(obj) + obj&.cell_value + end +end \ No newline at end of file diff --git a/app/services/admins/import_user_from_excel_service.rb b/app/services/admins/import_user_from_excel_service.rb new file mode 100644 index 00000000..709551c2 --- /dev/null +++ b/app/services/admins/import_user_from_excel_service.rb @@ -0,0 +1,71 @@ +class Admins::ImportUserFromExcelService < ApplicationService + Error = Class.new(StandardError) + + attr_reader :file, :result + + def initialize(file) + @file = file + @result = { success: 0, fail: [] } + end + + def call + raise Error, '文件不存在' if file.blank? + excel = Admins::NewImportUserFromExcel.new(file) + + excel.read_each(&method(:save_user)) + result + rescue ApplicationImport::Error => ex + raise Error, ex.message + end + + private + def save_user(data) + user = find_user(data) + if user.blank? + create_user(data) + result[:success] +=1 + else + fail_data = data.as_json + fail_data[:data] = fail_data.values.join(",") + fail_data[:message] = '用户已存在' + result[:fail] << fail_data + end + + rescue Exception => ex + fail_data = data.as_json + fail_data[:data] = fail_data.values.join(",") + fail_data[:message] = ex.message + result[:fail] << fail_data + end + + def create_user(data) + ActiveRecord::Base.transaction do + username = data.login&.gsub(/\s+/, "") + email = data.email&.gsub(/\s+/, "") + password = data.password + nickname = data.nickname&.gsub(/\s+/, "") + raise Error, "无法使用以下关键词:#{username},请重新命名" if ReversedKeyword.check_exists?(data.login) + Register::RemoteForm.new({username: username, email: email, password: password, platform: 'forge'}).validate! + user = User.new(admin: false, login: username, mail: email, nickname: nickname, platform: 'forge' , type: "User") + user.password = password + user.activate + raise Error, user.errors.full_messages.join(",") unless user.valid? + interactor = Gitea::RegisterInteractor.call({username: username, email: email, password: password}) + if interactor.success? + gitea_user = interactor.result + result = Gitea::User::GenerateTokenService.call(username, password) + user.gitea_token = result['sha1'] + user.gitea_uid = gitea_user[:body]['id'] + UserExtension.create!(user_id: user.id) if user.save! + else + raise interactor.error, 'gitea user create error' + end + + user + end + end + + def find_user(data) + User.find_by(login: data.login) + end +end \ No newline at end of file diff --git a/app/views/admins/users/index.html.erb b/app/views/admins/users/index.html.erb index e21a3d66..2f41ffdb 100644 --- a/app/views/admins/users/index.html.erb +++ b/app/views/admins/users/index.html.erb @@ -31,7 +31,9 @@ <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> <% end %> - <%= javascript_void_link '导入用户', class: 'btn btn-secondary btn-sm', data: { toggle: 'modal', target: '.admin-import-user-modal'} %> + <%= link_to '下载导入模板', "/导入用户模板.xlsx", class: 'btn btn-secondary mr-3' %> + + <%= javascript_void_link '导入用户', class: 'btn btn-secondary', data: { toggle: 'modal', target: '.admin-import-user-modal'} %> diff --git a/public/导入用户模板.xlsx b/public/导入用户模板.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c538bed63aab06831ab7286e6a1ffe52a0337864 GIT binary patch literal 9230 zcmai41z45K)}|W?>5$rVNJvXccS}fz^rlmgR=T@ex{>ab?(XhxkRSA(qjJwZ|2)s` z72muwv)0<{{bacqB$0w;}-pAL!2JFKdJ)o|P70|-^rObmE+QSzxKg4QTr;6x7 zK|o%Cfq)?YE~af^LFZs*ni$$F2F`#cc;Ru6Tnc2OC6;5P^!UsPt!j(U1e|V?Kn>d% zyr!kIA-aS%KLsk>A}~E4%&D@n*W6!gIDtt9#>JH{dRCHUhg%B4V(E*UDY4fpCj|vj zz*0z3nwSt~tHIdNonJUC6@Ulhfcc`6t=a@`8U#RA_z3w}mP^`D?cn-ao>Cj+iBYqa zY-fRbn#SpWLSTW(^0HcOfHyKusJL7h9?lJV`Ht=@9T>dp?z@7>HiyhADvZ^WrLC3k z-epUiJm6Dv>LPodi?sc!GKO5R*DM;`nX9T$n>g0fVnvzFle6u^gFDlZr^|O&M|)>< zbW1eXTLsarn=(7#3|ru`yHK?rZ76~2EtwrSEslyJ!5=}SeVVx5v`Dh~GlOI$zG?Wr zT($L<_2$a_2=Y&K=U(4HQ$C>k@&R3x-_g|tTIoN+8yU(V-Aa$_Z4dbVW|gZXF&8o) zfX|!A%by0*W-v7|PHaarV>bOUIJ}$)2197U`QmC$z2x;a2fN!zE(2*AHXkh3kcfUI zsgruquv)}rQ{bwtZi)c;p`nVpCVC6w5SxQtAS5Q*B_J%|eM^)friVV3a|w8#3kE)! zUwntri{%lslC|YBYR>Xxp0&*7ENzf zay_k_H5Wxmb6sMZ!5DoVSiGt!fMqwI&fpZ;yH6r|+uCYrmdhZ_abbY|~`5(C2TH654er((jK50*SG`9n? z@1L?RhUHoy`Tozmjw1fs>6b2yx#``;JIVY%j5ceZkP&-&eUy&a_H z`HbpXX*5zvDT*?;hUh2yF8y^k0dhA?Dzn-7)yh7+$5yEX(O@E6Hcve_bU{pY(S|mf zu#ug^8Ihg(k0{<-V z?5BV|(8|QxP+#BXX}};KK-%(p*v`6;ARt)(CHq5#;Sv8pg>Lg1dgOY{O&*v!;dokL zaRM33FqENt%1&&Q5Qes#g6MS6J@=boIX%FqO~TL*Ou%`X<>Pae!$lY|rm0l`tS&0A zd{-EAZNlxSkuoF<5_!Oeuxj&gsBVL?QXZJEkJ zLOD^=FUmh>jxwY6No|6V0eFo1S6`7)vP|j9~z|Y*P;3U5!D({$x@Q z(ov*e68(hK0Pk`K3700LYr1qXzbGyBa|}5Fjp-ShjiFmAY1uj=r)*`9+`H6zzGaN? z8OHwJsZ4Pt@wYAH=mk-vt&EfNkZ`#Pv!wo#Vu!OTxB3>tp0-+>=a?202oaMesFN?u zGiTLWg~uK64f;cfx4!~_B5jKODzZr*d}CLB)e%cgsL#p=DX z(RM;_t6oBXsnG>lVz;#8HI(UF+))-pazF@ls|xtS5)2+6xub(m0z0+}t@kPx)`Ghh zTL&d9gp1umI8t1uc3jXf-TG&Uc0OE6y0|cSUwh%v>XnOSZY0e!)jhh81fz=DZxGdK zBVR+Z*(fW|$=!$p{27$Wi$B>r`1u;m61+WPp3o|k0Y8y5&Woi@R!blC$ zK!{q#XKSIFRf{A^J)oPEg<*0u*fIDZxnV@f(XQ8_Y|mz-LwI8B?#GvrY#O0 zfDdc3q^p|{w`Ap|iUlmxu*5kBnG~R3zmh#i)%s|^R4LtTPvwmRLqR~5?SPLfWdcXE zo+e&xA7I$|37g-Ju>~pfKo$Uay?Lp4xAE4lNMTS)TVM@Jt<^l`9U+ai!nnN29HSTD$DPY0vsy&uBmr3|qHeXJZo4B#L-Zk>>5?bvx6+nSXY6a8fe& z_1fvt%7`$}MgvVNc!W^aQr>fmOWighYL25&+rar=S|BT0ap^JoxQBfZD21Lgf6h{p%#7OdOjZ-MhWeAU-h=l z@oi2r+hb-b8Q+v4l!sTnv(q&ls*{wZlJ%0O;W`VDWU$NM=O$8m7c9~gtMza|l$F@TD~-pGXCIe7f^IcY2}X#bHUH7oz(Y&s!LI>VSzsiL|ygD=$=I zs9WUjNkJ>N4R_xic-Yt=dVLL^HvTG8b4%r-&{JGOg8g!WAx9mOYJhz7z?-%h-`x@; zRw2>#mS>_k@sq*rXkuuNUt?{WgJbI7jM{gBi?L?iyl7Uc8Nl$UO-Ufj)OY_v&a~gP z#@w5Kjo-fopp zF9+9v?gd!)nnQ(bP-`IH`lf-2V>s2ENo8om5giQ03@eegghBYEU5Ftqlw!!~9gfF3 zVd~p+CFOE_8anC=e}#e)5MzC%MX%N7>f9C9Z7nb%3p0MSPgK&5xtLR9-5=s(=g}nc zz;X}e(!aRrXU+KSbgbo!t!3H;NvHt=tja_;yEb2t>7XsAag1#`8rB&V&h$j6o+?oI zDPz4-jlK_T7qmvS%bVtvm!~w`PPDmQw*wa=<5lTQZra1=Cc_>Lk|9?UAaS@t7fAwM z`csH^c<&OSY=BOrh3E0{1J+-Vigv#3c{#!>UqBS6W(E(jlt?-OrQ$fz#W%bMSm}m< z9+s$ONFz@C25_uZfpWGlekB70~(|DDnRGnFp##xiOU+5z3)oc9bv^;83X) ztyKT6;8hmCUEhR9jnA>AKp7S~emmL;USVckMtWl;1APO!iH{o&5hczNn;haeLhhJk zPhqI_>H*XBx+P6aUlOxLv2qvQRjK=`O_3ywG%tE{ z@Jtpc(IQA43--L-mO!Gq)SfE;Z$*Un-`5@W(3PTxWQS!e1Mr6l7$)JZQTK|m0H zrVMKvM^k<4#|mfcof&Xu9Q`}D*1fw{u?viWKXK02Q7eodGMN-yH~=`5=^H}v2uU|$ zt}wV=M9AT+*qLY8^ciZEX-zd48t2+amFCKxg`)Qo8C}Wen0EW{FAp;5NI5DfIKXs# zmmNR$S8p`P;7%ZYDzxf+#@~4{ZEUM9k3O?=<2<3N=}n{kkUtilH&7bdkQfsG5m-e!i#8`tAbB6sZCGvbh+yxl^kM^#z38 zR|4X+RJ9@I0u5|CeCfH_L!mb&Sl(g-1dg(5%u}S^A5NSI=;Ctut88z%zgVeV?GFgM zo=)F4a1#Lwsopl&ulZFL8hwqN04#jBG5dbu)q`_oeaB&Nd^h8#>U*{lnUfWnI?OPp z=>07%8KVE>YTKy6i^U9Jz<7fG(ssSannF7xQlvU+3iIZgD$sk>5gli)FVOvfly98* zGhJeMej~rHZ1NL{kz92%#YgBe0?G3{@JF**Hk9LU~Od4^$+_?D-V>WR* zUesG}I=H&gDSbvbzK5AZ7jLceE!P5mEl`bux~HLz7NJdUnnQ3OeN7UbZ>({mmnAEz ztO*Pyu~uakmTGWYiy(0*^7%mOmJV_ttA%X13OAk?Gybm9ND2xe#}N^}KFVO;Gz}ZW zNj-xX8<1mL@GQf&o=|%{u%ps0wYxGNrPf-p>n-1!>7*Z1&{`toJscdqTSiUW;F57g z+O>CSfn_RXu@=CL{~PnmSBUFm4)u1j?yYqk%Y1=-`yfVZJWvra5VqcH5jvvRm>Y;<)VsK|NtQ zB*JA8)W_7J14QY`HV@o{4sZ<7Q`(Tw{d;Yq#3nUYj>bsCii7GrNrS4B36gjeN2#uVT$w}eA^yjWebYd4(LG3S+Z{4Njh#3s2s0@Erv%5VA&VTQdRk^8xyOH%jw{(fcJZ}Dlf}n`Neh??mj|=ve?~{URO4_;wXF4c&8Hp{4pxin%t;Q~M*2)#%CF8m zp_5LfxX*+{ICMZLh%h@4Pf!DwNQBX?Gwb4RsL+Hdrt}+BkBS5HDLN5$8RlSXuoSv) zJEgkU%t;(Ix zOf%Oo{Okwp=HkC3eRpW0s`3@PTymR<_u=|bKIMDfJEW%kB?Bm0O-8`NT2olIfoO(Q zDYHjt|(Kr>w3ws$^;r?{-#rPF;C55SCo6< zGqVU?J@#ejod2N_L^;nbVuV)-sS@fbZFW!siMDM~hM1CeA%?RYbP-vh*UNRU+Tz2O z5>P41rdzyBB_BTggh&VTHL?8a##SdJIyAS=!~)yQqO|aeVxj!)|nQ3qAR7};Op&*HEA!4cBQkYpsA&b$SxFRy5VNmccL=_PZ206jFjIxO{53ugv|M?MV!zG6b}}g1iC-4T@EV3>A)$Ne+9@%n zaecX%QW+d-Kon$WP0K-jc%p;(bB&LgTn=j%7NGG*D%>8jK8K&IVogZ!<6(p>HxSz7 zp?V?VoPw$ff|9utxm{T?JcTMkS6Un`bkGvYMW2sW98j2iyUcr;eP6|Tb$4{xon9u* zlhklu4VD?Y3D;pEVSFj080VkV4oNRrMaWB@+a#fs<}Uy*6l_c&ES=;TFrXo`l085W zbdcPJ>K>8q=0fEwz$6Yt7Cse{)FPf}>a@028>mXLi(cVRqLnhaKj>V^26kEvm6N14 zb?T4kknZb%4M}hku^z&nzq;$;;U;Lj$m?F<3D8gTuJVE*D3l)2Yk^!AUn2JCZwjtj zH2~kwG8LGNm5UaA9kNM8{Okp0L>tctUWn8gvio}x_xNs~W=x<8puxMq)t6;tgs1Vf zOB=v{(36OdpZJJ3t>!a_y5C$`?LnQxwO2QqHZ-*zYHVzG5`?r@GG%+yB+BjO>|ESyTz{5mDB>gdKh~xWm#N5j?IH`~>fe&`}$JyaW zo(q8o3-KS0(bLgWA!cq+RTWx;8ub7<#ALM2;nG0^+;^T~ReTUqiral#D+B#OJTbE_ z2!bDvj}~HhQJ9!>f?`3mx<5&e1I$SqBSY<|$KuyuGzg?Vca=pNrBN1!@eH|kyuv6U zK@>5FeP4P6K_7}J?in^2Krrikv(=seffJE6D@Zd!i;UqG-mM~+zqz51ILxSbsl zrKzRZ0=;O%tCjs*>e{O)8DeGB@{UTfd}>LEt95PAfP(kA+%|NtX%ogFruDYL3S36L zPxdYm79pM?F?!qQ3T65V? zOmyC4V_v56d>(f`eC5Z3#m41)e$l2HsdVeF)3mC(*`f~097Xk!sd`+g8cD!qmre#Z z0RvYScIsuK*>{3J%gE|IDe9$%@=^GqUHRC0e`qOK8){qW>&e?#8JWNTQBAspl!&$Q zBKtTu+bd_W#+S*2&T&yE^Ac!@j_WaS;@Sxf$-$!t6w4Hsk8Df%x02TgEVSUU2>VfQ z+!(mJ3P~36lhCOX6Uep#didCAbhb){Eu5_CtU){`b&TRaOSM*z#Ki5<3byG~2@cCJ zef=aSCtIsU>0i*=oY0O&gQS}asOX*wGu~i(WyLyhp;!EZ5A6s{rIi~?aGrwY?nK!B zzW#%gLRerJ_2o-f;QPB>6N%`P+V6V?615Y8_utvGc0aoq>MnOf*(x}izfLN0dJh@I zTHD?_aH~(t`z)CL%F>ZL5?g#mmtTcFAog= zwog6jS~pAU)(*%=n^wtR#3jk!o{85#XjvMm1}6c-rQVb~Cfz968vM*=`GVF~6*E8t zqBTjFK&ESwh%DVPN&jLNMt`YS#^>Ims!|b=yL#PlFpEi)mlidVfOCyxv`5KkHW8g+ z=`Nn$=0X&FggEPc4(c~J0|@ot34Tx-yt@@yqiycqQapz{YOCik2*&nsHx30zA!pyj zCK}VG?R{xzjs;nEk9khg4X1HQ%2osqJW=GW>_3}0MI6geiM6Oo3H$V@lGv2Nn!>Cs ze$ZHLXS2NWzW(tS<)3@EP!B+ATIriU?b$lB+11@Y{I>AWw}t;(hVD_aAZ$RajUKt< zz~caUy+P6*9c9YqCGoDLo{L+I#Qh-I^DvdYhI&3(n$a5Q;MlA6_=OL8wYYPH8Vp%M zbsn~|9=yOVy9U|A4-U$69wPbmW8jVo7A+99+s8ULUY_f(Bc|y9q3R#x1{ptbQidB1 zZ7Im`CUY_iX0!9?l6bPzo6DGgI~M9lejy|Ix_zGNvvVz!C7XBM6%d%a)4#|SoBn2i ziF#EbG||>H1UL;7SR@|hqM6#IacEqBj>z1XMx0s~q^!FS#<@_w^x?;0&>VmseS z5%`g&^j-0ejBKt+d zP3}rnNwJB3hmh>mctGAWGgo~uibQ6tOE06xPJuvo%`ytr@azETWy)u>!%BZp!~l_E z>Vtzyf=QJq@zHW{N=sP16zvxp6=V}yKuUjbs4WGCvnatVRitrzvp}ctjRB6kGdHWH zob!3!=^JvHPnIF8_w~Ap70(vZZuS`tBVi;1B{zB4yBz=>EMr|z7;6%r`7}Q{zC-uv zl+v)yia9N~?29|1;cQ`d33KA`S3_uzL&%N^8YMjLwWJI+73aXjs=u^c`xj2(KQZq& zPT?P69hJizd4N;mAtaFeg!R8Pe(?)G@bwPi69;EN_CD~qC+eN+D})X$opLJneo-fK z2l~d21vgHDpHb}QKoZFVvUx>!{=>mh#~wS>nPBF181x^r5UKQaWqoz%gJpr2yVWM zMlT@5n8$3><5GNb1hK7Kjk@LGr1k%L8n^o%5XD1$IxRx#z|Bm7A*@4~h7`uaulS)i zr|WRJ?j6@eL*g32j#`|W|D4JfdZ@vXFF=-t=~W3f%wpnek8io z-T=*Q^v!KF6&x+}t<@hxWkE!b6c_`V&jndSYla$=yoGjvw$_q<4qzF4^t|cSX;hNO z;dv6J78svt+QS396*Gr_L(?4?d;W<`|XvalXg_QN*G04K~|ua^|q;itEgin{KRno=53k zcI);{mHUBdzN&l-7!37r;w)9iR(LMFQa-tjib0d7BRNsLIPl^q3WJsTypiqzA~Hmt zqtEcU<)3!R-I_mZ z2(4_SsXJ*t(Pg=Xk`V_5L;I1v|9NE|76$|rgz(|H1OFe_>8FAJ9k?G2{N&&_ADD-~ z4#=NQp9b(yEouZe^2iYcohEWkNkJF|J*C^ z6r!i>_b+Ylhb{fL)4#azr|M5h-e2l-4}tfW`rnlAzfJm)_xw2?Ck+qr9~Sg+y8pZ+ zf1>mX_qQ!NqAy3(|Umn?iSNp}CJ-y_oh2Sp($q)4M!zKR?+rM4(BXRw6 zJgzX!&lUbHf&829W5E469(CRD{&xHy3jCi>%R;t c|JQVWnUWEQd@TGP^22kGxCc26g2zw)2al(vY5)KL literal 0 HcmV?d00001 From 9cd18397f7a7ddeec987fc9851d661171b294698 Mon Sep 17 00:00:00 2001 From: yystopf Date: Wed, 18 May 2022 10:11:09 +0800 Subject: [PATCH 034/208] fix: import user login length required --- app/libs/custom_regexp.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/libs/custom_regexp.rb b/app/libs/custom_regexp.rb index 25c3ae98..72184630 100644 --- a/app/libs/custom_regexp.rb +++ b/app/libs/custom_regexp.rb @@ -1,7 +1,7 @@ module CustomRegexp PHONE = /1\d{10}/ EMAIL = /\A[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+\z/ - LOGIN = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾 + LOGIN = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]{4,15}/ #只含有数字、字母、下划线不能以下划线开头和结尾 LASTNAME = /\A[a-zA-Z0-9\u4e00-\u9fa5]+\z/ NICKNAME = /\A[\u4e00-\u9fa5_a-zA-Z0-9]+\z/ PASSWORD = /\A[a-z_A-Z0-9\-\.!@#\$%\\\^&\*\)\(\+=\{\}\[\]\/",'_<>~\·`\?:;|]{8,16}\z/ From ca3c56452917b968d0b302728686757e20708657 Mon Sep 17 00:00:00 2001 From: yystopf Date: Wed, 18 May 2022 11:03:39 +0800 Subject: [PATCH 035/208] fix: not found user return 404 --- app/controllers/owners_controller.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/owners_controller.rb b/app/controllers/owners_controller.rb index 067fe4da..863f8aa3 100644 --- a/app/controllers/owners_controller.rb +++ b/app/controllers/owners_controller.rb @@ -11,6 +11,8 @@ class OwnersController < ApplicationController end def show + return render_not_found unless @user.present? + @owner = Owner.find_by(login: params[:id]) || Owner.find_by(id: params[:id]) # return render_not_found unless @owner.present? # 组织 From e237fd46f7d151a3162f806a6042dae03abad34e Mon Sep 17 00:00:00 2001 From: yystopf Date: Wed, 18 May 2022 11:37:23 +0800 Subject: [PATCH 036/208] fix: not found user return 404 --- app/controllers/owners_controller.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/controllers/owners_controller.rb b/app/controllers/owners_controller.rb index 863f8aa3..28a5210d 100644 --- a/app/controllers/owners_controller.rb +++ b/app/controllers/owners_controller.rb @@ -11,10 +11,8 @@ class OwnersController < ApplicationController end def show - return render_not_found unless @user.present? - @owner = Owner.find_by(login: params[:id]) || Owner.find_by(id: params[:id]) - # return render_not_found unless @owner.present? + return render_not_found unless @owner.present? # 组织 if @owner.is_a?(Organization) return render_forbidden("没有查看组织的权限") if org_limited_condition || org_privacy_condition From 83e43f0c9792010817828abcc117294e2e287303 Mon Sep 17 00:00:00 2001 From: yystopf Date: Fri, 20 May 2022 11:43:22 +0800 Subject: [PATCH 037/208] add: new readme delay function --- app/helpers/repositories_helper.rb | 39 +++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index b8bfc31c..9fd45249 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -87,6 +87,42 @@ module RepositoriesHelper end end + # author hui.he + def new_readme_render_decode64_content(str, owner, repo, ref) + path = [owner&.login, repo&.identifier, 'tree', ref].join("/") + return nil if str.blank? + content = Base64.decode64(str).force_encoding('UTF-8') + s_regex = /\[.*?\]\((.*?)\)/ + src_regex = /src=\"(.*?)\"/ + ss = content.to_s.scan(s_regex) + ss_src = content.to_s.scan(src_regex) + total_sources = ss + ss_src + total_sources.uniq! + total_sources.each do |s| + begin + s_content = s[0] + # 链接直接跳过不做替换 + next if s_content.starts_with?('http://') || s_content.starts_with?('https://') || s_content.starts_with?('mailto:') + # 路径替换 + if s_content.starts_with?('./') || s_content.starts_with?("../") || s_content.starts_with?("/") + s_content = File.expand_path(s_content, path) + s_content = s_content.split("#{Rails.root}/")[1] + content = content.gsub(s[0], "/#{s_content}") + else + # 图片资源替换 + s_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw?filepath=#{s_content}&ref=#{ref}"].join + content = content.gsub(s[0], s_content) + end + rescue + next + end + end + + return content + rescue + return str + end + # unix_time values for example: 1604382982 def render_format_time_with_unix(unix_time) Time.at(unix_time).strftime("%Y-%m-%d %H:%M") @@ -102,7 +138,8 @@ module RepositoriesHelper Rails.logger.info("entry===#{entry["type"]} #{entry["name"]}") content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content'] Rails.logger.info("content===#{content}") - readme_render_decode64_content(content, owner, repo, ref) + # readme_render_decode64_content(content, owner, repo, ref) + new_readme_render_decode64_content(content, owner, repo, ref) else file_type = File.extname(entry['name'].to_s)[1..-1] if image_type?(file_type) From 779c86108a547da22ef3cb2148906c49cbea196f Mon Sep 17 00:00:00 2001 From: yystopf Date: Fri, 20 May 2022 17:00:02 +0800 Subject: [PATCH 038/208] fix: readme relative delay and replace_content --- app/controllers/repositories_controller.rb | 3 +- app/helpers/repositories_helper.rb | 30 +++++++++++++------ .../repositories/_simple_entry.json.jbuilder | 7 +++-- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index 272e121a..a2ab33b2 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -225,7 +225,8 @@ class RepositoriesController < ApplicationController @path = Gitea.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/" @readme = result[:status] === :success ? result[:body] : nil @readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path) - render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha") + @readme['replace_content'] = readme_decode64_content(@readme, @owner, @repository, params[:ref], @path) + render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha", "replace_content") rescue render json: nil end diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index 9fd45249..1897f3ae 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -88,8 +88,8 @@ module RepositoriesHelper end # author hui.he - def new_readme_render_decode64_content(str, owner, repo, ref) - path = [owner&.login, repo&.identifier, 'tree', ref].join("/") + def new_readme_render_decode64_content(str, owner, repo, ref, readme_path, readme_name) + file_path = readme_path.include?('/') ? readme_path.gsub("/#{readme_name}", '') : readme_path.gsub("#{readme_name}", '') return nil if str.blank? content = Base64.decode64(str).force_encoding('UTF-8') s_regex = /\[.*?\]\((.*?)\)/ @@ -103,15 +103,19 @@ module RepositoriesHelper s_content = s[0] # 链接直接跳过不做替换 next if s_content.starts_with?('http://') || s_content.starts_with?('https://') || s_content.starts_with?('mailto:') - # 路径替换 - if s_content.starts_with?('./') || s_content.starts_with?("../") || s_content.starts_with?("/") + ext = File.extname(s_content)[1..-1] + + if image_type?(ext) || download_type(ext) + s_content = File.expand_path(s_content, file_path) + s_content = s_content.split("#{Rails.root}/")[1] + # content = content.gsub(s[0], "/#{s_content}") + s_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw?filepath=#{s_content}&ref=#{ref}"].join + content = content.gsub(s[0], s_content) + else + path = [owner&.login, repo&.identifier, 'tree', ref, file_path].join("/") s_content = File.expand_path(s_content, path) s_content = s_content.split("#{Rails.root}/")[1] content = content.gsub(s[0], "/#{s_content}") - else - # 图片资源替换 - s_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw?filepath=#{s_content}&ref=#{ref}"].join - content = content.gsub(s[0], s_content) end rescue next @@ -133,13 +137,21 @@ module RepositoriesHelper date.to_time.strftime("%Y-%m-%d %H:%M") end + def readme_decode64_content(entry, owner, repo, ref, path=nil) + Rails.logger.info("entry===#{entry["type"]} #{entry["name"]}") + content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content'] + Rails.logger.info("content===#{content}") + # readme_render_decode64_content(content, owner, repo, ref) + new_readme_render_decode64_content(content, owner, repo, ref, entry['path'], entry['name']) + end + def decode64_content(entry, owner, repo, ref, path=nil) if is_readme?(entry['type'], entry['name']) Rails.logger.info("entry===#{entry["type"]} #{entry["name"]}") content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content'] Rails.logger.info("content===#{content}") # readme_render_decode64_content(content, owner, repo, ref) - new_readme_render_decode64_content(content, owner, repo, ref) + return Base64.decode64(content).force_encoding('UTF-8') else file_type = File.extname(entry['name'].to_s)[1..-1] if image_type?(file_type) diff --git a/app/views/repositories/_simple_entry.json.jbuilder b/app/views/repositories/_simple_entry.json.jbuilder index 9d0998c8..3a0d16e2 100644 --- a/app/views/repositories/_simple_entry.json.jbuilder +++ b/app/views/repositories/_simple_entry.json.jbuilder @@ -8,7 +8,10 @@ if @project.forge? json.path entry['path'] json.type entry['type'] json.size entry['size'] - + is_readme = is_readme?(entry['type'], entry['name']) + if is_readme + json.replace_content readme_decode64_content(entry, @owner, @repository, @ref, @path) + end json.content direct_download ? nil : decode64_content(entry, @owner, @repository, @ref, @path) json.target entry['target'] @@ -24,7 +27,7 @@ if @project.forge? json.direct_download direct_download json.image_type image_type - json.is_readme_file is_readme?(entry['type'], entry['name']) + json.is_readme_file is_readme json.commit do json.partial! 'last_commit', latest_commit: entry['latest_commit'] end From fe23c2afbf9d5287649da474eea7c25608f81a85 Mon Sep 17 00:00:00 2001 From: yystopf Date: Sat, 21 May 2022 16:45:42 +0800 Subject: [PATCH 039/208] fix: replace content next if blank --- app/helpers/repositories_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index 1897f3ae..9cb64b35 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -102,7 +102,7 @@ module RepositoriesHelper begin s_content = s[0] # 链接直接跳过不做替换 - next if s_content.starts_with?('http://') || s_content.starts_with?('https://') || s_content.starts_with?('mailto:') + next if s_content.starts_with?('http://') || s_content.starts_with?('https://') || s_content.starts_with?('mailto:') || s_content.blank? ext = File.extname(s_content)[1..-1] if image_type?(ext) || download_type(ext) From fbebd0a623fbbc0aec9ae2d0b488e26c536b773a Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 23 May 2022 09:27:09 +0800 Subject: [PATCH 040/208] fix --- app/libs/custom_regexp.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/libs/custom_regexp.rb b/app/libs/custom_regexp.rb index 72184630..b60a80cc 100644 --- a/app/libs/custom_regexp.rb +++ b/app/libs/custom_regexp.rb @@ -1,7 +1,7 @@ module CustomRegexp PHONE = /1\d{10}/ EMAIL = /\A[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+\z/ - LOGIN = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]{4,15}/ #只含有数字、字母、下划线不能以下划线开头和结尾 + LOGIN = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]{4,15}\z/ #只含有数字、字母、下划线不能以下划线开头和结尾 LASTNAME = /\A[a-zA-Z0-9\u4e00-\u9fa5]+\z/ NICKNAME = /\A[\u4e00-\u9fa5_a-zA-Z0-9]+\z/ PASSWORD = /\A[a-z_A-Z0-9\-\.!@#\$%\\\^&\*\)\(\+=\{\}\[\]\/",'_<>~\·`\?:;|]{8,16}\z/ From b0092487e699c72d96add9b34ca0e2f11de4827d Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 23 May 2022 18:10:41 +0800 Subject: [PATCH 041/208] fix: is readme use regex --- app/helpers/repositories_helper.rb | 2 +- app/libs/custom_regexp.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index 9cb64b35..73ce9823 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -22,7 +22,7 @@ module RepositoriesHelper def is_readme?(type, str) return false if type != 'file' || str.blank? readme_types = ["readme.md", "readme", "readme_en.md", "readme_zh.md", "readme_en", "readme_zh"] - readme_types.include?(str.to_s.downcase) + readme_types.include?(str.to_s.downcase) || str =~ CustomRegexp::MD_REGEX end def render_commit_author(author_json) diff --git a/app/libs/custom_regexp.rb b/app/libs/custom_regexp.rb index b60a80cc..e778c549 100644 --- a/app/libs/custom_regexp.rb +++ b/app/libs/custom_regexp.rb @@ -11,5 +11,5 @@ module CustomRegexp 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_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾 - + MD_REGEX = /^.+(\.[m|M][d|D])$/ end From c27cc063e67003e091afedc1b83d1b622281f762 Mon Sep 17 00:00:00 2001 From: yystopf Date: Tue, 24 May 2022 10:59:52 +0800 Subject: [PATCH 042/208] add: new update user info simple and sync gitea --- app/controllers/accounts_controller.rb | 776 ++++++++++---------- app/controllers/concerns/register_helper.rb | 28 + app/forms/gitea/user/update_form.rb | 2 +- config/routes.rb | 1 + 4 files changed, 428 insertions(+), 379 deletions(-) diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index e40630c6..1af348ab 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -1,378 +1,398 @@ -class AccountsController < ApplicationController - include ApplicationHelper - - #skip_before_action :check_account, :only => [:logout] - - def index - render json: session - end - - # 其他平台同步注册的用户 - def remote_register - Register::RemoteForm.new(remote_register_params).validate! - username = params[:username]&.gsub(/\s+/, "") - tip_exception("无法使用以下关键词:#{username},请重新命名") if ReversedKeyword.check_exists?(username) - email = params[:email]&.gsub(/\s+/, "") - password = params[:password] - platform = (params[:platform] || 'forge')&.gsub(/\s+/, "") - - ActiveRecord::Base.transaction do - result = autologin_register(username, email, password, platform) - if result[:message].blank? - render_ok({user: result[:user]}) - else - render_error(result[:message]) - end - end - rescue Exception => e - uid_logger_error(e.message) - tip_exception(-1, e.message) - end - - # 其他平台修改用户的信息,这边同步修改 - def remote_update - ActiveRecord::Base.transaction do - user_params = params[:user_params] - user_extension_params = params[:user_extension_params] - - u = User.find_by(login: params[:old_user_login]) - user_mail = u.try(:mail) - - if u.present? - ue = u.user_extension - u.login = user_params["login"] if user_params["login"] - u.mail = user_params["mail"] if user_params["mail"] - u.lastname = user_params["lastname"] if user_params["lastname"] - - ue.gender = user_extension_params["gender"] - ue.school_id = user_extension_params["school_id"] - ue.location = user_extension_params["location"] - ue.location_city = user_extension_params["location_city"] - ue.identity = user_extension_params["identity"] - ue.technical_title = user_extension_params["technical_title"] - ue.student_id = user_extension_params["student_id"] - ue.description = user_extension_params["description"] - ue.save! - u.save! - - sync_params = {} - - if (user_params["mail"] && user_params["mail"] != user_mail) - sync_params = sync_params.merge(email: user_params["mail"]) - end - - if sync_params.present? - interactor = Gitea::User::UpdateInteractor.call(u.login, sync_params) - if interactor.success? - render_ok - else - render_error(interactor.error) - end - end - end - end - rescue Exception => e - uid_logger_error(e.message) - tip_exception(-1, e.message) - end - - # 其他平台同步登录 - def remote_login - @user = User.try_to_login(params[:login], params[:password]) - if @user - successful_authentication(@user) - render_ok({user: {id: @user.id, token: @user.gitea_token}}) - else - render_error("用户不存在") - end - end - - #修改密码 - def remote_password - @user = User.find_by(login: params[:login]) - return render_error("未找到相关用户!") if @user.blank? - - sync_params = { - password: params[:password].to_s, - email: @user.mail - } - - interactor = Gitea::User::UpdateInteractor.call(@user.login, sync_params) - if interactor.success? - @user.update_attribute(:password, params[:password]) - render_ok - else - render_error(interactor.error) - end - end - - - - # 用户注册 - # 注意:用户注册需要兼顾本地版,本地版是不需要验证码及激活码以及使用授权的,注册完成即可使用 - # params[:login] 邮箱或者手机号 - # params[:namespace] 登录名 - # params[:code] 验证码 - # code_type 1:注册手机验证码 8:邮箱注册验证码 - # 本地forge注册入口需要重新更改逻辑 - def register - # type只可能是1或者8 - user = nil - begin - Register::Form.new(register_params).validate! - - user = Users::RegisterService.call(register_params) - password = register_params[:password].strip - - # gitea用户注册, email, username, password - interactor = Gitea::RegisterInteractor.call({username: user.login, email: user.mail, password: password}) - if interactor.success? - gitea_user = interactor.result - result = Gitea::User::GenerateTokenService.call(user.login, password) - user.gitea_token = result['sha1'] - user.gitea_uid = gitea_user[:body]['id'] - if user.save! - UserExtension.create!(user_id: user.id) - successful_authentication(user) - render_ok - end - else - tip_exception(-1, interactor.error) - end - rescue Register::BaseForm::EmailError => e - render_error(-2, e.message) - rescue Register::BaseForm::LoginError => e - render_error(-3, e.message) - rescue Register::BaseForm::PhoneError => e - render_error(-4, e.message) - rescue Register::BaseForm::PasswordFormatError => e - render_error(-5, e.message) - rescue Register::BaseForm::VerifiCodeError => e - render_error(-6, e.message) - rescue Exception => e - Gitea::User::DeleteService.call(user.login) unless user.nil? - uid_logger_error(e.message) - tip_exception(-1, e.message) - end - end - - # 用户登录 - def login - Users::LoginForm.new(account_params).validate! - @user = User.try_to_login(params[:login], params[:password]) - - return normal_status(-2, "错误的账号或密码") if @user.blank? - # user is already in local database - return normal_status(-2, "违反平台使用规范,账号已被锁定") if @user.locked? - - login_control = LimitForbidControl::UserLogin.new(@user) - return normal_status(-2, "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码") if login_control.forbid? - - password_ok = @user.check_password?(params[:password].to_s) - unless password_ok - if login_control.remain_times-1 == 0 - normal_status(-2, "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码") - else - normal_status(-2, "你已经输错密码#{login_control.error_times+1}次,还剩余#{login_control.remain_times-1}次机会") - end - login_control.increment! - return - end - - successful_authentication(@user) - sync_pwd_to_gitea!(@user, {password: params[:password].to_s}) # TODO用户密码未同步 - - # session[:user_id] = @user.id - end - - def change_password - @user = User.find_by(login: params[:login]) - return render_error("未找到相关用户!") if @user.blank? - return render_error("旧密码不正确") unless @user.check_password?(params[:old_password]) - - sync_params = { - password: params[:password].to_s, - email: @user.mail, - login_name: @user.login, - source_id: 0 - } - - interactor = Gitea::User::UpdateInteractor.call(@user.login, sync_params) - if interactor.success? - @user.update_attribute(:password, params[:password]) - render_ok - else - render_error(interactor.error) - end - end - - # 忘记密码 - def reset_password - begin - code = params[:code] - login_type = phone_mail_type(params[:login].strip) - # 获取验证码 - if login_type == 1 - phone = params[:login] - verifi_code = VerificationCode.where(phone: phone, code: code, code_type: 2).last - user = User.find_by_phone(phone) - else - email = params[:login] - verifi_code = VerificationCode.where(email: email, code: code, code_type: 3).last - user = User.find_by_mail(email) #这里有问题,应该是为email,而不是mail 6.13-hs - end - return normal_status(-2, "验证码不正确") if verifi_code.try(:code) != code.strip - return normal_status(-2, "验证码已失效") if !verifi_code&.effective? - return normal_status(-1, "8~16位密码,支持字母数字和符号") unless params[:new_password] =~ CustomRegexp::PASSWORD - - user.password, user.password_confirmation = params[:new_password], params[:new_password_confirmation] - ActiveRecord::Base.transaction do - user.save! - LimitForbidControl::UserLogin.new(user).clear - end - sucess_status - rescue Exception => e - uid_logger_error(e.message) - tip_exception(e.message) - end - end - - def successful_authentication(user) - uid_logger("Successful authentication start: '#{user.login}' from #{request.remote_ip} at #{Time.now.utc}") - # Valid user - self.logged_user = user - # generate a key and set cookie if autologin - - set_autologin_cookie(user) - UserAction.create(:action_id => user.try(:id), :action_type => "Login", :user_id => user.try(:id), :ip => request.remote_ip) - user.update_column(:last_login_on, Time.now) - session[:"#{default_yun_session}"] = user.id - Rails.logger.info("#########_____session_default_yun_session__________###############{default_yun_session}") - # 注册完成后有一天的试用申请(先去掉) - # UserDayCertification.create(user_id: user.id, status: 1) - end - - def set_autologin_cookie(user) - token = Token.get_or_create_permanent_login_token(user, "autologin") - sync_user_token_to_trustie(user.login, token.value) - - cookie_options = { - :value => token.value, - :expires => 1.month.from_now, - :path => '/', - :secure => false, - :httponly => true - } - if edu_setting('cookie_domain').present? - cookie_options = cookie_options.merge(domain: edu_setting('cookie_domain')) - end - cookies[autologin_cookie_name] = cookie_options - cookies.signed[:user_id] ||= user.id - - logger.info("cookies is #{cookies} ======> #{cookies.signed[:user_id]} =====> #{cookies[autologin_cookie_name]}") - end - - def logout - Rails.logger.info("########___logout_current_user____________########{current_user.try(:id)}") - UserAction.create(action_id: User.current.id, action_type: "Logout", user_id: User.current.id, :ip => request.remote_ip) - logout_user - render :json => {status: 1, message: "退出成功!"} - end - - # 检验邮箱是否已被注册及邮箱或者手机号是否合法 - # 参数type为事件类型 1:注册;2:忘记密码;3:绑定 - def valid_email_and_phone - check_mail_and_phone_valid(params[:login], params[:type]) - end - - # 发送验证码 - # params[:login] 手机号或者邮箱号 - # params[:type]为事件通知类型 1:用户注册 2:忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验收手机号有效 # 如果有新的继续后面加 - # 发送验证码:send_type 1:注册手机验证码 2:找回密码手机验证码 3:找回密码邮箱验证码 4:绑定手机 5:绑定邮箱 - # 6:手机验证码登录 7:邮箱验证码登录 8:邮箱注册验证码 9: 验收手机号有效 - def get_verification_code - code = %W(0 1 2 3 4 5 6 7 8 9) - value = params[:login] - type = params[:type].strip.to_i - login_type = phone_mail_type(value) - send_type = verify_type(login_type, type) - verification_code = code.sample(6).join - - sign = Digest::MD5.hexdigest("#{OPENKEY}#{value}") - tip_exception(501, "请求不合理") if sign != params[:smscode] - - logger.info "########### 验证码:#{verification_code}" - logger.info("########get_verification_code: login_type: #{login_type}, send_type:#{send_type}, ") - - # 记录验证码 - check_verification_code(verification_code, send_type, value) - render_ok - end - - # check user's login or email or phone is used - # params[:value] 手机号或者邮箱号或者登录名 - # params[:type] 为事件类型 1:登录名(login) 2:email(邮箱) 3:phone(手机号) - def check - Register::CheckColumnsForm.new(check_params).validate! - render_ok - end - - private - - # type 事件类型 1:用户注册 2:忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验证手机号是否有效 # 如果有新的继续后面加 - # login_type 1:手机类型 2:邮箱类型 - def verify_type login_type, type - case type - when 1 - login_type == 1 ? 1 : 8 - when 2 - login_type == 1 ? 2 : 3 - when 3 - login_type == 1 ? 4 : tip_exception('请填写正确的手机号') - when 4 - login_type == 1 ? tip_exception('请填写正确的邮箱') : 5 - when 5 - login_type == 1 ? 9 : tip_exception('请填写正确的手机号') - end - end - - def generate_login(login) - type = phone_mail_type(login.strip) - - if type == 1 - uid_logger("start register by phone: type is #{type}") - pre = 'p' - email = nil - phone = login - else - uid_logger("start register by email: type is #{type}") - pre = 'm' - email = login - phone = nil - end - code = generate_identifier User, 8, pre - - { login: pre + code, email: email, phone: phone } - end - - def user_params - params.require(:user).permit(:login, :email, :phone) - end - - def account_params - params.require(:account).permit(:login, :password) - end - - def check_params - params.permit(:type, :value) - end - - def register_params - params.permit(:login, :namespace, :password, :code) - end - - def remote_register_params - params.permit(:username, :email, :password, :platform) - end - -end +class AccountsController < ApplicationController + before_action :require_login, only: [:simple_update] + include ApplicationHelper + + #skip_before_action :check_account, :only => [:logout] + + def simple_update + simple_update_params.merge!(username: params[:username]&.gsub(/\s+/, "")) + simple_update_params.merge!(email: params[:email]&.gsub(/\s+/, "")) + simple_update_params.merge!(platform: (params[:platform] || 'forge')&.gsub(/\s+/, "")) + Register::RemoteForm.new(simple_update_params).validate! + + ActiveRecord::Base.transaction do + result = auto_update(current_user, simple_update_params) + if result[:message].blank? + render_ok + else + render_error(result[:message]) + end + end + end + + def index + render json: session + end + + # 其他平台同步注册的用户 + def remote_register + Register::RemoteForm.new(remote_register_params).validate! + username = params[:username]&.gsub(/\s+/, "") + tip_exception("无法使用以下关键词:#{username},请重新命名") if ReversedKeyword.check_exists?(username) + email = params[:email]&.gsub(/\s+/, "") + password = params[:password] + platform = (params[:platform] || 'forge')&.gsub(/\s+/, "") + + ActiveRecord::Base.transaction do + result = autologin_register(username, email, password, platform) + if result[:message].blank? + render_ok({user: result[:user]}) + else + render_error(result[:message]) + end + end + rescue Exception => e + uid_logger_error(e.message) + tip_exception(-1, e.message) + end + + # 其他平台修改用户的信息,这边同步修改 + def remote_update + ActiveRecord::Base.transaction do + user_params = params[:user_params] + user_extension_params = params[:user_extension_params] + + u = User.find_by(login: params[:old_user_login]) + user_mail = u.try(:mail) + + if u.present? + ue = u.user_extension + u.login = user_params["login"] if user_params["login"] + u.mail = user_params["mail"] if user_params["mail"] + u.lastname = user_params["lastname"] if user_params["lastname"] + + ue.gender = user_extension_params["gender"] + ue.school_id = user_extension_params["school_id"] + ue.location = user_extension_params["location"] + ue.location_city = user_extension_params["location_city"] + ue.identity = user_extension_params["identity"] + ue.technical_title = user_extension_params["technical_title"] + ue.student_id = user_extension_params["student_id"] + ue.description = user_extension_params["description"] + ue.save! + u.save! + + sync_params = {} + + if (user_params["mail"] && user_params["mail"] != user_mail) + sync_params = sync_params.merge(email: user_params["mail"]) + end + + if sync_params.present? + interactor = Gitea::User::UpdateInteractor.call(u.login, sync_params) + if interactor.success? + render_ok + else + render_error(interactor.error) + end + end + end + end + rescue Exception => e + uid_logger_error(e.message) + tip_exception(-1, e.message) + end + + # 其他平台同步登录 + def remote_login + @user = User.try_to_login(params[:login], params[:password]) + if @user + successful_authentication(@user) + render_ok({user: {id: @user.id, token: @user.gitea_token}}) + else + render_error("用户不存在") + end + end + + #修改密码 + def remote_password + @user = User.find_by(login: params[:login]) + return render_error("未找到相关用户!") if @user.blank? + + sync_params = { + password: params[:password].to_s, + email: @user.mail + } + + interactor = Gitea::User::UpdateInteractor.call(@user.login, sync_params) + if interactor.success? + @user.update_attribute(:password, params[:password]) + render_ok + else + render_error(interactor.error) + end + end + + + + # 用户注册 + # 注意:用户注册需要兼顾本地版,本地版是不需要验证码及激活码以及使用授权的,注册完成即可使用 + # params[:login] 邮箱或者手机号 + # params[:namespace] 登录名 + # params[:code] 验证码 + # code_type 1:注册手机验证码 8:邮箱注册验证码 + # 本地forge注册入口需要重新更改逻辑 + def register + # type只可能是1或者8 + user = nil + begin + Register::Form.new(register_params).validate! + + user = Users::RegisterService.call(register_params) + password = register_params[:password].strip + + # gitea用户注册, email, username, password + interactor = Gitea::RegisterInteractor.call({username: user.login, email: user.mail, password: password}) + if interactor.success? + gitea_user = interactor.result + result = Gitea::User::GenerateTokenService.call(user.login, password) + user.gitea_token = result['sha1'] + user.gitea_uid = gitea_user[:body]['id'] + if user.save! + UserExtension.create!(user_id: user.id) + successful_authentication(user) + render_ok + end + else + tip_exception(-1, interactor.error) + end + rescue Register::BaseForm::EmailError => e + render_error(-2, e.message) + rescue Register::BaseForm::LoginError => e + render_error(-3, e.message) + rescue Register::BaseForm::PhoneError => e + render_error(-4, e.message) + rescue Register::BaseForm::PasswordFormatError => e + render_error(-5, e.message) + rescue Register::BaseForm::VerifiCodeError => e + render_error(-6, e.message) + rescue Exception => e + Gitea::User::DeleteService.call(user.login) unless user.nil? + uid_logger_error(e.message) + tip_exception(-1, e.message) + end + end + + # 用户登录 + def login + Users::LoginForm.new(account_params).validate! + @user = User.try_to_login(params[:login], params[:password]) + + return normal_status(-2, "错误的账号或密码") if @user.blank? + # user is already in local database + return normal_status(-2, "违反平台使用规范,账号已被锁定") if @user.locked? + + login_control = LimitForbidControl::UserLogin.new(@user) + return normal_status(-2, "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码") if login_control.forbid? + + password_ok = @user.check_password?(params[:password].to_s) + unless password_ok + if login_control.remain_times-1 == 0 + normal_status(-2, "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码") + else + normal_status(-2, "你已经输错密码#{login_control.error_times+1}次,还剩余#{login_control.remain_times-1}次机会") + end + login_control.increment! + return + end + + successful_authentication(@user) + sync_pwd_to_gitea!(@user, {password: params[:password].to_s}) # TODO用户密码未同步 + + # session[:user_id] = @user.id + end + + def change_password + @user = User.find_by(login: params[:login]) + return render_error("未找到相关用户!") if @user.blank? + return render_error("旧密码不正确") unless @user.check_password?(params[:old_password]) + + sync_params = { + password: params[:password].to_s, + email: @user.mail, + login_name: @user.login, + source_id: 0 + } + + interactor = Gitea::User::UpdateInteractor.call(@user.login, sync_params) + if interactor.success? + @user.update_attribute(:password, params[:password]) + render_ok + else + render_error(interactor.error) + end + end + + # 忘记密码 + def reset_password + begin + code = params[:code] + login_type = phone_mail_type(params[:login].strip) + # 获取验证码 + if login_type == 1 + phone = params[:login] + verifi_code = VerificationCode.where(phone: phone, code: code, code_type: 2).last + user = User.find_by_phone(phone) + else + email = params[:login] + verifi_code = VerificationCode.where(email: email, code: code, code_type: 3).last + user = User.find_by_mail(email) #这里有问题,应该是为email,而不是mail 6.13-hs + end + return normal_status(-2, "验证码不正确") if verifi_code.try(:code) != code.strip + return normal_status(-2, "验证码已失效") if !verifi_code&.effective? + return normal_status(-1, "8~16位密码,支持字母数字和符号") unless params[:new_password] =~ CustomRegexp::PASSWORD + + user.password, user.password_confirmation = params[:new_password], params[:new_password_confirmation] + ActiveRecord::Base.transaction do + user.save! + LimitForbidControl::UserLogin.new(user).clear + end + sucess_status + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + end + + def successful_authentication(user) + uid_logger("Successful authentication start: '#{user.login}' from #{request.remote_ip} at #{Time.now.utc}") + # Valid user + self.logged_user = user + # generate a key and set cookie if autologin + + set_autologin_cookie(user) + UserAction.create(:action_id => user.try(:id), :action_type => "Login", :user_id => user.try(:id), :ip => request.remote_ip) + user.update_column(:last_login_on, Time.now) + session[:"#{default_yun_session}"] = user.id + Rails.logger.info("#########_____session_default_yun_session__________###############{default_yun_session}") + # 注册完成后有一天的试用申请(先去掉) + # UserDayCertification.create(user_id: user.id, status: 1) + end + + def set_autologin_cookie(user) + token = Token.get_or_create_permanent_login_token(user, "autologin") + sync_user_token_to_trustie(user.login, token.value) + + cookie_options = { + :value => token.value, + :expires => 1.month.from_now, + :path => '/', + :secure => false, + :httponly => true + } + if edu_setting('cookie_domain').present? + cookie_options = cookie_options.merge(domain: edu_setting('cookie_domain')) + end + cookies[autologin_cookie_name] = cookie_options + cookies.signed[:user_id] ||= user.id + + logger.info("cookies is #{cookies} ======> #{cookies.signed[:user_id]} =====> #{cookies[autologin_cookie_name]}") + end + + def logout + Rails.logger.info("########___logout_current_user____________########{current_user.try(:id)}") + UserAction.create(action_id: User.current.id, action_type: "Logout", user_id: User.current.id, :ip => request.remote_ip) + logout_user + render :json => {status: 1, message: "退出成功!"} + end + + # 检验邮箱是否已被注册及邮箱或者手机号是否合法 + # 参数type为事件类型 1:注册;2:忘记密码;3:绑定 + def valid_email_and_phone + check_mail_and_phone_valid(params[:login], params[:type]) + end + + # 发送验证码 + # params[:login] 手机号或者邮箱号 + # params[:type]为事件通知类型 1:用户注册 2:忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验收手机号有效 # 如果有新的继续后面加 + # 发送验证码:send_type 1:注册手机验证码 2:找回密码手机验证码 3:找回密码邮箱验证码 4:绑定手机 5:绑定邮箱 + # 6:手机验证码登录 7:邮箱验证码登录 8:邮箱注册验证码 9: 验收手机号有效 + def get_verification_code + code = %W(0 1 2 3 4 5 6 7 8 9) + value = params[:login] + type = params[:type].strip.to_i + login_type = phone_mail_type(value) + send_type = verify_type(login_type, type) + verification_code = code.sample(6).join + + sign = Digest::MD5.hexdigest("#{OPENKEY}#{value}") + tip_exception(501, "请求不合理") if sign != params[:smscode] + + logger.info "########### 验证码:#{verification_code}" + logger.info("########get_verification_code: login_type: #{login_type}, send_type:#{send_type}, ") + + # 记录验证码 + check_verification_code(verification_code, send_type, value) + render_ok + end + + # check user's login or email or phone is used + # params[:value] 手机号或者邮箱号或者登录名 + # params[:type] 为事件类型 1:登录名(login) 2:email(邮箱) 3:phone(手机号) + def check + Register::CheckColumnsForm.new(check_params).validate! + render_ok + end + + private + + # type 事件类型 1:用户注册 2:忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验证手机号是否有效 # 如果有新的继续后面加 + # login_type 1:手机类型 2:邮箱类型 + def verify_type login_type, type + case type + when 1 + login_type == 1 ? 1 : 8 + when 2 + login_type == 1 ? 2 : 3 + when 3 + login_type == 1 ? 4 : tip_exception('请填写正确的手机号') + when 4 + login_type == 1 ? tip_exception('请填写正确的邮箱') : 5 + when 5 + login_type == 1 ? 9 : tip_exception('请填写正确的手机号') + end + end + + def generate_login(login) + type = phone_mail_type(login.strip) + + if type == 1 + uid_logger("start register by phone: type is #{type}") + pre = 'p' + email = nil + phone = login + else + uid_logger("start register by email: type is #{type}") + pre = 'm' + email = login + phone = nil + end + code = generate_identifier User, 8, pre + + { login: pre + code, email: email, phone: phone } + end + + def user_params + params.require(:user).permit(:login, :email, :phone) + end + + def account_params + params.require(:account).permit(:login, :password) + end + + def check_params + params.permit(:type, :value) + end + + def register_params + params.permit(:login, :namespace, :password, :code) + end + + def remote_register_params + params.permit(:username, :email, :password, :platform) + end + + def simple_update_params + params.permit(:username, :email, :password, :platform) + end +end diff --git a/app/controllers/concerns/register_helper.rb b/app/controllers/concerns/register_helper.rb index 3a23a210..3d5b0fc0 100644 --- a/app/controllers/concerns/register_helper.rb +++ b/app/controllers/concerns/register_helper.rb @@ -27,4 +27,32 @@ module RegisterHelper result end + def auto_update(user, params={}) + return if params.blank? + result = {message: nil, user: nil} + before_login = user.login + user.login = params[:username] + user.password = params[:password] + user.mail = params[:email] + + if user.save! + sync_params = { + password: params[:password].to_s, + email: params[:email], + login_name: params[:username], + new_name: params[:username], + source_id: 0 + } + + interactor = Gitea::User::UpdateInteractor.call(before_login, sync_params) + if interactor.success? + result[:user] = user + else + result[:message] = '用户同步Gitea失败!' + end + else + result[:message] = user.errors.full_messages.join(",") + return + end + end end diff --git a/app/forms/gitea/user/update_form.rb b/app/forms/gitea/user/update_form.rb index 7912d51f..401282ff 100644 --- a/app/forms/gitea/user/update_form.rb +++ b/app/forms/gitea/user/update_form.rb @@ -4,7 +4,7 @@ class Gitea::User::UpdateForm attr_accessor :username, :email, :admin, :allow_create_organization, :allow_git_hook, :allow_import_local, :full_name, :location, :login_name, :max_repo_creation, :must_change_password, :password, :prohibit_login, - :source_id, :website + :source_id, :website, :new_name validates :username, presence: true validates :email, presence: true, format: { with: EMAIL_REGEX, multiline: true } diff --git a/config/routes.rb b/config/routes.rb index 01ff4fa3..eae7cf8e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -203,6 +203,7 @@ Rails.application.routes.draw do post :remote_password post :change_password post :check + post :simple_update end end From 1358c23ca6e4bd498855dab49f00937df06dcb40 Mon Sep 17 00:00:00 2001 From: yystopf Date: Tue, 24 May 2022 11:43:47 +0800 Subject: [PATCH 043/208] fix: trasfer project member must remove old permission --- app/services/projects/transfer_service.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index 7d1214eb..15726396 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -24,7 +24,8 @@ class Projects::TransferService < ApplicationService private def update_owner - project.members.map{|m| m.destroy! if m.user_id == owner.id || (new_owner.is_a?(Organization) && new_owner.is_member?(m.user_id)) } + project.members.map{|m| m.destroy! if m.user_id == owner.id || project.member(new_owner.id) || (new_owner.is_a?(Organization) && new_owner.is_member?(m.user_id)) } + project.set_owner_permission(new_owner) project.update!(user_id: new_owner.id) end From aa18fe0ad42f3715019038c425acc7b7060eb1e1 Mon Sep 17 00:00:00 2001 From: yystopf Date: Tue, 24 May 2022 11:43:47 +0800 Subject: [PATCH 044/208] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E8=BD=AC?= =?UTF-8?q?=E7=A7=BB=E9=A1=B9=E7=9B=AE=E6=97=B6=E6=96=B0=E6=8E=A5=E5=8F=97?= =?UTF-8?q?=E8=80=85=E9=9C=80=E8=A6=81=E7=A7=BB=E9=99=A4=E5=8E=9F=E6=9D=A5?= =?UTF-8?q?=E7=9A=84=E6=9D=83=E9=99=90=E8=A7=92=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/services/projects/transfer_service.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index 7d1214eb..15726396 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -24,7 +24,8 @@ class Projects::TransferService < ApplicationService private def update_owner - project.members.map{|m| m.destroy! if m.user_id == owner.id || (new_owner.is_a?(Organization) && new_owner.is_member?(m.user_id)) } + project.members.map{|m| m.destroy! if m.user_id == owner.id || project.member(new_owner.id) || (new_owner.is_a?(Organization) && new_owner.is_member?(m.user_id)) } + project.set_owner_permission(new_owner) project.update!(user_id: new_owner.id) end From 8e8ec821f24cb2a38a1af45d043b415518511d3e Mon Sep 17 00:00:00 2001 From: yystopf Date: Tue, 24 May 2022 17:26:10 +0800 Subject: [PATCH 045/208] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20=E6=A0=B9?= =?UTF-8?q?=E6=8D=AE=E9=82=AE=E7=AE=B1=E6=9D=A5=E8=8E=B7=E5=8F=96=E7=94=A8?= =?UTF-8?q?=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/concerns/render_helper.rb | 2 +- app/controllers/users_controller.rb | 6 ++++++ app/views/users/email_search.json.jbuilder | 1 + config/routes.rb | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 app/views/users/email_search.json.jbuilder diff --git a/app/controllers/concerns/render_helper.rb b/app/controllers/concerns/render_helper.rb index 81e85d1c..4b289cd0 100644 --- a/app/controllers/concerns/render_helper.rb +++ b/app/controllers/concerns/render_helper.rb @@ -3,7 +3,7 @@ module RenderHelper render json: { status: 0, message: 'success' }.merge(data) end - def render_error(status = -1, message = '') + def render_error(message = '', status=-1) render json: { status: status, message: message } end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 532f29ba..4b2871c7 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -301,6 +301,12 @@ class UsersController < ApplicationController end end + def email_search + return render_error('请输入email') if params[:email].blank? + @user = User.find_by(mail: params[:email]) + return render_not_found unless @user.present? + end + private def load_user @user = User.find_by_login(params[:id]) || User.find_by(id: params[:id]) diff --git a/app/views/users/email_search.json.jbuilder b/app/views/users/email_search.json.jbuilder new file mode 100644 index 00000000..2b31e182 --- /dev/null +++ b/app/views/users/email_search.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'users/user', locals: { user: @user } diff --git a/config/routes.rb b/config/routes.rb index eae7cf8e..393dceb3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -237,6 +237,7 @@ Rails.application.routes.draw do get :trustie_projects get :trustie_related_projects post :sync_user_info + get :email_search scope '/ci', module: :ci do scope do From e36c7a4b6aa1cb19f3872f21214fe50388275186 Mon Sep 17 00:00:00 2001 From: yystopf Date: Tue, 24 May 2022 18:09:36 +0800 Subject: [PATCH 046/208] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E9=82=AE?= =?UTF-8?q?=E7=AE=B1=E6=90=9C=E7=B4=A2=E4=B8=8D=E9=9C=80=E8=A6=81return=20?= =?UTF-8?q?404?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/users_controller.rb | 1 - app/views/users/email_search.json.jbuilder | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 4b2871c7..98a48c6e 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -304,7 +304,6 @@ class UsersController < ApplicationController def email_search return render_error('请输入email') if params[:email].blank? @user = User.find_by(mail: params[:email]) - return render_not_found unless @user.present? end private diff --git a/app/views/users/email_search.json.jbuilder b/app/views/users/email_search.json.jbuilder index 2b31e182..c077c3fa 100644 --- a/app/views/users/email_search.json.jbuilder +++ b/app/views/users/email_search.json.jbuilder @@ -1 +1,5 @@ -json.partial! 'users/user', locals: { user: @user } +if @user.present? + json.partial! 'users/user', locals: { user: @user } +else + json.null +end From 2ea78b4f208e578ccaf5dc8869d63f820ddc8904 Mon Sep 17 00:00:00 2001 From: yystopf Date: Wed, 25 May 2022 11:47:35 +0800 Subject: [PATCH 047/208] =?UTF-8?q?=E5=A2=9E=E5=8A=A0:=20=E5=B7=B2?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E7=94=A8=E6=88=B7=E5=AF=B9=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=90=8D=E3=80=81=E9=82=AE=E7=AE=B1=E3=80=81=E7=94=B5=E8=AF=9D?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/accounts_controller.rb | 7 +++++- app/forms/register/base_form.rb | 24 ++++++++++++++----- .../register/login_check_columns_form.rb | 19 +++++++++++++++ config/routes.rb | 1 + 4 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 app/forms/register/login_check_columns_form.rb diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index 1af348ab..531095b4 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -1,5 +1,5 @@ class AccountsController < ApplicationController - before_action :require_login, only: [:simple_update] + before_action :require_login, only: [:login_check, :simple_update] include ApplicationHelper #skip_before_action :check_account, :only => [:logout] @@ -333,6 +333,11 @@ class AccountsController < ApplicationController Register::CheckColumnsForm.new(check_params).validate! render_ok end + + def login_check + Register::LoginCheckColumnsForm.new(check_params.merge(user: current_user)).validate! + render_ok + end private diff --git a/app/forms/register/base_form.rb b/app/forms/register/base_form.rb index df5abd87..26382b7e 100644 --- a/app/forms/register/base_form.rb +++ b/app/forms/register/base_form.rb @@ -10,28 +10,40 @@ module Register VerifiCodeError = Class.new(Error) private - def check_login(login) + def check_login(login, user=nil) login = strip(login) raise LoginError, "登录名格式有误" unless login =~ CustomRegexp::LOGIN login_exist = Owner.exists?(login: login) || ReversedKeyword.check_exists?(login) - raise LoginError, '登录名已被使用' if login_exist + if user.present? + raise LoginError, '登录名已被使用' if login_exist && login != user&.login + else + raise LoginError, '登录名已被使用' if login_exist + end end - def check_mail(mail) + def check_mail(mail, user=nil) mail = strip(mail) raise EmailError, "邮件格式有误" unless mail =~ CustomRegexp::EMAIL mail_exist = Owner.exists?(mail: mail) - raise EmailError, '邮箱已被使用' if mail_exist + if user.present? + raise EmailError, '邮箱已被使用' if mail_exist && mail != user&.mail + else + raise EmailError, '邮箱已被使用' if mail_exist + end end - def check_phone(phone) + def check_phone(phone, user=nil) phone = strip(phone) raise PhoneError, "手机号格式有误" unless phone =~ CustomRegexp::PHONE phone_exist = Owner.exists?(phone: phone) - raise PhoneError, '手机号已被使用' if phone_exist + if user.present? + raise PhoneError, '手机号已被使用' if phone_exist && phone != user&.phone + else + raise PhoneError, '手机号已被使用' if phone_exist + end end def check_password(password) diff --git a/app/forms/register/login_check_columns_form.rb b/app/forms/register/login_check_columns_form.rb new file mode 100644 index 00000000..0c6a93af --- /dev/null +++ b/app/forms/register/login_check_columns_form.rb @@ -0,0 +1,19 @@ +module Register + class LoginCheckColumnsForm < Register::BaseForm + attr_accessor :type, :value, :user + + validates :type, presence: true, numericality: true + validates :value, presence: true + validate :check! + + def check! + # params[:type] 为事件类型 1:登录名(login) 2:email(邮箱) 3:phone(手机号) + case strip(type).to_i + when 1 then check_login(strip(value), user) + when 2 then check_mail(strip(value), user) + when 3 then check_phone(strip(value), user) + else raise("type值无效") + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 393dceb3..4d1e3ade 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -203,6 +203,7 @@ Rails.application.routes.draw do post :remote_password post :change_password post :check + post :login_check post :simple_update end end From bd16ca476d8d84e868b778c2a0f0f9a95dbe8a10 Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 26 May 2022 15:08:42 +0800 Subject: [PATCH 048/208] =?UTF-8?q?=E5=A2=9E=E5=8A=A0:=20=E9=80=80?= =?UTF-8?q?=E5=87=BA=E4=BB=93=E5=BA=93api=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/docs/slate/source/includes/_projects.md | 32 ++++++++++++++ public/docs/api.html | 46 +++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/app/docs/slate/source/includes/_projects.md b/app/docs/slate/source/includes/_projects.md index d4899a71..e133bcdd 100644 --- a/app/docs/slate/source/includes/_projects.md +++ b/app/docs/slate/source/includes/_projects.md @@ -849,4 +849,36 @@ await octokit.request('POST /api/:owner/:repo/applied_transfer_projects/cancel.j "created_at": "2021-04-26 09:54", "time_ago": "1分钟前" } +``` + +## 退出项目 +供项目成员(开发者、报告者)退出项目用 + +> 示例: + +```shell +curl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/quit.json +``` + +```javascript +await octokit.request('POST /api/:owner/:repo/quit.json') +``` + +### HTTP 请求 +`POST /api/:owner/:repo/quit.json` + +### 请求参数 +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|owner |是| |string |用户登录名 | +|repo |是| |string |项目标识identifier | + + +> 返回的JSON示例: + +```json +{ + "status": 0, + "message": "success" +} ``` \ No newline at end of file diff --git a/public/docs/api.html b/public/docs/api.html index 45da6440..8ef6924b 100644 --- a/public/docs/api.html +++ b/public/docs/api.html @@ -467,6 +467,9 @@
  • 取消迁移项目
  • +
  • + 退出项目 +
  • @@ -6026,6 +6029,49 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat "created_at": "2021-04-26 09:54", "time_ago": "1分钟前" } +

    退出项目

    +

    供项目成员(开发者、报告者)退出项目用

    + +
    +

    示例:

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

    HTTP 请求

    +

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

    +

    请求参数

    + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    + +
    +

    返回的JSON示例:

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

    Repositories

    仓库详情

    仓库详情

    From a6e50d2a250168a0cb981d403a6a4ec65a37ea5b Mon Sep 17 00:00:00 2001 From: yystopf Date: Fri, 27 May 2022 10:48:20 +0800 Subject: [PATCH 049/208] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20devops=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E6=BF=80=E6=B4=BB=E9=A1=B9=E7=9B=AE=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/ci/projects_controller.rb | 2 +- .../concerns/ci/cloud_account_manageable.rb | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 69644e7a..06e51537 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -40,9 +40,9 @@ class Ci::ProjectsController < Ci::BaseController begin ActiveRecord::Base.transaction do if @repo + @repo.destroy! if @repo&.repo_user_id == 0 return render_error('该项目已经激活') if @repo.repo_active? @repo.activate!(@project) - return render_ok else @repo = Ci::Repo.auto_create!(@ci_user, @project) @ci_user.update_column(:user_syncing, false) diff --git a/app/controllers/concerns/ci/cloud_account_manageable.rb b/app/controllers/concerns/ci/cloud_account_manageable.rb index 9f0998ed..f2157657 100644 --- a/app/controllers/concerns/ci/cloud_account_manageable.rb +++ b/app/controllers/concerns/ci/cloud_account_manageable.rb @@ -160,9 +160,9 @@ module Ci::CloudAccountManageable state = SecureRandom.hex(8) # redirect_uri eg: # https://localhost:3000/login/oauth/authorize?client_id=94976481-ad0e-4ed4-9247-7eef106007a2&redirect_uri=http%3A%2F%2F121.69.81.11%3A80%2Flogin&response_type=code&state=9cab990b9cfb1805 - redirect_uri = CGI.escape("#{@cloud_account.drone_url}/login") - clientId = client_id(oauth) - grant_url = "#{Gitea.gitea_config[:domain]}/login/oauth/authorize?client_id=#{clientId}&redirect_uri=#{redirect_uri}&response_type=code&state=#{state}" + # redirect_uri = CGI.escape("#{@cloud_account.drone_url}/login") + # clientId = client_id(oauth) + grant_url = "#{@cloud_account.drone_url}/login" logger.info "[gitea] grant_url: #{grant_url}" conn = Faraday.new(url: grant_url) do |req| @@ -179,7 +179,7 @@ module Ci::CloudAccountManageable def drone_oauth_user!(url, state) logger.info "[drone] drone_oauth_user url: #{url}" - conn = Faraday.new(url: "#{Gitea.gitea_config[:domain]}#{url}") do |req| + conn = Faraday.new(url: url) do |req| req.request :url_encoded req.adapter Faraday.default_adapter req.headers["cookie"] = "_session_=#{SecureRandom.hex(28)}; _oauth_state_=#{state}" @@ -188,8 +188,8 @@ module Ci::CloudAccountManageable response = conn.get logger.info "[drone] response headers: #{response.headers}" - true - # response.headers['location'].include?('error') ? false : true + # true + response.headers['location'].include?('error') ? false : true end private From 17fb43cea83ca814ed5c66add62de41d7b1fe2ab Mon Sep 17 00:00:00 2001 From: xiaoxiaoqiong Date: Mon, 30 May 2022 10:24:39 +0800 Subject: [PATCH 050/208] =?UTF-8?q?render=5Ferror=E6=9B=BF=E6=8D=A2?= =?UTF-8?q?=E6=88=90tip=5Fexception?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/ci/projects_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 06e51537..c358611e 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -41,7 +41,7 @@ class Ci::ProjectsController < Ci::BaseController ActiveRecord::Base.transaction do if @repo @repo.destroy! if @repo&.repo_user_id == 0 - return render_error('该项目已经激活') if @repo.repo_active? + return tip_exception('该项目已经激活') if @repo.repo_active? @repo.activate!(@project) else @repo = Ci::Repo.auto_create!(@ci_user, @project) @@ -55,7 +55,7 @@ class Ci::ProjectsController < Ci::BaseController end render_ok rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end end From 6448a30e0301ed24eea251f1f766d1755f8991eb Mon Sep 17 00:00:00 2001 From: xiaoxiaoqiong Date: Mon, 30 May 2022 10:25:43 +0800 Subject: [PATCH 051/208] =?UTF-8?q?render=5Ferror=E6=9B=BF=E6=8D=A2?= =?UTF-8?q?=E6=88=90tip=5Fexception?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ci/cloud_accounts_controller.rb | 34 +++++++++---------- app/controllers/ci/pipelines_controller.rb | 24 ++++++------- app/controllers/ci/projects_controller.rb | 6 ++-- app/controllers/ci/secrets_controller.rb | 6 ++-- app/controllers/ci/templates_controller.rb | 6 ++-- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/app/controllers/ci/cloud_accounts_controller.rb b/app/controllers/ci/cloud_accounts_controller.rb index 7d829dbd..d22b2ba4 100644 --- a/app/controllers/ci/cloud_accounts_controller.rb +++ b/app/controllers/ci/cloud_accounts_controller.rb @@ -14,12 +14,12 @@ class Ci::CloudAccountsController < Ci::BaseController def create flag, msg = check_bind_cloud_account! - return render_error(msg) if flag === true + return tip_exception(msg) if flag === true ActiveRecord::Base.transaction do @cloud_account = bind_account! if @cloud_account.blank? - render_error('激活失败, 请检查你的云服务器信息是否正确.') + tip_exception('激活失败, 请检查你的云服务器信息是否正确.') raise ActiveRecord::Rollback else current_user.set_drone_step!(User::DEVOPS_UNVERIFIED) @@ -27,17 +27,17 @@ class Ci::CloudAccountsController < Ci::BaseController end end rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end def activate - return render_error('请先认证') unless current_user.ci_certification? + return tip_exception('请先认证') unless current_user.ci_certification? begin @cloud_account = Ci::CloudAccount.find params[:id] ActiveRecord::Base.transaction do if @repo - return render_error('该项目已经激活') if @repo.repo_active? + return tip_exception('该项目已经激活') if @repo.repo_active? @repo.activate!(@project) else @repo = Ci::Repo.auto_create!(@ci_user, @project) @@ -50,7 +50,7 @@ class Ci::CloudAccountsController < Ci::BaseController end render_ok rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end end @@ -59,39 +59,39 @@ class Ci::CloudAccountsController < Ci::BaseController def bind flag, msg = check_bind_cloud_account! - return render_error(msg) if flag === true + return tip_exception(msg) if flag === true ActiveRecord::Base.transaction do @cloud_account = bind_account! if @cloud_account.blank? - render_error('激活失败, 请检查你的云服务器信息是否正确.') + tip_exception('激活失败, 请检查你的云服务器信息是否正确.') raise ActiveRecord::Rollback else current_user.set_drone_step!(User::DEVOPS_UNVERIFIED) end end rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end def trustie_bind account = params[:account].to_s - return render_error("account不能为空.") if account.blank? + return tip_exception("account不能为空.") if account.blank? flag, msg = check_trustie_bind_cloud_account! - return render_error(msg) if flag === true + return tip_exception(msg) if flag === true ActiveRecord::Base.transaction do @cloud_account = trustie_bind_account! if @cloud_account.blank? - render_error('激活失败, 请检查你的云服务器信息是否正确.') + tip_exception('激活失败, 请检查你的云服务器信息是否正确.') raise ActiveRecord::Rollback else current_user.set_drone_step!(User::DEVOPS_UNVERIFIED) end end rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end def unbind @@ -107,18 +107,18 @@ class Ci::CloudAccountsController < Ci::BaseController render_ok end rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end def oauth_grant password = params[:password].to_s - return render_error('你输入的密码不正确.') unless current_user.check_password?(password) + return tip_exception('你输入的密码不正确.') unless current_user.check_password?(password) oauth = current_user.oauths.last - return render_error("服务器出小差了.") if oauth.blank? + return tip_exception("服务器出小差了.") if oauth.blank? result = gitea_oauth_grant!(password, oauth) - return render_error('授权失败.') unless result === true + return tip_exception('授权失败.') unless result === true current_user.set_drone_step!(User::DEVOPS_CERTIFICATION) end diff --git a/app/controllers/ci/pipelines_controller.rb b/app/controllers/ci/pipelines_controller.rb index ea8b25f7..28805965 100644 --- a/app/controllers/ci/pipelines_controller.rb +++ b/app/controllers/ci/pipelines_controller.rb @@ -30,7 +30,7 @@ class Ci::PipelinesController < Ci::BaseController ActiveRecord::Base.transaction do size = Ci::Pipeline.where('branch=? and identifier=? and owner=?', params[:branch], params[:repo], params[:owner]).size if size > 0 - render_error("#{params[:branch]}分支已经存在流水线!") + tip_exception("#{params[:branch]}分支已经存在流水线!") return end pipeline = Ci::Pipeline.new(pipeline_name: params[:pipeline_name], file_name: params[:file_name],owner: params[:owner], @@ -53,7 +53,7 @@ class Ci::PipelinesController < Ci::BaseController render_ok({id: pipeline.id}) end rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end # 在代码库创建文件 @@ -118,7 +118,7 @@ class Ci::PipelinesController < Ci::BaseController end render_ok rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end def destroy @@ -132,7 +132,7 @@ class Ci::PipelinesController < Ci::BaseController end render_ok rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end def content @@ -182,7 +182,7 @@ class Ci::PipelinesController < Ci::BaseController render_ok end rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end def update_stage @@ -192,7 +192,7 @@ class Ci::PipelinesController < Ci::BaseController end render_ok rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end def delete_stage @@ -205,7 +205,7 @@ class Ci::PipelinesController < Ci::BaseController render_ok end rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end def update_stage_index(pipeline_id, show_index, diff) @@ -229,7 +229,7 @@ class Ci::PipelinesController < Ci::BaseController unless steps.empty? steps.each do |step| unless step[:template_id] - render_error('请选择模板!') + tip_exception('请选择模板!') return end if !step[:id] @@ -246,7 +246,7 @@ class Ci::PipelinesController < Ci::BaseController render_ok end rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end def create_stage_step @@ -262,7 +262,7 @@ class Ci::PipelinesController < Ci::BaseController render_ok end rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end def update_stage_step @@ -279,7 +279,7 @@ class Ci::PipelinesController < Ci::BaseController render_ok end rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end def delete_stage_step @@ -289,6 +289,6 @@ class Ci::PipelinesController < Ci::BaseController end render_ok rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end end diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index c358611e..53f11fb2 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -30,12 +30,12 @@ class Ci::ProjectsController < Ci::BaseController @file = interactor.result render_result(1, "更新成功") else - render_error(interactor.error) + tip_exception(interactor.error) end end def activate - return render_error('你还未认证') unless current_user.ci_certification? + return tip_exception('你还未认证') unless current_user.ci_certification? begin ActiveRecord::Base.transaction do @@ -60,7 +60,7 @@ class Ci::ProjectsController < Ci::BaseController end def deactivate - return render_error('该项目已经取消激活') if !@repo.repo_active? + return tip_exception('该项目已经取消激活') if !@repo.repo_active? @project.update_column(:open_devops, false) @repo.deactivate_repos! diff --git a/app/controllers/ci/secrets_controller.rb b/app/controllers/ci/secrets_controller.rb index 324f0bb5..3057824e 100644 --- a/app/controllers/ci/secrets_controller.rb +++ b/app/controllers/ci/secrets_controller.rb @@ -20,14 +20,14 @@ class Ci::SecretsController < Ci::BaseController if result["id"] render_ok else - render_error(result["message"]) + tip_exception(result["message"]) end else result = Ci::Drone::API.new(@ci_user.user_hash, ci_drone_url, params[:owner], params[:repo], options).create_secret if result["id"] render_ok else - render_error(result["message"]) + tip_exception(result["message"]) end end end @@ -39,7 +39,7 @@ class Ci::SecretsController < Ci::BaseController Ci::Drone::API.new(@ci_user.user_hash, ci_drone_url, params[:owner], params[:repo], {name: name}).delete_secret render_ok else - render_error("参数名不能为空") + tip_exception("参数名不能为空") end rescue Exception => ex render_ok diff --git a/app/controllers/ci/templates_controller.rb b/app/controllers/ci/templates_controller.rb index cb49590d..22c20952 100644 --- a/app/controllers/ci/templates_controller.rb +++ b/app/controllers/ci/templates_controller.rb @@ -50,7 +50,7 @@ class Ci::TemplatesController < Ci::BaseController end render_ok rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end def update @@ -63,7 +63,7 @@ class Ci::TemplatesController < Ci::BaseController ) render_ok rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end def destroy @@ -73,7 +73,7 @@ class Ci::TemplatesController < Ci::BaseController end render_ok rescue Exception => ex - render_error(ex.message) + tip_exception(ex.message) end #======流水线模板查询=====# From d6fd85f536f2b328faae42572da3337a4aa4417f Mon Sep 17 00:00:00 2001 From: xiaoxiaoqiong Date: Mon, 30 May 2022 11:18:24 +0800 Subject: [PATCH 052/208] =?UTF-8?q?Repository.url=E9=87=8D=E5=86=99?= =?UTF-8?q?=EF=BC=8C=E5=AF=BC=E5=85=A5=E9=A1=B9=E7=9B=AE=E6=97=B6=E4=B8=BA?= =?UTF-8?q?=E7=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/ci/pipelines_controller.rb | 1 + app/models/repository.rb | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/app/controllers/ci/pipelines_controller.rb b/app/controllers/ci/pipelines_controller.rb index 28805965..d6eee138 100644 --- a/app/controllers/ci/pipelines_controller.rb +++ b/app/controllers/ci/pipelines_controller.rb @@ -81,6 +81,7 @@ class Ci::PipelinesController < Ci::BaseController repo_branch: pipeline.branch, repo_config: pipeline.file_name } + Rails.logger.info("########create_params===#{create_params.to_json}") repo = Ci::Repo.create_repo(create_params) repo end diff --git a/app/models/repository.rb b/app/models/repository.rb index a012b449..12cc2fa3 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -48,6 +48,10 @@ class Repository < ApplicationRecord self.identifier.parameterize end + def url + self['url'].blank? ? "#{Rails.application.config_for(:configuration)['platform_url']}/#{self.owner&.login}/#{self.identifier}.git" : self['url'] + end + # with repository is mirror def set_mirror! self.build_mirror(status: Mirror.statuses[:waiting]).save From 169dc84e22f933e9d0b5d1e40fecc4a0ac8dacd4 Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 30 May 2022 14:50:12 +0800 Subject: [PATCH 053/208] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20=E7=BB=84?= =?UTF-8?q?=E7=BB=87=E4=B8=8B=E9=A1=B9=E7=9B=AE=E9=9C=80=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E4=B8=BA=E5=85=AC=E6=9C=89=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/views/organizations/projects/index.json.jbuilder | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/organizations/projects/index.json.jbuilder b/app/views/organizations/projects/index.json.jbuilder index a663b0c1..5a4615fc 100644 --- a/app/views/organizations/projects/index.json.jbuilder +++ b/app/views/organizations/projects/index.json.jbuilder @@ -1,6 +1,6 @@ json.total_count @projects.total_count json.projects @projects.each do |project| - json.(project, :id, :name, :identifier, :description, :forked_count, :praises_count, :forked_from_project_id) + json.(project, :id, :name, :identifier, :description, :forked_count, :praises_count, :forked_from_project_id, :is_public) json.mirror_url project.repository&.mirror_url json.type project.numerical_for_project_type json.praised project.praised_by?(current_user) From 9427db3e5a9c1856e8519506fada75c7dc68248b Mon Sep 17 00:00:00 2001 From: yystopf Date: Tue, 31 May 2022 16:34:08 +0800 Subject: [PATCH 054/208] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20=E9=80=9A?= =?UTF-8?q?=E7=94=A8=E6=B6=88=E6=81=AF=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/notices_controller.rb | 14 ++++++- app/jobs/send_template_message_job.rb | 11 +++++ app/models/message_template/custom_tip.rb | 51 +++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 app/models/message_template/custom_tip.rb diff --git a/app/controllers/notices_controller.rb b/app/controllers/notices_controller.rb index bf7eb21b..7b67412e 100644 --- a/app/controllers/notices_controller.rb +++ b/app/controllers/notices_controller.rb @@ -1,7 +1,7 @@ class NoticesController < ApplicationController def create - tip_exception("参数有误") if params["source"].blank? + return tip_exception("参数有误") if params["source"].blank? user_id = params[:user_id] if params["source"] == "CompetitionBegin" @@ -13,9 +13,21 @@ class NoticesController < ApplicationController elsif params["source"] == "CompetitionReview" competition_id = params[:competition_id] SendTemplateMessageJob.perform_later('CompetitionReview', user_id, competition_id) + elsif params["source"] == "CustomTip" + users_id = params[:users_id] + props = params[:props].to_unsafe_hash + return tip_exception("参数有误") unless props.is_a?(Hash) && users_id.is_a?(Array) + template_id = params[:template_id] + SendTemplateMessageJob.perform_later('CustomTip', users_id, template_id, props) else tip_exception("#{params["source"]}未配置") end render_ok end + + + private + def params_props + params.require(:notice).permit(:props) + end end diff --git a/app/jobs/send_template_message_job.rb b/app/jobs/send_template_message_job.rb index 9cc819a1..bec3f7b0 100644 --- a/app/jobs/send_template_message_job.rb +++ b/app/jobs/send_template_message_job.rb @@ -4,6 +4,17 @@ class SendTemplateMessageJob < ApplicationJob def perform(source, *args) Rails.logger.info "SendTemplateMessageJob [args] #{args}" case source + when 'CustomTip' + receivers_id, template_id, props = args[0], args[1], args[2] + template = MessageTemplate.find_by_id(template_id) + return unless template.present? + receivers = User.where(id: receivers_id) + receivers_string, content, notification_url = MessageTemplate::CustomTip.get_message_content(receivers, template, props) + Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {receivers_id: receivers_id, template_id: template_id, props: props}) + receivers.find_each do |receiver| + receivers_email_string, email_title, email_content = MessageTemplate::CustomTip.get_email_message_content(receiver, template, props) + Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) + end when 'FollowTip' watcher_id = args[0] watcher = Watcher.find_by_id(watcher_id) diff --git a/app/models/message_template/custom_tip.rb b/app/models/message_template/custom_tip.rb new file mode 100644 index 00000000..6c5a3345 --- /dev/null +++ b/app/models/message_template/custom_tip.rb @@ -0,0 +1,51 @@ +# == 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::CustomTip < MessageTemplate + + # MessageTemplate::CustomTip.get_message_content(User.where(login: 'yystopf'), "hahah") + def self.get_message_content(receivers, template, props={}) + return '', '', '' if receivers.blank? || template.blank? + content = template.sys_notice + notification_url = template.notification_url + props.each do |k, v| + content.gsub!("{#{k}}", v) + notification_url.gsub!("{#{k}}", v) + end + return receivers_string(receivers), content, notification_url + rescue => e + Rails.logger.info("MessageTemplate::CustomTip.get_message_content [ERROR] #{e}") + return '', '', '' + end + + def self.get_email_message_content(receiver, template, props={}) + return '', '', '' if receiver.blank? || template.blank? + title = template.email_title + content = template.email + props.each do |k, v| + title.gsub!("{#{k}}", v) + content.gsub!("{#{k}}", v) + end + content.gsub!('{receiver}', receiver&.real_name) + title.gsub!('{platform}', PLATFORM) + content.gsub!('{platform}', PLATFORM) + + return receiver&.mail, title, content + rescue => e + Rails.logger.info("MessageTemplate::CustomTip.get_email_message_content [ERROR] #{e}") + return '', '', '' + end +end \ No newline at end of file From 9a17c4865f4188cd5d266caf3ea2d08d4e185532 Mon Sep 17 00:00:00 2001 From: xiaoxiaoqiong Date: Wed, 1 Jun 2022 09:27:18 +0800 Subject: [PATCH 055/208] =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E5=90=8D=E7=A7=B0?= =?UTF-8?q?=E5=92=8C=E6=8F=8F=E8=BF=B0=E5=A2=9E=E5=8A=A0=E6=95=8F=E6=84=9F?= =?UTF-8?q?=E8=AF=8D=E6=A3=80=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/project.rb | 839 +++++++++++++++++++++--------------------- 1 file changed, 423 insertions(+), 416 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 2e47c000..9c77685f 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1,416 +1,423 @@ - -# == Schema Information -# -# Table name: projects -# -# id :integer not null, primary key -# name :string(255) default(""), not null -# description :text(4294967295) -# homepage :string(255) default("") -# is_public :boolean default("1"), not null -# parent_id :integer -# created_on :datetime -# updated_on :datetime -# identifier :string(255) -# status :integer default("1"), not null -# lft :integer -# rgt :integer -# inherit_members :boolean default("0"), not null -# project_type :integer default("0") -# hidden_repo :boolean default("0"), not null -# attachmenttype :integer default("1") -# user_id :integer -# dts_test :integer default("0") -# enterprise_name :string(255) -# organization_id :integer -# project_new_type :integer -# gpid :integer -# forked_from_project_id :integer -# forked_count :integer default("0") -# publish_resource :integer default("0") -# visits :integer default("0") -# hot :integer default("0") -# invite_code :string(255) -# qrcode :string(255) -# qrcode_expiretime :integer default("0") -# script :text(65535) -# training_status :integer default("0") -# rep_identifier :string(255) -# project_category_id :integer -# project_language_id :integer -# license_id :integer -# ignore_id :integer -# praises_count :integer default("0") -# watchers_count :integer default("0") -# issues_count :integer default("0") -# pull_requests_count :integer default("0") -# language :string(255) -# versions_count :integer default("0") -# issue_tags_count :integer default("0") -# closed_issues_count :integer default("0") -# open_devops :boolean default("0") -# gitea_webhook_id :integer -# open_devops_count :integer default("0") -# recommend :boolean default("0") -# platform :integer default("0") -# default_branch :string(255) default("master") -# website :string(255) -# lesson_url :string(255) -# is_pinned :boolean default("0") -# recommend_index :integer default("0") -# -# Indexes -# -# index_projects_on_forked_from_project_id (forked_from_project_id) -# index_projects_on_identifier (identifier) -# index_projects_on_invite_code (invite_code) -# index_projects_on_is_public (is_public) -# index_projects_on_lft (lft) -# index_projects_on_license_id (license_id) -# index_projects_on_name (name) -# index_projects_on_platform (platform) -# index_projects_on_project_category_id (project_category_id) -# index_projects_on_project_language_id (project_language_id) -# index_projects_on_project_type (project_type) -# index_projects_on_recommend (recommend) -# index_projects_on_rgt (rgt) -# index_projects_on_status (status) -# index_projects_on_updated_on (updated_on) -# - - - - -class Project < ApplicationRecord - include Matchable - include Publicable - include Watchable - include ProjectOperable - include Dcodes - - # common:开源托管项目 - # mirror:普通镜像项目,没有定时同步功能 - # sync_mirror:同步镜像项目,有系统定时同步功能,且用户可手动同步操作 - # - enum project_type: { sync_mirror: 2, mirror: 1, common: 0 } - - # forge: trustie平台项目, educoder: educoder平台项目, 默认为forge平台 - enum platform: { forge: 0, educoder: 1 } - - belongs_to :ignore, optional: true - belongs_to :license, optional: true - belongs_to :owner, class_name: 'Owner', foreign_key: :user_id, optional: true - belongs_to :organization_extension, foreign_key: :user_id, primary_key: :organization_id, optional: true, counter_cache: :num_projects - belongs_to :project_category, optional: true , :counter_cache => true - belongs_to :project_language, optional: true , :counter_cache => true - belongs_to :forked_from_project, class_name: 'Project', optional: true, foreign_key: :forked_from_project_id - has_many :project_trends, dependent: :destroy - has_many :watchers, as: :watchable, dependent: :destroy - has_many :fork_users, dependent: :destroy - has_many :forked_users, class_name: 'ForkUser', foreign_key: :fork_project_id, dependent: :destroy - has_many :forked_projects, class_name: 'Project', foreign_key: :forked_from_project_id - has_one :project_educoder, dependent: :destroy - - has_one :project_score, dependent: :destroy - has_one :repository, dependent: :destroy - has_many :pull_requests, dependent: :destroy - has_many :issue_tags, -> { order("issue_tags.created_at DESC") }, dependent: :destroy - has_many :issues, dependent: :destroy - # has_many :user_grades, dependent: :destroy - has_many :attachments, as: :container, dependent: :destroy - has_one :project_score, dependent: :destroy - has_many :versions, -> { order("versions.created_on DESC, versions.name DESC") }, dependent: :destroy - has_many :praise_treads, as: :praise_tread_object, dependent: :destroy - has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position" - has_one :project_detail, dependent: :destroy - has_many :project_units, dependent: :destroy - has_one :applied_transfer_project,-> { order created_at: :desc }, dependent: :destroy - has_many :pinned_projects, dependent: :destroy - has_many :has_pinned_users, through: :pinned_projects, source: :user - has_many :webhooks, class_name: "Gitea::Webhook", primary_key: :gpid, foreign_key: :repo_id - after_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 - before_destroy :decre_project_common, :decre_forked_from_project_count - after_destroy :decre_user_statistic, :decre_platform_statistic - scope :project_statics_select, -> {select(:id,:name, :is_public, :identifier, :status, :project_type, :user_id, :forked_count, :description, :visits, :project_category_id, :project_language_id, :license_id, :ignore_id, :watchers_count, :created_on)} - scope :no_anomory_projects, -> {where("projects.user_id is not null and projects.user_id != ?", 2)} - scope :recommend, -> { visible.project_statics_select.where(recommend: true) } - scope :pinned, -> {where(is_pinned: true)} - - delegate :content, to: :project_detail, allow_nil: true - delegate :name, to: :license, prefix: true, allow_nil: true - - def self.all_visible(user_id=nil) - user_projects_sql = Project.joins(:owner).where(users: {type: 'User'}).to_sql - org_public_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension).where(organization_extensions: {visibility: 'common'})).to_sql - if user_id.present? - org_limit_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension).where(organization_extensions: {visibility: 'limited'})).to_sql - org_privacy_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension, :organization_users).where(organization_extensions: {visibility: 'privacy'}, organization_users: {user_id: user_id})).to_sql - return Project.from("( #{ user_projects_sql } UNION #{ org_public_projects_sql } UNION #{ org_limit_projects_sql } UNION #{org_privacy_projects_sql} ) AS projects").visible - else - return Project.from("( #{ user_projects_sql } UNION #{ org_public_projects_sql } ) AS projects").visible - end - end - - def reset_cache_data - CacheAsyncResetJob.set(wait: 5.seconds).perform_later("project_common_service", self.id) - if changes[:user_id].present? - CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: -1}, changes[:user_id].first) - CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: 1}, changes[:user_id].last) - end - if changes[:project_language_id].present? - first_language = ProjectLanguage.find_by_id(changes[:project_language_id].first) - last_language = ProjectLanguage.find_by_id(changes[:project_language_id].last) - CacheAsyncSetJob.perform_later("user_statistic_service", {project_language_count_key: first_language&.name, project_language_count: -1}, self.user_id) - CacheAsyncSetJob.perform_later("user_statistic_service", {project_language_count_key: last_language&.name, project_language_count: 1}, self.user_id) - CacheAsyncSetJob.perform_later("platform_statistic_service", {project_language_count_key: first_language&.name, project_language_count: -1}) - CacheAsyncSetJob.perform_later("platform_statistic_service", {project_language_count_key: last_language&.name, project_language_count: 1}) - end - if changes[:is_public].present? - if changes[:is_public][0] && !changes[:is_public][1] - CacheAsyncClearJob.perform_later('project_rank_service', self.id) - end - if !changes[:is_public][0] && changes[:is_public][1] - $redis_cache.srem("v2-project-rank-deleted", self.id) - end - end - end - - def decre_project_common - CacheAsyncClearJob.perform_later('project_common_service', self.id) - end - - def decre_forked_from_project_count - forked_project = self.forked_from_project - if forked_project.present? - forked_project.decrement(:forked_count, 1) - forked_project.save - end - end - - def incre_user_statistic - CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: 1, project_language_count_key: self.project_language&.name, project_language_count: 1}, self.user_id) - end - - def decre_user_statistic - CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: -1, project_language_count_key: self.project_language&.name, project_language_count: -1}, self.user_id) - end - - def incre_platform_statistic - CacheAsyncSetJob.perform_later("platform_statistic_service", {project_count: 1, project_language_count_key: self.project_language&.name, project_language_count: 1}) - end - - def decre_platform_statistic - CacheAsyncSetJob.perform_later("platform_statistic_service", {project_count: -1, project_language_count_key: self.project_language&.name, project_language_count: -1}) - end - - def is_full_public - owner = self.owner - if owner.is_a?(Organization) - return self.is_public && owner&.visibility == "common" - else - return self.is_public - end - end - - def reset_unmember_followed - if changes[:is_public].present? && changes[:is_public] == [true, false] - self.watchers.where.not(user_id: self.all_collaborators).destroy_all - end - end - - def set_invite_code - if self.invite_code.nil? - self.invite_code= self.generate_dcode('invite_code', 6) - end - end - - def set_recommend_and_is_pinned - self.recommend = self.recommend_index.zero? ? false : true - # 私有项目不允许设置精选和推荐 - unless self.is_public - self.recommend = false - self.recommend_index = 0 - self.is_pinned = false - end - end - - def self.search_project(search) - ransack(name_or_identifier_cont: search) - end - # 创建者 - def creator - User.find(user_id).full_name - end - - def members_user_infos - members.joins(:roles).where("roles.name in ('Manager', 'Developer', 'Reporter')").joins("left join users on members.user_id = users.id ").includes(:user).where("users.type = ?", "User") - # members.joins("left join users on members.user_id = users.id").select("users.id", "users.login","users.firstname","users.lastname") - # .pluck("users.id", "users.login","users.lastname", "users.firstname") - end - - def to_param - self.identifier.parameterize - end - - def get_issues_count(status_id) - if status_id.present? - self&.issues.issue_issue.select(:id, :status_id).where(status_id: status_id)&.pluck(:id).size - else - self&.issues.issue_issue.select(:id)&.pluck(:id).size - end - end - - def get_pull_requests_count(status_id) - if status_id.present? - self&.pull_requests.select(:id, :status).where(status: status_id)&.pluck(:id).size - else - self&.pull_requests.select(:id)&.pluck(:id).size - end - end - - #创建项目管理员 - def check_project_members - return if owner.is_a?(Organization) - unless members.present? && members.exists?(user_id: self.user_id) - member_params = { - user_id: self.user_id, - project_id: self.id - } - user_member = Member.new(member_params) - if user_member.save - role_id = Role.select(:id,:position).where(position: 3)&.first&.id - MemberRole.create!(member_id: user_member.id ,role_id: role_id) - end - end - end - - - def self.init_bluck_repository - Project.includes(:repository).find_each do |project| - puts project.id - next if project.owner.blank? - if project.repository.blank? - puts "########### start create repositoy #############" - Repository.create!(project_id: project.id, identifier: Project.generate_identifier, user_id: project&.owner&.id) - end - end - end - - def self.generate_identifier - str_arr = (("a".."z").to_a + ("A".."Z").to_a) - - str = str_arr.shuffle[0..8].join - while Repository.exists?(identifier: str) - str = str_arr.shuffle[0..8].join - end - str - end - - def self.list_user_projects(user_id) - projects = Project.is_private.select(:id,:user_id) - user_not_show_1 = projects.where("user_id != ?",user_id).pluck(:id).uniq - - user_show_2 = projects.joins(:members).where("members.user_id = ?", user_id).pluck(:id).uniq - Project.where.not(id: (user_not_show_1 - user_show_2).uniq) - end - - def members_count - members.select(:id).size - end - - - def can_visited? - is_public? || User.current.admin? || member?(User.current) - end - - def releases_size(current_user_id, type) - if current_user_id == self.user_id && type.to_s == "all" - self.repository.version_releases_count - else - self.repository.version_releases.releases_size - end - end - - def contributor_users - self.pull_requests.select(:user_id).pluck(:user_id).uniq.size - end - - def open_issues_count - issues_count - closed_issues_count - end - - def numerical_for_project_type - self.class.name.constantize.project_types["#{self.project_type}"] - end - - def watched_by? user - watchers.pluck(:user_id).include? user&.id - end - - def praised_by? user - praise_treads.pluck(:user_id).include? user&.id - end - - def get_premission user - return "Owner" if owner?(user) - return "Manager" if manager?(user) - return "Developer" if develper?(user) - return "Reporter" if reporter?(user) - - return "" - end - - def fork_project - Project.find_by(id: self.forked_from_project_id) - end - - def self.members_projects(member_user_id) - joins(:members).where(members: { user_id: member_user_id}) - end - - def self.find_with_namespace(namespace_path, identifier) - logger.info "########namespace_path: #{namespace_path} ########identifier: #{identifier} " - - user = Owner.find_by_login namespace_path - user = Owner.new(login: namespace_path) if user.nil? - project = user&.projects&.find_by(identifier: identifier) || Project.find_by(identifier: "#{namespace_path}/#{identifier}") - return nil if project.blank? - - [project, user] - end - - def ci_reactivate? - open_devops_count > 0 - end - - def ci_reactivate!(ci_repo) - ci_repo.update_column(:repo_active, 1) - update_column(:open_devops, true) - increment!(:open_devops_count) - end - - def self.sync_educoder_shixun(url, private_token, page, per_page) - SyncEducoderShixunJob.perform_later(url, private_token, page, per_page) - end - - def self.update_common_projects_count! - ps = ProjectStatistic.first - ps.increment!(:common_projects_count) unless ps.blank? - end - - def self.update_mirror_projects_count! - ps = ProjectStatistic.first - ps.increment!(:mirror_projects_count) unless ps.blank? - end - - def set_updated_on(time) - return if time.blank? - update_column(:updated_on, time) - end - - def is_transfering - applied_transfer_project&.common? ? true : false - end -end + +# == Schema Information +# +# Table name: projects +# +# id :integer not null, primary key +# name :string(255) default(""), not null +# description :text(4294967295) +# homepage :string(255) default("") +# is_public :boolean default("1"), not null +# parent_id :integer +# created_on :datetime +# updated_on :datetime +# identifier :string(255) +# status :integer default("1"), not null +# lft :integer +# rgt :integer +# inherit_members :boolean default("0"), not null +# project_type :integer default("0") +# hidden_repo :boolean default("0"), not null +# attachmenttype :integer default("1") +# user_id :integer +# dts_test :integer default("0") +# enterprise_name :string(255) +# organization_id :integer +# project_new_type :integer +# gpid :integer +# forked_from_project_id :integer +# forked_count :integer default("0") +# publish_resource :integer default("0") +# visits :integer default("0") +# hot :integer default("0") +# invite_code :string(255) +# qrcode :string(255) +# qrcode_expiretime :integer default("0") +# script :text(65535) +# training_status :integer default("0") +# rep_identifier :string(255) +# project_category_id :integer +# project_language_id :integer +# license_id :integer +# ignore_id :integer +# praises_count :integer default("0") +# watchers_count :integer default("0") +# issues_count :integer default("0") +# pull_requests_count :integer default("0") +# language :string(255) +# versions_count :integer default("0") +# issue_tags_count :integer default("0") +# closed_issues_count :integer default("0") +# open_devops :boolean default("0") +# gitea_webhook_id :integer +# open_devops_count :integer default("0") +# recommend :boolean default("0") +# platform :integer default("0") +# default_branch :string(255) default("master") +# website :string(255) +# lesson_url :string(255) +# is_pinned :boolean default("0") +# recommend_index :integer default("0") +# +# Indexes +# +# index_projects_on_forked_from_project_id (forked_from_project_id) +# index_projects_on_identifier (identifier) +# index_projects_on_invite_code (invite_code) +# index_projects_on_is_public (is_public) +# index_projects_on_lft (lft) +# index_projects_on_license_id (license_id) +# index_projects_on_name (name) +# index_projects_on_platform (platform) +# index_projects_on_project_category_id (project_category_id) +# index_projects_on_project_language_id (project_language_id) +# index_projects_on_project_type (project_type) +# index_projects_on_recommend (recommend) +# index_projects_on_rgt (rgt) +# index_projects_on_status (status) +# index_projects_on_updated_on (updated_on) +# + + + + +class Project < ApplicationRecord + include Matchable + include Publicable + include Watchable + include ProjectOperable + include Dcodes + + # common:开源托管项目 + # mirror:普通镜像项目,没有定时同步功能 + # sync_mirror:同步镜像项目,有系统定时同步功能,且用户可手动同步操作 + # + enum project_type: { sync_mirror: 2, mirror: 1, common: 0 } + + # forge: trustie平台项目, educoder: educoder平台项目, 默认为forge平台 + enum platform: { forge: 0, educoder: 1 } + + belongs_to :ignore, optional: true + belongs_to :license, optional: true + belongs_to :owner, class_name: 'Owner', foreign_key: :user_id, optional: true + belongs_to :organization_extension, foreign_key: :user_id, primary_key: :organization_id, optional: true, counter_cache: :num_projects + belongs_to :project_category, optional: true , :counter_cache => true + belongs_to :project_language, optional: true , :counter_cache => true + belongs_to :forked_from_project, class_name: 'Project', optional: true, foreign_key: :forked_from_project_id + has_many :project_trends, dependent: :destroy + has_many :watchers, as: :watchable, dependent: :destroy + has_many :fork_users, dependent: :destroy + has_many :forked_users, class_name: 'ForkUser', foreign_key: :fork_project_id, dependent: :destroy + has_many :forked_projects, class_name: 'Project', foreign_key: :forked_from_project_id + has_one :project_educoder, dependent: :destroy + + has_one :project_score, dependent: :destroy + has_one :repository, dependent: :destroy + has_many :pull_requests, dependent: :destroy + has_many :issue_tags, -> { order("issue_tags.created_at DESC") }, dependent: :destroy + has_many :issues, dependent: :destroy + # has_many :user_grades, dependent: :destroy + has_many :attachments, as: :container, dependent: :destroy + has_one :project_score, dependent: :destroy + has_many :versions, -> { order("versions.created_on DESC, versions.name DESC") }, dependent: :destroy + has_many :praise_treads, as: :praise_tread_object, dependent: :destroy + has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position" + has_one :project_detail, dependent: :destroy + has_many :project_units, dependent: :destroy + has_one :applied_transfer_project,-> { order created_at: :desc }, dependent: :destroy + has_many :pinned_projects, dependent: :destroy + has_many :has_pinned_users, through: :pinned_projects, source: :user + has_many :webhooks, class_name: "Gitea::Webhook", primary_key: :gpid, foreign_key: :repo_id + after_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 + before_destroy :decre_project_common, :decre_forked_from_project_count + after_destroy :decre_user_statistic, :decre_platform_statistic + scope :project_statics_select, -> {select(:id,:name, :is_public, :identifier, :status, :project_type, :user_id, :forked_count, :description, :visits, :project_category_id, :project_language_id, :license_id, :ignore_id, :watchers_count, :created_on)} + scope :no_anomory_projects, -> {where("projects.user_id is not null and projects.user_id != ?", 2)} + scope :recommend, -> { visible.project_statics_select.where(recommend: true) } + scope :pinned, -> {where(is_pinned: true)} + + delegate :content, to: :project_detail, allow_nil: true + delegate :name, to: :license, prefix: true, allow_nil: true + + validate :validate_sensitive_string + + def self.all_visible(user_id=nil) + user_projects_sql = Project.joins(:owner).where(users: {type: 'User'}).to_sql + org_public_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension).where(organization_extensions: {visibility: 'common'})).to_sql + if user_id.present? + org_limit_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension).where(organization_extensions: {visibility: 'limited'})).to_sql + org_privacy_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension, :organization_users).where(organization_extensions: {visibility: 'privacy'}, organization_users: {user_id: user_id})).to_sql + return Project.from("( #{ user_projects_sql } UNION #{ org_public_projects_sql } UNION #{ org_limit_projects_sql } UNION #{org_privacy_projects_sql} ) AS projects").visible + else + return Project.from("( #{ user_projects_sql } UNION #{ org_public_projects_sql } ) AS projects").visible + end + end + + def reset_cache_data + CacheAsyncResetJob.set(wait: 5.seconds).perform_later("project_common_service", self.id) + if changes[:user_id].present? + CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: -1}, changes[:user_id].first) + CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: 1}, changes[:user_id].last) + end + if changes[:project_language_id].present? + first_language = ProjectLanguage.find_by_id(changes[:project_language_id].first) + last_language = ProjectLanguage.find_by_id(changes[:project_language_id].last) + CacheAsyncSetJob.perform_later("user_statistic_service", {project_language_count_key: first_language&.name, project_language_count: -1}, self.user_id) + CacheAsyncSetJob.perform_later("user_statistic_service", {project_language_count_key: last_language&.name, project_language_count: 1}, self.user_id) + CacheAsyncSetJob.perform_later("platform_statistic_service", {project_language_count_key: first_language&.name, project_language_count: -1}) + CacheAsyncSetJob.perform_later("platform_statistic_service", {project_language_count_key: last_language&.name, project_language_count: 1}) + end + if changes[:is_public].present? + if changes[:is_public][0] && !changes[:is_public][1] + CacheAsyncClearJob.perform_later('project_rank_service', self.id) + end + if !changes[:is_public][0] && changes[:is_public][1] + $redis_cache.srem("v2-project-rank-deleted", self.id) + end + end + end + + def decre_project_common + CacheAsyncClearJob.perform_later('project_common_service', self.id) + end + + def decre_forked_from_project_count + forked_project = self.forked_from_project + if forked_project.present? + forked_project.decrement(:forked_count, 1) + forked_project.save + end + end + + def incre_user_statistic + CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: 1, project_language_count_key: self.project_language&.name, project_language_count: 1}, self.user_id) + end + + def decre_user_statistic + CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: -1, project_language_count_key: self.project_language&.name, project_language_count: -1}, self.user_id) + end + + def incre_platform_statistic + CacheAsyncSetJob.perform_later("platform_statistic_service", {project_count: 1, project_language_count_key: self.project_language&.name, project_language_count: 1}) + end + + def decre_platform_statistic + CacheAsyncSetJob.perform_later("platform_statistic_service", {project_count: -1, project_language_count_key: self.project_language&.name, project_language_count: -1}) + end + + def is_full_public + owner = self.owner + if owner.is_a?(Organization) + return self.is_public && owner&.visibility == "common" + else + return self.is_public + end + end + + def reset_unmember_followed + if changes[:is_public].present? && changes[:is_public] == [true, false] + self.watchers.where.not(user_id: self.all_collaborators).destroy_all + end + end + + def set_invite_code + if self.invite_code.nil? + self.invite_code= self.generate_dcode('invite_code', 6) + end + end + + def set_recommend_and_is_pinned + self.recommend = self.recommend_index.zero? ? false : true + # 私有项目不允许设置精选和推荐 + unless self.is_public + self.recommend = false + self.recommend_index = 0 + self.is_pinned = false + end + end + + def self.search_project(search) + ransack(name_or_identifier_cont: search) + end + # 创建者 + def creator + User.find(user_id).full_name + end + + def members_user_infos + members.joins(:roles).where("roles.name in ('Manager', 'Developer', 'Reporter')").joins("left join users on members.user_id = users.id ").includes(:user).where("users.type = ?", "User") + # members.joins("left join users on members.user_id = users.id").select("users.id", "users.login","users.firstname","users.lastname") + # .pluck("users.id", "users.login","users.lastname", "users.firstname") + end + + def to_param + self.identifier.parameterize + end + + def get_issues_count(status_id) + if status_id.present? + self&.issues.issue_issue.select(:id, :status_id).where(status_id: status_id)&.pluck(:id).size + else + self&.issues.issue_issue.select(:id)&.pluck(:id).size + end + end + + def get_pull_requests_count(status_id) + if status_id.present? + self&.pull_requests.select(:id, :status).where(status: status_id)&.pluck(:id).size + else + self&.pull_requests.select(:id)&.pluck(:id).size + end + end + + #创建项目管理员 + def check_project_members + return if owner.is_a?(Organization) + unless members.present? && members.exists?(user_id: self.user_id) + member_params = { + user_id: self.user_id, + project_id: self.id + } + user_member = Member.new(member_params) + if user_member.save + role_id = Role.select(:id,:position).where(position: 3)&.first&.id + MemberRole.create!(member_id: user_member.id ,role_id: role_id) + end + end + end + + + def self.init_bluck_repository + Project.includes(:repository).find_each do |project| + puts project.id + next if project.owner.blank? + if project.repository.blank? + puts "########### start create repositoy #############" + Repository.create!(project_id: project.id, identifier: Project.generate_identifier, user_id: project&.owner&.id) + end + end + end + + def self.generate_identifier + str_arr = (("a".."z").to_a + ("A".."Z").to_a) + + str = str_arr.shuffle[0..8].join + while Repository.exists?(identifier: str) + str = str_arr.shuffle[0..8].join + end + str + end + + def self.list_user_projects(user_id) + projects = Project.is_private.select(:id,:user_id) + user_not_show_1 = projects.where("user_id != ?",user_id).pluck(:id).uniq + + user_show_2 = projects.joins(:members).where("members.user_id = ?", user_id).pluck(:id).uniq + Project.where.not(id: (user_not_show_1 - user_show_2).uniq) + end + + def members_count + members.select(:id).size + end + + + def can_visited? + is_public? || User.current.admin? || member?(User.current) + end + + def releases_size(current_user_id, type) + if current_user_id == self.user_id && type.to_s == "all" + self.repository.version_releases_count + else + self.repository.version_releases.releases_size + end + end + + def contributor_users + self.pull_requests.select(:user_id).pluck(:user_id).uniq.size + end + + def open_issues_count + issues_count - closed_issues_count + end + + def numerical_for_project_type + self.class.name.constantize.project_types["#{self.project_type}"] + end + + def watched_by? user + watchers.pluck(:user_id).include? user&.id + end + + def praised_by? user + praise_treads.pluck(:user_id).include? user&.id + end + + def get_premission user + return "Owner" if owner?(user) + return "Manager" if manager?(user) + return "Developer" if develper?(user) + return "Reporter" if reporter?(user) + + return "" + end + + def fork_project + Project.find_by(id: self.forked_from_project_id) + end + + def self.members_projects(member_user_id) + joins(:members).where(members: { user_id: member_user_id}) + end + + def self.find_with_namespace(namespace_path, identifier) + logger.info "########namespace_path: #{namespace_path} ########identifier: #{identifier} " + + user = Owner.find_by_login namespace_path + user = Owner.new(login: namespace_path) if user.nil? + project = user&.projects&.find_by(identifier: identifier) || Project.find_by(identifier: "#{namespace_path}/#{identifier}") + return nil if project.blank? + + [project, user] + end + + def ci_reactivate? + open_devops_count > 0 + end + + def ci_reactivate!(ci_repo) + ci_repo.update_column(:repo_active, 1) + update_column(:open_devops, true) + increment!(:open_devops_count) + end + + def self.sync_educoder_shixun(url, private_token, page, per_page) + SyncEducoderShixunJob.perform_later(url, private_token, page, per_page) + end + + def self.update_common_projects_count! + ps = ProjectStatistic.first + ps.increment!(:common_projects_count) unless ps.blank? + end + + def self.update_mirror_projects_count! + ps = ProjectStatistic.first + ps.increment!(:mirror_projects_count) unless ps.blank? + end + + def set_updated_on(time) + return if time.blank? + update_column(:updated_on, time) + end + + def is_transfering + applied_transfer_project&.common? ? true : false + end + + def validate_sensitive_string + raise("项目名称包含敏感词汇,请重新输入") if name && !HarmoniousDictionary.clean?(name) + raise("项目描述包含敏感词汇,请重新输入") if description && !HarmoniousDictionary.clean?(description) + end +end From 877948b0cd515eb414891801e63f8cd934e77368 Mon Sep 17 00:00:00 2001 From: yystopf Date: Wed, 1 Jun 2022 10:00:57 +0800 Subject: [PATCH 056/208] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20=E9=80=9A?= =?UTF-8?q?=E7=94=A8=E6=B6=88=E6=81=AF=E6=A8=A1=E6=9D=BF=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E5=88=9B=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admins/message_templates_controller.rb | 20 +++++++++++++++++-- app/models/message_template.rb | 3 +-- .../admins/message_templates/_list.html.erb | 4 ++-- .../admins/message_templates/index.html.erb | 8 +++++++- app/views/admins/message_templates/new.js.erb | 2 ++ config/routes.rb | 2 +- 6 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 app/views/admins/message_templates/new.js.erb diff --git a/app/controllers/admins/message_templates_controller.rb b/app/controllers/admins/message_templates_controller.rb index 23c94e78..af77858e 100644 --- a/app/controllers/admins/message_templates_controller.rb +++ b/app/controllers/admins/message_templates_controller.rb @@ -2,8 +2,24 @@ 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) + message_templates = MessageTemplate.ransack(sys_notice_or_email_or_email_title_cont: params[:search]).result + @message_templates = kaminari_paginate(message_templates) + end + + def new + @message_template = MessageTemplate::CustomTip.new + end + + def create + @message_template = MessageTemplate::CustomTip.new(ignore_params) + + if @message_template.save! + redirect_to admins_message_templates_path + flash[:success] = "创建消息模板成功" + else + render :new + flash[:danger] = "创建消息模板失败" + end end def edit diff --git a/app/models/message_template.rb b/app/models/message_template.rb index f3420013..50de7d8e 100644 --- a/app/models/message_template.rb +++ b/app/models/message_template.rb @@ -16,8 +16,7 @@ class MessageTemplate < ApplicationRecord PLATFORM = 'GitLink' def self.build_init_data - MessageTemplate::IssueAssignerExpire.destroy_all - MessageTemplate::IssueCreatorExpire.destroy_all + MessageTemplate.where.not(type: 'MessageTemplate::CustomTip').destroy_all 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: "#{PLATFORM}: {nickname1} 在 {nickname2}/{repository} 指派给你一个疑修") diff --git a/app/views/admins/message_templates/_list.html.erb b/app/views/admins/message_templates/_list.html.erb index 4082735f..dae87d57 100644 --- a/app/views/admins/message_templates/_list.html.erb +++ b/app/views/admins/message_templates/_list.html.erb @@ -11,8 +11,8 @@ <% if message_templates.present? %> - <% message_templates.each_with_index do |message_template_type, index| %> - <% message_template = message_template_type.constantize.last%> + <% message_templates.each_with_index do |message_template, index| %> + <%# message_template = message_template_type.constantize.last%> <%= list_index_no((params[:page] || 1).to_i, index) %> <%= message_template.simple_type %> diff --git a/app/views/admins/message_templates/index.html.erb b/app/views/admins/message_templates/index.html.erb index 54d27333..e14de841 100644 --- a/app/views/admins/message_templates/index.html.erb +++ b/app/views/admins/message_templates/index.html.erb @@ -3,7 +3,13 @@ <% end %>
    - <%= link_to "初始化数据", init_data_admins_message_templates_path, class: "btn btn-primary pull-right", "data-disabled-with":"...初始化数据" %> + <%= form_tag(admins_message_templates_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> + <%= text_field_tag(:search, params[:search], class: 'form-control col-12 col-md-2 mr-3', placeholder: '名称检索') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> + + <% end %> + <%= link_to "初始化数据", init_data_admins_message_templates_path, class: "btn btn-primary mr-3 pull-right", "data-disabled-with":"...初始化数据" %> + <%= link_to "新增", new_admins_message_template_path, remote: true, 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/new.js.erb b/app/views/admins/message_templates/new.js.erb new file mode 100644 index 00000000..e75d923d --- /dev/null +++ b/app/views/admins/message_templates/new.js.erb @@ -0,0 +1,2 @@ +$("#admins-message-templates-content").html("<%= j render partial: 'admins/message_templates/form', locals:{type: 'create'} %>") +createMDEditor('message-template-email-editor', { height: 500, placeholder: '请输入邮件模版' }); diff --git a/config/routes.rb b/config/routes.rb index 4d1e3ade..b585f077 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -694,7 +694,7 @@ Rails.application.routes.draw do get :history end end - resources :message_templates, only: [:index, :edit, :update] do + resources :message_templates, only: [:index, :new, :create, :edit, :update] do collection do get :init_data end From a07204111a83813d0dc73c48cd63168ebc63991b Mon Sep 17 00:00:00 2001 From: xiaoxiaoqiong Date: Wed, 1 Jun 2022 15:24:39 +0800 Subject: [PATCH 057/208] =?UTF-8?q?=E9=80=9A=E7=9F=A5=E9=82=AE=E4=BB=B6?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=94=A8=E6=88=B7=E4=B8=8D=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=E6=97=B6=E5=8F=91=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/jobs/send_template_message_job.rb | 9 ++++++++- app/models/message_template/custom_tip.rb | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/app/jobs/send_template_message_job.rb b/app/jobs/send_template_message_job.rb index bec3f7b0..557d4d0f 100644 --- a/app/jobs/send_template_message_job.rb +++ b/app/jobs/send_template_message_job.rb @@ -8,13 +8,20 @@ class SendTemplateMessageJob < ApplicationJob receivers_id, template_id, props = args[0], args[1], args[2] template = MessageTemplate.find_by_id(template_id) return unless template.present? - receivers = User.where(id: receivers_id) + receivers = User.where(id: receivers_id).or(User.where(mail: receivers_id)) + not_exists_receivers = receivers_id - receivers.pluck(:id) - receivers.pluck(:mail) receivers_string, content, notification_url = MessageTemplate::CustomTip.get_message_content(receivers, template, props) Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {receivers_id: receivers_id, template_id: template_id, props: props}) receivers.find_each do |receiver| receivers_email_string, email_title, email_content = MessageTemplate::CustomTip.get_email_message_content(receiver, template, props) Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) end + not_exists_receivers.each do |mail| + valid_email_regex = /^[a-zA-Z0-9]+([.\-_\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/i + next unless (mail =~ valid_email_regex) + email_title, email_content = MessageTemplate::CustomTip.get_email_content(template, props) + Notice::Write::EmailCreateService.call(mail, email_title, email_content) + end when 'FollowTip' watcher_id = args[0] watcher = Watcher.find_by_id(watcher_id) diff --git a/app/models/message_template/custom_tip.rb b/app/models/message_template/custom_tip.rb index 6c5a3345..f1e3096f 100644 --- a/app/models/message_template/custom_tip.rb +++ b/app/models/message_template/custom_tip.rb @@ -48,4 +48,21 @@ class MessageTemplate::CustomTip < MessageTemplate Rails.logger.info("MessageTemplate::CustomTip.get_email_message_content [ERROR] #{e}") return '', '', '' end + + def self.get_email_content(template, props = {}) + return '', '', '' if template.blank? + title = template.email_title + content = template.email + props.each do |k, v| + title.gsub!("{#{k}}", v) + content.gsub!("{#{k}}", v) + end + title.gsub!('{platform}', PLATFORM) + content.gsub!('{platform}', PLATFORM) + + return title, content + rescue => e + Rails.logger.info("MessageTemplate::CustomTip.get_email_content [ERROR] #{e}") + return '', '' + end end \ No newline at end of file From 0b543f7ddab712fe6f0f77a64da5fe7aa627f439 Mon Sep 17 00:00:00 2001 From: xiaoxiaoqiong Date: Wed, 1 Jun 2022 16:08:20 +0800 Subject: [PATCH 058/208] =?UTF-8?q?=E9=80=9A=E7=9F=A5=E9=82=AE=E4=BB=B6?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=94=A8=E6=88=B7=E4=B8=8D=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=E6=97=B6=E5=8F=91=E9=80=81base=5Furl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/message_template/custom_tip.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/models/message_template/custom_tip.rb b/app/models/message_template/custom_tip.rb index f1e3096f..b0f628d6 100644 --- a/app/models/message_template/custom_tip.rb +++ b/app/models/message_template/custom_tip.rb @@ -25,6 +25,7 @@ class MessageTemplate::CustomTip < MessageTemplate content.gsub!("{#{k}}", v) notification_url.gsub!("{#{k}}", v) end + notification_url.gsub!('{baseurl}', base_url) return receivers_string(receivers), content, notification_url rescue => e Rails.logger.info("MessageTemplate::CustomTip.get_message_content [ERROR] #{e}") @@ -42,6 +43,7 @@ class MessageTemplate::CustomTip < MessageTemplate content.gsub!('{receiver}', receiver&.real_name) title.gsub!('{platform}', PLATFORM) content.gsub!('{platform}', PLATFORM) + content.gsub!('{baseurl}', base_url) return receiver&.mail, title, content rescue => e @@ -59,6 +61,7 @@ class MessageTemplate::CustomTip < MessageTemplate end title.gsub!('{platform}', PLATFORM) content.gsub!('{platform}', PLATFORM) + content.gsub!('{baseurl}', base_url) return title, content rescue => e From 8ae55875165708bec23d081f74a927f99f559626 Mon Sep 17 00:00:00 2001 From: yystopf Date: Wed, 8 Jun 2022 10:37:31 +0800 Subject: [PATCH 059/208] fix: search name use strip --- app/models/user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/user.rb b/app/models/user.rb index fa940c29..bc9808c6 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -179,7 +179,7 @@ class User < Owner scope :active, lambda { where(status: STATUS_ACTIVE) } scope :like, lambda { |keywords| sql = "CONCAT(lastname, firstname) LIKE :search OR nickname LIKE :search OR login LIKE :search OR mail LIKE :search OR nickname LIKE :search" - where(sql, :search => "%#{keywords.split(" ").join('|')}%") unless keywords.blank? + where(sql, :search => "%#{keywords.strip}%") unless keywords.blank? } scope :simple_select, -> {select(:id, :login, :lastname,:firstname, :nickname, :gitea_uid, :type)} From 3e4dd90e1737ebc908a650f3bb44ec3e59d6d6fd Mon Sep 17 00:00:00 2001 From: yystopf Date: Wed, 8 Jun 2022 10:46:55 +0800 Subject: [PATCH 060/208] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=90=8D=E7=A7=B0=E5=B1=95=E7=A4=BA=E6=97=B6=E5=8F=AA?= =?UTF-8?q?=E5=8E=BB=E6=8E=89=E9=A6=96=E6=9C=AB=E7=9A=84=E7=A9=BA=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/user.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/user.rb b/app/models/user.rb index bc9808c6..d3803a9f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -553,7 +553,8 @@ class User < Owner return '游客' unless logged? name = lastname + firstname name = name.blank? ? (nickname.blank? ? login : nickname) : name - name.gsub(/\s+/, '').strip #6.11 -hs + # name.gsub(/\s+/, '').strip #6.11 -hs + name.strip end def only_real_name From ce467736a71d087e898440faa83841342f9ead87 Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 13 Jun 2022 12:53:40 +0800 Subject: [PATCH 061/208] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20readme=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E6=AD=A3=E5=B8=B8=E8=B7=B3=E8=BD=AC=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/helpers/repositories_helper.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index 73ce9823..c31b9c59 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -104,8 +104,7 @@ module RepositoriesHelper # 链接直接跳过不做替换 next if s_content.starts_with?('http://') || s_content.starts_with?('https://') || s_content.starts_with?('mailto:') || s_content.blank? ext = File.extname(s_content)[1..-1] - - if image_type?(ext) || download_type(ext) + if (image_type?(ext) || download_type(ext)) && !s_content.to_s.end_with?('/') s_content = File.expand_path(s_content, file_path) s_content = s_content.split("#{Rails.root}/")[1] # content = content.gsub(s[0], "/#{s_content}") From 66c40d479163f528f65a7b14e72230c98c4d262b Mon Sep 17 00:00:00 2001 From: xiaoxiaoqiong Date: Wed, 15 Jun 2022 11:16:08 +0800 Subject: [PATCH 062/208] =?UTF-8?q?fixed=20gitea=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/interactors/gitea/register_interactor.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/interactors/gitea/register_interactor.rb b/app/interactors/gitea/register_interactor.rb index d8dbc6ca..e188809a 100644 --- a/app/interactors/gitea/register_interactor.rb +++ b/app/interactors/gitea/register_interactor.rb @@ -13,7 +13,7 @@ module Gitea end def success? - @error.nil? + @error.nil? && @result[:status].to_s == "success" end def result From 42d2b51f7f2df6788119d2c3489dcdd8c5ab4947 Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 16 Jun 2022 13:42:17 +0800 Subject: [PATCH 063/208] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E6=95=8F=E6=84=9F=E8=AF=8D=E5=88=A4=E5=AE=9A=E5=8F=AA?= =?UTF-8?q?=E5=9C=A8=E5=88=9B=E5=BB=BA=E6=88=96=E8=80=85=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E7=9A=84=E6=97=B6=E5=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/project.rb | 2 +- app/models/project_unit.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/project.rb b/app/models/project.rb index 9c77685f..edaa00eb 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -141,7 +141,7 @@ class Project < ApplicationRecord delegate :content, to: :project_detail, allow_nil: true delegate :name, to: :license, prefix: true, allow_nil: true - validate :validate_sensitive_string + validate :validate_sensitive_string, on: [:create, :update] def self.all_visible(user_id=nil) user_projects_sql = Project.joins(:owner).where(users: {type: 'User'}).to_sql diff --git a/app/models/project_unit.rb b/app/models/project_unit.rb index cc35a6b2..01f6c667 100644 --- a/app/models/project_unit.rb +++ b/app/models/project_unit.rb @@ -14,6 +14,8 @@ # class ProjectUnit < ApplicationRecord + + default_scope {where(unit_type: ProjectUnit.unit_types.keys)} belongs_to :project enum unit_type: {code: 1, issues: 2, pulls: 3, wiki:4, devops: 5, versions: 6, resources: 7} From c42f3d59281ba65edc85873e6ff3a9d551968410 Mon Sep 17 00:00:00 2001 From: yystopf Date: Wed, 15 Jun 2022 18:07:23 +0800 Subject: [PATCH 064/208] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E9=82=80?= =?UTF-8?q?=E8=AF=B7=E7=94=A8=E6=88=B7=E9=93=BE=E6=8E=A5=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project_invite_links_controller.rb | 36 +++++++++ .../project_invite_links/create_form.rb | 8 ++ app/models/applied_project.rb | 21 ++--- app/models/project.rb | 1 + app/models/project_invite_link.rb | 57 ++++++++++++++ app/services/projects/link_join_service.rb | 76 +++++++++++++++++++ app/views/projects/_detail.json.jbuilder | 8 ++ .../_detail.json.jbuilder | 12 +++ .../current_link.json.jbuilder | 1 + config/routes.rb | 7 ++ ...20614070028_create_project_invite_links.rb | 16 ++++ ...project_invite_link_to_applied_projects.rb | 6 ++ spec/models/project_invite_link_spec.rb | 5 ++ 13 files changed, 245 insertions(+), 9 deletions(-) create mode 100644 app/controllers/projects/project_invite_links_controller.rb create mode 100644 app/forms/projects/project_invite_links/create_form.rb create mode 100644 app/models/project_invite_link.rb create mode 100644 app/services/projects/link_join_service.rb create mode 100644 app/views/projects/_detail.json.jbuilder create mode 100644 app/views/projects/project_invite_links/_detail.json.jbuilder create mode 100644 app/views/projects/project_invite_links/current_link.json.jbuilder create mode 100644 db/migrate/20220614070028_create_project_invite_links.rb create mode 100644 db/migrate/20220614081950_add_project_invite_link_to_applied_projects.rb create mode 100644 spec/models/project_invite_link_spec.rb diff --git a/app/controllers/projects/project_invite_links_controller.rb b/app/controllers/projects/project_invite_links_controller.rb new file mode 100644 index 00000000..59f618fc --- /dev/null +++ b/app/controllers/projects/project_invite_links_controller.rb @@ -0,0 +1,36 @@ +class Projects::ProjectInviteLinksController < Projects::BaseController + before_action :require_manager!, except: [:redirect_link] + before_action :require_login + + def current_link + @project_invite_link = ProjectInviteLink.find_by(user_id: current_user.id, project_id: @project.id) + @project_invite_link = ProjectInviteLink.build!(@project, current_user) unless @project_invite_link.present? + end + + def generate_link + ActiveRecord::Base.transaction do + params_data = link_params.merge({user_id: current_user.id, project_id: @project.id}) + puts params_data + Projects::ProjectInviteLinks::CreateForm.new(params_data).validate! + @project_invite_link = ProjectInviteLink.create!(params_data.merge(sign: ProjectInviteLink.random_hex_sign)) + render_ok + end + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + def redirect_link + Projects::LinkJoinService.call(current_user, @project, params[:invite_sign]) + render_ok + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + + private + def link_params + params.require(:project_invite_link).permit(:role, :is_apply) + end +end diff --git a/app/forms/projects/project_invite_links/create_form.rb b/app/forms/projects/project_invite_links/create_form.rb new file mode 100644 index 00000000..701625c0 --- /dev/null +++ b/app/forms/projects/project_invite_links/create_form.rb @@ -0,0 +1,8 @@ +class Projects::ProjectInviteLinks::CreateForm < BaseForm + attr_accessor :user_id, :project_id, :role, :is_apply + + validates :user_id, :project_id, :role, presence: true + validates :role, inclusion: { in: %w(manager developer reporter), message: "请输入正确的权限." } + validates :is_apply, inclusion: {in: [true, false], message: "请输入是否需要管理员审核."} +end + \ No newline at end of file diff --git a/app/models/applied_project.rb b/app/models/applied_project.rb index ea7ca6ee..2b18d4d8 100644 --- a/app/models/applied_project.rb +++ b/app/models/applied_project.rb @@ -2,24 +2,27 @@ # # Table name: forge_applied_projects # -# id :integer not null, primary key -# project_id :integer -# user_id :integer -# role :integer default("0") -# status :integer default("0") -# created_at :datetime not null -# updated_at :datetime not null +# id :integer not null, primary key +# project_id :integer +# user_id :integer +# role :integer default("0") +# status :integer default("0") +# created_at :datetime not null +# updated_at :datetime not null +# project_invite_link_id :integer # # Indexes # -# index_forge_applied_projects_on_project_id (project_id) -# index_forge_applied_projects_on_user_id (user_id) +# index_forge_applied_projects_on_project_id (project_id) +# index_forge_applied_projects_on_project_invite_link_id (project_invite_link_id) +# index_forge_applied_projects_on_user_id (user_id) # class AppliedProject < ApplicationRecord self.table_name = "forge_applied_projects" belongs_to :user belongs_to :project + belongs_to :project_invite_link, optional: true has_many :applied_messages, as: :applied, dependent: :destroy # has_many :forge_activities, as: :forge_act, dependent: :destroy diff --git a/app/models/project.rb b/app/models/project.rb index edaa00eb..92e1bac7 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -128,6 +128,7 @@ class Project < ApplicationRecord has_many :pinned_projects, dependent: :destroy has_many :has_pinned_users, through: :pinned_projects, source: :user has_many :webhooks, class_name: "Gitea::Webhook", primary_key: :gpid, foreign_key: :repo_id + has_many :project_invite_links, dependent: :destroy 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 diff --git a/app/models/project_invite_link.rb b/app/models/project_invite_link.rb new file mode 100644 index 00000000..fa5c0c44 --- /dev/null +++ b/app/models/project_invite_link.rb @@ -0,0 +1,57 @@ +# == Schema Information +# +# Table name: project_invite_links +# +# id :integer not null, primary key +# project_id :integer +# user_id :integer +# role :integer default("4") +# is_apply :boolean default("1") +# sign :string(255) +# expired_at :datetime +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_project_invite_links_on_project_id (project_id) +# index_project_invite_links_on_sign (sign) +# index_project_invite_links_on_user_id (user_id) +# + +class ProjectInviteLink < ApplicationRecord + + default_scope { where("expired_at > ?", Time.now).or(where(expired_at: nil)) } + + belongs_to :project + belongs_to :user + has_many :applied_projects + + scope :with_project_id, -> (project_id) {where(project_id: project_id)} + scope :with_user_id, -> (user_id) {where(user_id: user_id)} + + enum role: {manager: 3, developer: 4, reporter: 5} + + before_create :set_old_data_expired_at + + def self.random_hex_sign + SecureRandom.hex + end + + def self.build!(project, user, role="developer", is_apply=true) + self.create!( + project_id: project&.id, + user_id: user&.id, + role: role, + is_apply: is_apply, + sign: random_hex_sign + ) + end + + private + def set_old_data_expired_at + ProjectInviteLink.where(user_id: self.user_id, project_id: self.project).update_all(expired_at: Time.now) + end + + +end diff --git a/app/services/projects/link_join_service.rb b/app/services/projects/link_join_service.rb new file mode 100644 index 00000000..c42376ef --- /dev/null +++ b/app/services/projects/link_join_service.rb @@ -0,0 +1,76 @@ +class Projects::LinkJoinService < ApplicationService + Error = Class.new(StandardError) + + attr_reader :user, :project, :invite_sign, :params + + def initialize(user, project, invite_sign, params={}) + @user = user + @project = project + @invite_sign = invite_sign + @params = params + end + + def call + ActiveRecord::Base.transaction do + validate! + if invite_link.is_apply + # 如果需要申请才能加入,创建一条申请记录 + create_applied_project! + else + # 如果不需要申请,直接为项目添加该成员 + create_member! + end + end + end + + private + def validate! + raise Error, 'invite_sign必须存在!' if invite_sign.blank? + raise Error, '邀请链接不存在!' unless invite_link.present? + raise Error, '邀请链接已失效!' unless invite_user_in_project + raise Error, '用户已加入该项目!' if project.member?(user.id) + end + + def create_applied_project! + user.applied_projects.create!(project: project, role: role_value) + end + + def create_member! + Projects::AddMemberInteractor.call(project.owner, project, user, permission) + end + + def invite_link + ProjectInviteLink.find_by(project_id: project.id, sign: invite_sign) + end + + def invite_user_in_project + in_project = project.member?(invite_link.user) + invite_link.update_column(:expired_at, Time.now) unless in_project + in_project + end + + def role_value + @_role ||= + case invite_link&.role + when 'manager' then 3 + when 'developer' then 4 + when 'reporter' then 5 + else + 5 + end + end + + def permission + case invite_link&.role + when 'manager' + 'admin' + when 'developer' + 'write' + when 'reporter' + 'read' + else + 'read' + end + end + +end \ No newline at end of file diff --git a/app/views/projects/_detail.json.jbuilder b/app/views/projects/_detail.json.jbuilder new file mode 100644 index 00000000..ed23fdef --- /dev/null +++ b/app/views/projects/_detail.json.jbuilder @@ -0,0 +1,8 @@ +json.id project.id +json.identifier project.identifier +json.name project.name +json.description project.description +json.is_public project.is_public +json.owner do + json.partial! "/users/user_simple", locals: {user: project.owner} +end \ No newline at end of file diff --git a/app/views/projects/project_invite_links/_detail.json.jbuilder b/app/views/projects/project_invite_links/_detail.json.jbuilder new file mode 100644 index 00000000..7c9f78dd --- /dev/null +++ b/app/views/projects/project_invite_links/_detail.json.jbuilder @@ -0,0 +1,12 @@ +json.(project_invite_link, :id, :role, :is_apply, :sign) + +json.user do + json.partial! "/users/user_simple", locals: {user: project_invite_link.user} +end +if project_invite_link&.project.present? + json.project do + json.partial! "/projects/detail", locals: {project: project_invite_link.project} + end +else + json.project nil +end diff --git a/app/views/projects/project_invite_links/current_link.json.jbuilder b/app/views/projects/project_invite_links/current_link.json.jbuilder new file mode 100644 index 00000000..1903e10a --- /dev/null +++ b/app/views/projects/project_invite_links/current_link.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'detail', locals: { project_invite_link: @project_invite_link } diff --git a/config/routes.rb b/config/routes.rb index b585f077..ed28b5ba 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -603,6 +603,13 @@ Rails.application.routes.draw do post :cancel end end + resources :project_invite_links, only: [:index] do + collection do + get :current_link + post :generate_link + post :redirect_link + end + end resources :webhooks, except: [:show, :new] do member do get :tasks diff --git a/db/migrate/20220614070028_create_project_invite_links.rb b/db/migrate/20220614070028_create_project_invite_links.rb new file mode 100644 index 00000000..5b482967 --- /dev/null +++ b/db/migrate/20220614070028_create_project_invite_links.rb @@ -0,0 +1,16 @@ +class CreateProjectInviteLinks < ActiveRecord::Migration[5.2] + def change + create_table :project_invite_links do |t| + t.references :project + t.references :user + t.integer :role, default: 4 + t.boolean :is_apply, default: true + t.string :sign + t.datetime :expired_at + + t.timestamps + end + + add_index :project_invite_links, :sign + end +end diff --git a/db/migrate/20220614081950_add_project_invite_link_to_applied_projects.rb b/db/migrate/20220614081950_add_project_invite_link_to_applied_projects.rb new file mode 100644 index 00000000..31cbd951 --- /dev/null +++ b/db/migrate/20220614081950_add_project_invite_link_to_applied_projects.rb @@ -0,0 +1,6 @@ +class AddProjectInviteLinkToAppliedProjects < ActiveRecord::Migration[5.2] + def change + add_column :forge_applied_projects, :project_invite_link_id, :integer + add_index :forge_applied_projects, :project_invite_link_id + end +end diff --git a/spec/models/project_invite_link_spec.rb b/spec/models/project_invite_link_spec.rb new file mode 100644 index 00000000..d7c46fa5 --- /dev/null +++ b/spec/models/project_invite_link_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ProjectInviteLink, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end From f6fb50529c9a8db640ade92c66d3ca6b5542e281 Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 16 Jun 2022 11:56:09 +0800 Subject: [PATCH 065/208] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20=E9=82=80?= =?UTF-8?q?=E8=AF=B7=E9=A1=B9=E7=9B=AE=E9=93=BE=E6=8E=A5=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project_invite_links_controller.rb | 1 - app/docs/slate/source/includes/_projects.md | 186 ++++++++ app/services/projects/link_join_service.rb | 2 +- .../generate_link.json.jbuilder | 1 + public/docs/api.html | 450 ++++++++++++++++-- 5 files changed, 597 insertions(+), 43 deletions(-) create mode 100644 app/views/projects/project_invite_links/generate_link.json.jbuilder diff --git a/app/controllers/projects/project_invite_links_controller.rb b/app/controllers/projects/project_invite_links_controller.rb index 59f618fc..c177367c 100644 --- a/app/controllers/projects/project_invite_links_controller.rb +++ b/app/controllers/projects/project_invite_links_controller.rb @@ -13,7 +13,6 @@ class Projects::ProjectInviteLinksController < Projects::BaseController puts params_data Projects::ProjectInviteLinks::CreateForm.new(params_data).validate! @project_invite_link = ProjectInviteLink.create!(params_data.merge(sign: ProjectInviteLink.random_hex_sign)) - render_ok end rescue Exception => e uid_logger_error(e.message) diff --git a/app/docs/slate/source/includes/_projects.md b/app/docs/slate/source/includes/_projects.md index e133bcdd..7e700457 100644 --- a/app/docs/slate/source/includes/_projects.md +++ b/app/docs/slate/source/includes/_projects.md @@ -1,4 +1,190 @@ # Projects +## 获取项目邀请链接 +当前登录(管理员)用户获取项目邀请链接的接口(第一次请求会默认生成role类型为developer和is_apply为true的链接) + +> 示例: + +```shell +curl -X GET http://localhost:3000/api/yystopf/kellect/project_invite_links/current_link.json +``` + +```javascript +await octokit.request('GET /api/yystopf/kellect/project_invite_links/current_link.json') +``` + +### HTTP 请求 +`GET /api/:owner/:repo/project_invite_links/current_link.json` + +### 返回字段说明 +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|id |int |链接id | +|role |string |邀请角色| +|is_apply |boolean |是否需要审核 | +|sign |string |邀请标识(放在链接后面即可)| +|user.id |int |链接创建者的id | +|user.type |string |链接创建者的类型 | +|user.name |string |链接创建者的名称 | +|user.login |string |链接创建者的标识 | +|user.image_url |string |链接创建者头像 | +|project.id |int |链接关联项目的id | +|project.identifier |string |链接关联项目的标识 | +|project.name |string |链接关联项目的名称 | +|project.description |string |链接关联项目的描述 | +|project.is_public |bool |链接关联项目是否公开 | +|project.owner.id |bool |链接关联项目拥有者id | +|project.owner.type |string |链接关联项目拥有者类型 | +|project.owner.name |string |链接关联项目拥有者昵称 | +|project.owner.login |string |链接关联项目拥有者标识 | +|project.owner.image_url|string |链接关联项目拥有者头像 | + +> 返回的JSON示例: + +```json +{ + "id": 7, + "role": "developer", + "is_apply": false, + "sign": "6b6b454843c291d4e52e60853cb8ad9f", + "user": { + "id": 2, + "type": "User", + "name": "heh", + "login": "yystopf", + "image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png" + }, + "project": { + "id": 474, + "identifier": "kellect", + "name": "kellect", + "description": null, + "is_public": true, + "owner": { + "id": 2, + "type": "User", + "name": "heh", + "login": "yystopf", + "image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png" + } + } +} +``` +## 生成项目邀请链接 +当前登录(管理员)用户生成的项目邀请链接,可选role和is_apply参数 + +> 示例: + +```shell +curl -X POST http://localhost:3000/api/yystopf/kellect/project_invite_links/generate_link.json +``` + +```javascript +await octokit.request('POST /api/yystopf/kellect/project_invite_links/generate_link.json') +``` + +### HTTP 请求 +`POST /api/:owner/:repo/project_invite_links/generate_link.json` + +### 请求参数 +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|role |是| |string |项目权限,reporter: 报告者, developer: 开发者,manager:管理员 | +|is_apply |是| |boolean |是否需要审核 | + + +> 请求的JSON示例 + +```json +{ + "role": "developer", + "is_apply": false +} +``` + +### 返回字段说明 +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|id |int |链接id | +|role |string |邀请角色| +|is_apply |boolean |是否需要审核 | +|sign |string |邀请标识(放在链接后面即可)| +|user.id |int |链接创建者的id | +|user.type |string |链接创建者的类型 | +|user.name |string |链接创建者的名称 | +|user.login |string |链接创建者的标识 | +|user.image_url |string |链接创建者头像 | +|project.id |int |链接关联项目的id | +|project.identifier |string |链接关联项目的标识 | +|project.name |string |链接关联项目的名称 | +|project.description |string |链接关联项目的描述 | +|project.is_public |bool |链接关联项目是否公开 | +|project.owner.id |bool |链接关联项目拥有者id | +|project.owner.type |string |链接关联项目拥有者类型 | +|project.owner.name |string |链接关联项目拥有者昵称 | +|project.owner.login |string |链接关联项目拥有者标识 | +|project.owner.image_url|string |链接关联项目拥有者头像 | + +> 返回的JSON示例: + +```json +{ + "id": 7, + "role": "developer", + "is_apply": false, + "sign": "6b6b454843c291d4e52e60853cb8ad9f", + "user": { + "id": 2, + "type": "User", + "name": "heh", + "login": "yystopf", + "image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png" + }, + "project": { + "id": 474, + "identifier": "kellect", + "name": "kellect", + "description": null, + "is_public": true, + "owner": { + "id": 2, + "type": "User", + "name": "heh", + "login": "yystopf", + "image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png" + } + } +} +``` + +## 请求项目邀请链接 +当前登录(非项目)用户加入项目的接口,如果项目链接不需要审核,请求成功后即加入项目,如果需要审核,那么会提交一个申请,需要项目管理员审核 + +> 示例: + +```shell +curl -X POST http://localhost:3000/api/yystopf/kellect/project_invite_links/redirect_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6 +``` + +```javascript +await octokit.request('POST /api/yystopf/kellect/project_invite_links/redirect_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6') +``` + +### HTTP 请求 +`POST /api/:owner/:repo/project_invite_links/generate_link.json?invite_sign=xxx` + +### 请求参数 +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|invite_sign |是| |string |项目邀请链接的标识 | + +> 返回的JSON示例: + +```json +{ + "status": 0, + "message": "success" +} +``` ## 申请加入项目 申请加入项目 diff --git a/app/services/projects/link_join_service.rb b/app/services/projects/link_join_service.rb index c42376ef..e586933f 100644 --- a/app/services/projects/link_join_service.rb +++ b/app/services/projects/link_join_service.rb @@ -32,7 +32,7 @@ class Projects::LinkJoinService < ApplicationService end def create_applied_project! - user.applied_projects.create!(project: project, role: role_value) + user.applied_projects.create!(project: project, role: role_value, project_invite_link_id: invite_link&.id) end def create_member! diff --git a/app/views/projects/project_invite_links/generate_link.json.jbuilder b/app/views/projects/project_invite_links/generate_link.json.jbuilder new file mode 100644 index 00000000..1903e10a --- /dev/null +++ b/app/views/projects/project_invite_links/generate_link.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'detail', locals: { project_invite_link: @project_invite_link } diff --git a/public/docs/api.html b/public/docs/api.html index 8ef6924b..bd3f49eb 100644 --- a/public/docs/api.html +++ b/public/docs/api.html @@ -425,6 +425,15 @@
  • Projects
      +
    • + 获取项目邀请链接 +
    • +
    • + 生成项目邀请链接 +
    • +
    • + 请求项目邀请链接 +
    • 申请加入项目
    • @@ -4390,7 +4399,366 @@ Success — a happy kitten is an authenticated kitten! "created_at": "2021-06-09 16:41", "time_ago": "7分钟前" } -

      Projects

      申请加入项目

      +

      Projects

      获取项目邀请链接

      +

      当前登录(管理员)用户获取项目邀请链接的接口(第一次请求会默认生成role类型为developer和is_apply为true的链接)

      + +
      +

      示例:

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

      HTTP 请求

      +

      GET /api/:owner/:repo/project_invite_links/current_link.json

      +

      返回字段说明

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      参数类型字段说明
      idint链接id
      rolestring邀请角色
      is_applyboolean是否需要审核
      signstring邀请标识(放在链接后面即可)
      user.idint链接创建者的id
      user.typestring链接创建者的类型
      user.namestring链接创建者的名称
      user.loginstring链接创建者的标识
      user.image_urlstring链接创建者头像
      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链接关联项目拥有者头像
      + +
      +

      返回的JSON示例:

      +
      +
      {
      +    "id": 7,
      +    "role": "developer",
      +    "is_apply": false,
      +    "sign": "6b6b454843c291d4e52e60853cb8ad9f",
      +    "user": {
      +        "id": 2,
      +        "type": "User",
      +        "name": "heh",
      +        "login": "yystopf",
      +        "image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
      +    },
      +    "project": {
      +        "id": 474,
      +        "identifier": "kellect",
      +        "name": "kellect",
      +        "description": null,
      +        "is_public": true,
      +        "owner": {
      +            "id": 2,
      +            "type": "User",
      +            "name": "heh",
      +            "login": "yystopf",
      +            "image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
      +        }
      +    }
      +}
      +

      生成项目邀请链接

      +

      当前登录(管理员)用户生成的项目邀请链接,可选role和is_apply参数

      + +
      +

      示例:

      +
      +
      curl -X POST http://localhost:3000/api/yystopf/kellect/project_invite_links/generate_link.json
      +
      await octokit.request('POST /api/yystopf/kellect/project_invite_links/generate_link.json')
      +

      HTTP 请求

      +

      POST /api/:owner/:repo/project_invite_links/generate_link.json

      +

      请求参数

      + + + + + + + + + + + + + + + + + + + + + + + +
      参数必选默认类型字段说明
      rolestring项目权限,reporter: 报告者, developer: 开发者,manager:管理员
      is_applyboolean是否需要审核
      + +
      +

      请求的JSON示例

      +
      +
      {
      +    "role": "developer",
      +    "is_apply": false
      +}
      +

      返回字段说明

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      参数类型字段说明
      idint链接id
      rolestring邀请角色
      is_applyboolean是否需要审核
      signstring邀请标识(放在链接后面即可)
      user.idint链接创建者的id
      user.typestring链接创建者的类型
      user.namestring链接创建者的名称
      user.loginstring链接创建者的标识
      user.image_urlstring链接创建者头像
      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链接关联项目拥有者头像
      + +
      +

      返回的JSON示例:

      +
      +
      {
      +    "id": 7,
      +    "role": "developer",
      +    "is_apply": false,
      +    "sign": "6b6b454843c291d4e52e60853cb8ad9f",
      +    "user": {
      +        "id": 2,
      +        "type": "User",
      +        "name": "heh",
      +        "login": "yystopf",
      +        "image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
      +    },
      +    "project": {
      +        "id": 474,
      +        "identifier": "kellect",
      +        "name": "kellect",
      +        "description": null,
      +        "is_public": true,
      +        "owner": {
      +            "id": 2,
      +            "type": "User",
      +            "name": "heh",
      +            "login": "yystopf",
      +            "image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
      +        }
      +    }
      +}
      +

      请求项目邀请链接

      +

      当前登录(非项目)用户加入项目的接口,如果项目链接不需要审核,请求成功后即加入项目,如果需要审核,那么会提交一个申请,需要项目管理员审核

      + +
      +

      示例:

      +
      +
      curl -X POST http://localhost:3000/api/yystopf/kellect/project_invite_links/redirect_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6
      +
      await octokit.request('POST /api/yystopf/kellect/project_invite_links/redirect_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6')
      +

      HTTP 请求

      +

      POST /api/:owner/:repo/project_invite_links/generate_link.json?invite_sign=xxx

      +

      请求参数

      + + + + + + + + + + + + + + + + +
      参数必选默认类型字段说明
      invite_signstring项目邀请链接的标识
      + +
      +

      返回的JSON示例:

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

      申请加入项目

      申请加入项目

      @@ -4398,9 +4766,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X POST http://localhost:3000/api/applied_projects.json
       
      await octokit.request('POST /api/appliedr_projects.json')
      -

      HTTP 请求

      +

      HTTP 请求

      POST /api/applied_projects.json

      -

      请求参数

      +

      请求参数

      @@ -4435,7 +4803,7 @@ Success — a happy kitten is an authenticated kitten! "role": "developer" } } -

      返回字段说明

      +

      返回字段说明

      参数
      @@ -4576,9 +4944,9 @@ Success — a happy kitten is an authenticated kitten! -d"limit=5"\ http://localhost:3000/api/projects | jq
      await octokit.request('GET /api/projects')
      -

      HTTP 请求

      +

      HTTP 请求

      GET api/projects

      -

      请求参数

      +

      请求参数

      参数
      @@ -4645,7 +5013,7 @@ http://localhost:3000/api/projects | jq
      参数项目类型, 取值为:common、mirror; common:开源托管项目, mirror:开源镜像项目
      -

      返回字段说明

      +

      返回字段说明

      @@ -4797,9 +5165,9 @@ Remember — a happy kitten is an authenticated kitten!
      curl -X GET \
       http://localhost:3000/api/projects/recommend  | jq
       
      await octokit.request('GET /api/projects/recommend.json')
      -

      HTTP 请求

      +

      HTTP 请求

      GET api/projects/recommend

      -

      返回字段说明

      +

      返回字段说明

      参数
      @@ -4933,9 +5301,9 @@ Remember — a happy kitten is an authenticated kitten!
      curl -X GET \
       http://localhost:3000/api/yystopf/ceshi/menu_list  | jq
       
      await octokit.request('GET /api/yystopf/ceshi/menu_list')
      -

      HTTP 请求

      +

      HTTP 请求

      GET api/:owner/:repo/menu_list

      -

      请求参数

      +

      请求参数

      参数
      @@ -4960,7 +5328,7 @@ http://localhost:3000/api/yystopf/ceshi/menu_list | jq
      参数项目标识identifier
      -

      返回字段说明

      +

      返回字段说明

      @@ -5001,9 +5369,9 @@ http://localhost:3000/api/yystopf/ceshi/menu_list | jq
      curl -X GET \
       http://localhost:3000/api/jasder/forgeplus/about  | jq
       
      await octokit.request('GET /api/jasder/forgeplus/about')
      -

      HTTP 请求

      +

      HTTP 请求

      GET api/:owner/:repo/about

      -

      请求参数

      +

      请求参数

      参数
      @@ -5028,7 +5396,7 @@ http://localhost:3000/api/jasder/forgeplus/about | jq
      参数项目标识identifier
      -

      返回字段说明

      +

      返回字段说明

      @@ -5074,7 +5442,7 @@ Remember — a happy kitten is an authenticated kitten!
      curl -X GET \
       http://localhost:3000/api/yystopf/ceshi/project_units.json
       
      await octokit.request('GET /api/yystopf/ceshi/project_units')
      -

      HTTP 请求

      +

      HTTP 请求

      GET /api/yystopf/ceshi/project_units

      返回字段说明:

      参数
      @@ -5117,9 +5485,9 @@ http://localhost:3000/api/yystopf/ceshi/project_units.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 请求

      +

      HTTP 请求

      POST /api/yystopf/ceshi/project_units

      -

      请求参数

      +

      请求参数

      @@ -5181,9 +5549,9 @@ http://localhost:3000/api/yystopf/ceshi/project_units.json -d"license_id=1"\ http://localhost:3000/api/projects.json
      await octokit.request('GET /api/projects.json')
      -

      HTTP 请求

      +

      HTTP 请求

      POST api/projects

      -

      请求参数

      +

      请求参数

      参数
      @@ -5257,7 +5625,7 @@ http://localhost:3000/api/projects.json
      参数项目是否私有, true:为私有,false: 公开,默认为公开
      -

      返回字段说明

      +

      返回字段说明

      @@ -5299,9 +5667,9 @@ http://localhost:3000/api/projects.json -d"project_language_id=2"\ http://localhost:3000/api/projects/migrate.json
      await octokit.request('GET /api/projects/migrate.json')
      -

      HTTP 请求

      +

      HTTP 请求

      POST api/projects/migrate.json

      -

      请求参数

      +

      请求参数

      参数
      @@ -5389,7 +5757,7 @@ http://localhost:3000/api/projects/migrate.json
      参数项目是否私有, true:为私有,false: 非私有,默认为公开
      -

      返回字段说明

      +

      返回字段说明

      @@ -5424,9 +5792,9 @@ http://localhost:3000/api/projects/migrate.json
      curl -X POST http://localhost:3000/api/repositories/1244/sync_mirror.json
       
      await octokit.request('POST /api/repositories/1244/sync_mirror.json')
      -

      HTTP 请求

      +

      HTTP 请求

      POST api/repositories/:id/sync_mirror.json

      -

      请求参数

      +

      请求参数

      参数
      @@ -5444,7 +5812,7 @@ http://localhost:3000/api/projects/migrate.json
      参数仓库id
      -

      返回字段说明

      +

      返回字段说明

      @@ -5479,9 +5847,9 @@ http://localhost:3000/api/projects/migrate.json
      curl -X POST http://localhost:3000/api/jasder/forgeplus/forks.json
       
      await octokit.request('POST /api/jaser/jasder_test/forks.json')
      -

      HTTP 请求

      +

      HTTP 请求

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

      -

      请求参数

      +

      请求参数

      参数
      @@ -5506,7 +5874,7 @@ http://localhost:3000/api/projects/migrate.json
      参数项目标识identifier
      -

      返回字段说明

      +

      返回字段说明

      @@ -5542,9 +5910,9 @@ http://localhost:3000/api/projects/migrate.json
      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 请求

      +

      HTTP 请求

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

      -

      请求参数

      +

      请求参数

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

      返回字段说明

      +

      返回字段说明

      @@ -5636,9 +6004,9 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat
      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 请求

      +

      HTTP 请求

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

      -

      请求参数

      +

      请求参数

      参数
      @@ -5670,7 +6038,7 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat
      参数迁移对象标识
      -

      返回字段说明

      +

      返回字段说明

      @@ -5840,9 +6208,9 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat
      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 请求

      +

      HTTP 请求

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

      -

      请求参数

      +

      请求参数

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

      返回字段说明

      +

      返回字段说明

      @@ -6037,9 +6405,9 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat
      curl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/quit.json
       
      await octokit.request('POST /api/:owner/:repo/quit.json')
      -

      HTTP 请求

      +

      HTTP 请求

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

      -

      请求参数

      +

      请求参数

      参数
      From 3d0e32380145aac8574ee1773e0762978369f8fb Mon Sep 17 00:00:00 2001 From: yystopf Date: Fri, 17 Jun 2022 14:07:33 +0800 Subject: [PATCH 066/208] fix --- app/models/project_unit.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/project_unit.rb b/app/models/project_unit.rb index 01f6c667..c3810ff8 100644 --- a/app/models/project_unit.rb +++ b/app/models/project_unit.rb @@ -15,7 +15,6 @@ class ProjectUnit < ApplicationRecord - default_scope {where(unit_type: ProjectUnit.unit_types.keys)} belongs_to :project enum unit_type: {code: 1, issues: 2, pulls: 3, wiki:4, devops: 5, versions: 6, resources: 7} From 5b4e6f490279752352740e5fd4503a7bc06f75ac Mon Sep 17 00:00:00 2001 From: yystopf Date: Fri, 17 Jun 2022 14:19:41 +0800 Subject: [PATCH 067/208] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E5=AD=98=E5=9C=A8=E6=95=8F=E6=84=9F=E8=AF=8D=E6=9B=B4?= =?UTF-8?q?=E6=96=B0forked=E6=95=B0=E9=87=8F=E4=BD=BF=E7=94=A8update=5Fcol?= =?UTF-8?q?umn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/project.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/project.rb b/app/models/project.rb index edaa00eb..444a0ec8 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -187,7 +187,7 @@ class Project < ApplicationRecord forked_project = self.forked_from_project if forked_project.present? forked_project.decrement(:forked_count, 1) - forked_project.save + forked_project.update_column(:forked_count, forked_project.forked_count) end end From 1f9caeece5f74e5b69b2a2be68ea5f710ccbe9b9 Mon Sep 17 00:00:00 2001 From: xiaoxiaoqiong Date: Fri, 17 Jun 2022 17:02:24 +0800 Subject: [PATCH 068/208] =?UTF-8?q?auth2=E6=8E=A5=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile | 266 +++++---- app/controllers/application_controller.rb | 8 +- app/models/user.rb | 15 + config/initializers/doorkeeper.rb | 553 ++++++++++++++++++ config/locales/doorkeeper.en.yml | 151 +++++ config/locales/doorkeeper.zh-CN.yml | 126 ++++ config/routes.rb | 1 + ...20220616040105_create_doorkeeper_tables.rb | 88 +++ 8 files changed, 1075 insertions(+), 133 deletions(-) create mode 100644 config/initializers/doorkeeper.rb create mode 100644 config/locales/doorkeeper.en.yml create mode 100644 config/locales/doorkeeper.zh-CN.yml create mode 100644 db/migrate/20220616040105_create_doorkeeper_tables.rb diff --git a/Gemfile b/Gemfile index 713eb860..effb2be6 100644 --- a/Gemfile +++ b/Gemfile @@ -1,130 +1,136 @@ -source 'https://gems.ruby-china.com' -git_source(:github) { |repo| "https://github.com/#{repo}.git" } - -gem 'rails', '~> 5.2.0' -gem 'mysql2', '>= 0.4.4', '< 0.6.0' -gem 'puma', '~> 3.11' -gem 'sass-rails', '~> 5.0' -gem 'uglifier', '>= 1.3.0' - -# gem 'coffee-rails', '~> 4.2' -gem 'turbolinks', '~> 5' -gem 'jbuilder', '~> 2.5' -gem 'groupdate', '~> 4.1.0' -gem 'chartkick' -gem 'grape-entity', '~> 0.7.1' -gem 'kaminari', '~> 1.1', '>= 1.1.1' - -gem 'bootsnap', '>= 1.1.0', require: false - -gem 'chinese_pinyin' - -gem 'rack-cors' -gem 'redis-rails' -gem 'roo-xls' -gem 'simple_xlsx_reader' - -gem 'rubyzip' - -gem 'spreadsheet' -gem 'ruby-ole' -# 导出为xlsx -gem 'axlsx', '~> 3.0.0.pre' -gem 'axlsx_rails', '~> 0.5.2' - -gem 'oauth2' -#导出为pdf -gem 'pdfkit' -gem 'wkhtmltopdf-binary' -# gem 'request_store' -#gem 'iconv' -# markdown 转html -gem 'redcarpet', '~> 3.4' - -gem 'rqrcode', '~> 0.10.1' -gem 'rqrcode_png' - -gem 'acts-as-taggable-on', '~> 6.0' - -# a tree structure -gem 'ancestry' -gem 'acts_as_list' -gem 'omniauth-cas' - -# profiler Middleware -gem 'rack-mini-profiler' - -# object-based searching -gem 'ransack' - -group :development, :test do - gem 'rspec-rails', '~> 3.8' -end - -group :development do - gem 'prettier' - gem 'rubocop', '~> 0.52.0' - gem 'solargraph', '~> 0.38.0' - gem 'awesome_print' - gem 'web-console', '>= 3.3.0' - gem 'listen', '>= 3.0.5', '< 3.2' - gem 'spring' - gem 'spring-watcher-listen', '~> 2.0.0' - gem "annotate", "~> 2.6.0" -end - -group :test do - gem 'capybara', '>= 2.15', '< 4.0' - gem 'selenium-webdriver' - gem 'chromedriver-helper' -end - -gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] - -#编码检测 -gem 'rchardet', '~> 1.8' - -# http client -gem 'faraday', '~> 0.15.4' - -# view -gem 'active_decorator' -gem 'bootstrap', '~> 4.3.1' -gem 'jquery-rails' -gem 'simple_form' -gem 'font-awesome-sass', '4.7.0' - -# i18n -gem 'rails-i18n', '~> 5.1' - -# job -gem 'sidekiq' -gem 'sinatra' -gem "sidekiq-cron", "~> 1.1" - -# batch insert -gem 'bulk_insert' - -# elasticsearch -gem 'searchkick' - -gem 'aasm' -gem 'enumerize' - -gem 'diffy' - -gem 'deep_cloneable', '~> 3.0.0' - -# oauth2 -gem 'omniauth', '~> 1.9.0' -gem 'omniauth-oauth2', '~> 1.6.0' - -# global var -gem 'request_store' - -# 敏感词汇 -gem 'harmonious_dictionary', '~> 0.0.1' - -gem 'parallel', '~> 1.19', '>= 1.19.1' - -gem 'letter_avatar' +source 'https://gems.ruby-china.com' +git_source(:github) { |repo| "https://github.com/#{repo}.git" } + +gem 'rails', '~> 5.2.0' +gem 'mysql2', '>= 0.4.4', '< 0.6.0' +gem 'puma', '~> 3.11' +gem 'sass-rails', '~> 5.0' +gem 'uglifier', '>= 1.3.0' + +# gem 'coffee-rails', '~> 4.2' +gem 'turbolinks', '~> 5' +gem 'jbuilder', '~> 2.5' +gem 'groupdate', '~> 4.1.0' +gem 'chartkick' +gem 'grape-entity', '~> 0.7.1' +gem 'kaminari', '~> 1.1', '>= 1.1.1' + +gem 'bootsnap', '>= 1.1.0', require: false + +gem 'chinese_pinyin' + +gem 'rack-cors' +gem 'redis-rails' +gem 'roo-xls' +gem 'simple_xlsx_reader' + +gem 'rubyzip' + +gem 'spreadsheet' +gem 'ruby-ole' +# 导出为xlsx +gem 'axlsx', '~> 3.0.0.pre' +gem 'axlsx_rails', '~> 0.5.2' + +gem 'oauth2' +#导出为pdf +gem 'pdfkit' +gem 'wkhtmltopdf-binary' +# gem 'request_store' +#gem 'iconv' +# markdown 转html +gem 'redcarpet', '~> 3.4' + +gem 'rqrcode', '~> 0.10.1' +gem 'rqrcode_png' + +gem 'acts-as-taggable-on', '~> 6.0' + +# a tree structure +gem 'ancestry' +gem 'acts_as_list' +gem 'omniauth-cas' + +# profiler Middleware +gem 'rack-mini-profiler' + +# object-based searching +gem 'ransack' + +group :development, :test do + gem 'rspec-rails', '~> 3.8' +end + +group :development do + gem 'prettier' + gem 'rubocop', '~> 0.52.0' + gem 'solargraph', '~> 0.38.0' + gem 'awesome_print' + gem 'web-console', '>= 3.3.0' + gem 'listen', '>= 3.0.5', '< 3.2' + gem 'spring' + gem 'spring-watcher-listen', '~> 2.0.0' + gem "annotate", "~> 2.6.0" +end + +group :test do + gem 'capybara', '>= 2.15', '< 4.0' + gem 'selenium-webdriver' + gem 'chromedriver-helper' +end + +gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] + +#编码检测 +gem 'rchardet', '~> 1.8' + +# http client +gem 'faraday', '~> 0.15.4' + +# view +gem 'active_decorator' +gem 'bootstrap', '~> 4.3.1' +gem 'jquery-rails' +gem 'simple_form' +gem 'font-awesome-sass', '4.7.0' + +# i18n +gem 'rails-i18n', '~> 5.1' + +# job +gem 'sidekiq' +gem 'sinatra' +gem "sidekiq-cron", "~> 1.1" + +# batch insert +gem 'bulk_insert' + +# elasticsearch +gem 'searchkick' + +gem 'aasm' +gem 'enumerize' + +gem 'diffy' + +gem 'deep_cloneable', '~> 3.0.0' + +# oauth2 +gem 'omniauth', '~> 1.9.0' +gem 'omniauth-oauth2', '~> 1.6.0' + +# global var +gem 'request_store' + +# 敏感词汇 +gem 'harmonious_dictionary', '~> 0.0.1' + +gem 'parallel', '~> 1.19', '>= 1.19.1' + +gem 'letter_avatar' + +gem 'jwt' + +gem 'doorkeeper' + +gem 'doorkeeper-jwt' diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 4e59a7e0..2ccd10ff 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -265,9 +265,11 @@ class ApplicationController < ActionController::Base end end - # if !User.current.logged? && Rails.env.development? - # User.current = User.find 1 - # end + if !User.current.logged? && Rails.env.development? + user = User.find 1 + User.current = user + start_user_session(user) + end # 测试版前端需求 diff --git a/app/models/user.rb b/app/models/user.rb index d3803a9f..2d2d15c5 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -671,6 +671,21 @@ class User < Owner raise text end + def self.authenticate!(login, password) + user = self.where("login = ? or mail = ? or phone = ? ", login.to_s.gsub(" ",''),login.to_s.gsub(" ",''),login.downcase.to_s.gsub(" ",'')).limit(1).first + return (user.check_password?(password) ? user : nil) unless user.nil? + nil + end + + # Generate public/private keys + def generate_keys + key_size = (Rails.env == 'test' ? 512 : 2048) + + serialized_private_key = OpenSSL::PKey::RSA::generate(key_size).to_s + serialized_public_key = OpenSSL::PKey::RSA.new(serialized_private_key) + [serialized_private_key, serialized_public_key] + end + def show_real_name name = lastname + firstname if name.blank? diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb new file mode 100644 index 00000000..196e2503 --- /dev/null +++ b/config/initializers/doorkeeper.rb @@ -0,0 +1,553 @@ +# frozen_string_literal: true + +Doorkeeper.configure do + # Change the ORM that doorkeeper will use (requires ORM extensions installed). + # Check the list of supported ORMs here: https://github.com/doorkeeper-gem/doorkeeper#orms + orm :active_record + + # This block will be called to check whether the resource owner is authenticated or not. + resource_owner_authenticator do + raise "Please configure doorkeeper resource_owner_authenticator block located in #{__FILE__}" + # Put your resource owner authentication logic here. + # Example implementation: + User.find_by(id: session[:www_user_id]) || redirect_to(new_user_session_url) + end + + resource_owner_from_credentials do |routes| + User.authenticate!(params[:username], params[:password]) + end + + access_token_generator '::Doorkeeper::JWT' + + admin_authenticator do + user = User.find_by_id(session[:www_user_id]) + unless user #&& user.admin_or_business? + redirect_to root_url + end + end + + # If you didn't skip applications controller from Doorkeeper routes in your application routes.rb + # file then you need to declare this block in order to restrict access to the web interface for + # adding oauth authorized applications. In other case it will return 403 Forbidden response + # every time somebody will try to access the admin web interface. + # + # admin_authenticator do + # # Put your admin authentication logic here. + # # Example implementation: + # + # if current_user + # head :forbidden unless current_user.admin? + # else + # redirect_to sign_in_url + # end + # end + + # You can use your own model classes if you need to extend (or even override) default + # Doorkeeper models such as `Application`, `AccessToken` and `AccessGrant. + # + # Be default Doorkeeper ActiveRecord ORM uses it's own classes: + # + # access_token_class "Doorkeeper::AccessToken" + # access_grant_class "Doorkeeper::AccessGrant" + # application_class "Doorkeeper::Application" + # + # Don't forget to include Doorkeeper ORM mixins into your custom models: + # + # * ::Doorkeeper::Orm::ActiveRecord::Mixins::AccessToken - for access token + # * ::Doorkeeper::Orm::ActiveRecord::Mixins::AccessGrant - for access grant + # * ::Doorkeeper::Orm::ActiveRecord::Mixins::Application - for application (OAuth2 clients) + # + # For example: + # + # access_token_class "MyAccessToken" + # + # class MyAccessToken < ApplicationRecord + # include ::Doorkeeper::Orm::ActiveRecord::Mixins::AccessToken + # + # self.table_name = "hey_i_wanna_my_name" + # + # def destroy_me! + # destroy + # end + # end + + # Enables polymorphic Resource Owner association for Access Tokens and Access Grants. + # By default this option is disabled. + # + # Make sure you properly setup you database and have all the required columns (run + # `bundle exec rails generate doorkeeper:enable_polymorphic_resource_owner` and execute Rails + # migrations). + # + # If this option enabled, Doorkeeper will store not only Resource Owner primary key + # value, but also it's type (class name). See "Polymorphic Associations" section of + # Rails guides: https://guides.rubyonrails.org/association_basics.html#polymorphic-associations + # + # [NOTE] If you apply this option on already existing project don't forget to manually + # update `resource_owner_type` column in the database and fix migration template as it will + # set NOT NULL constraint for Access Grants table. + # + # use_polymorphic_resource_owner + + # If you are planning to use Doorkeeper in Rails 5 API-only application, then you might + # want to use API mode that will skip all the views management and change the way how + # Doorkeeper responds to a requests. + # + # api_only + + # Enforce token request content type to application/x-www-form-urlencoded. + # It is not enabled by default to not break prior versions of the gem. + # + # enforce_content_type + + # Authorization Code expiration time (default: 10 minutes). + # + # authorization_code_expires_in 10.minutes + + # Access token expiration time (default: 2 hours). + # If you want to disable expiration, set this to `nil`. + # + # access_token_expires_in 2.hours + + # Assign custom TTL for access tokens. Will be used instead of access_token_expires_in + # option if defined. In case the block returns `nil` value Doorkeeper fallbacks to + # +access_token_expires_in+ configuration option value. If you really need to issue a + # non-expiring access token (which is not recommended) then you need to return + # Float::INFINITY from this block. + # + # `context` has the following properties available: + # + # * `client` - the OAuth client application (see Doorkeeper::OAuth::Client) + # * `grant_type` - the grant type of the request (see Doorkeeper::OAuth) + # * `scopes` - the requested scopes (see Doorkeeper::OAuth::Scopes) + # * `resource_owner` - authorized resource owner instance (if present) + # + # custom_access_token_expires_in do |context| + # context.client.additional_settings.implicit_oauth_expiration + # end + + # Use a custom class for generating the access token. + # See https://doorkeeper.gitbook.io/guides/configuration/other-configurations#custom-access-token-generator + # + # access_token_generator '::Doorkeeper::JWT' + + # The controller +Doorkeeper::ApplicationController+ inherits from. + # Defaults to +ActionController::Base+ unless +api_only+ is set, which changes the default to + # +ActionController::API+. The return value of this option must be a stringified class name. + # See https://doorkeeper.gitbook.io/guides/configuration/other-configurations#custom-controllers + # + # base_controller 'ApplicationController' + + # Reuse access token for the same resource owner within an application (disabled by default). + # + # This option protects your application from creating new tokens before old valid one becomes + # expired so your database doesn't bloat. Keep in mind that when this option is `on` Doorkeeper + # doesn't updates existing token expiration time, it will create a new token instead. + # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383 + # + # You can not enable this option together with +hash_token_secrets+. + # + # reuse_access_token + + # In case you enabled `reuse_access_token` option Doorkeeper will try to find matching + # token using `matching_token_for` Access Token API that searches for valid records + # in batches in order not to pollute the memory with all the database records. By default + # Doorkeeper uses batch size of 10 000 records. You can increase or decrease this value + # depending on your needs and server capabilities. + # + # token_lookup_batch_size 10_000 + + # Set a limit for token_reuse if using reuse_access_token option + # + # This option limits token_reusability to some extent. + # If not set then access_token will be reused unless it expires. + # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/1189 + # + # This option should be a percentage(i.e. (0,100]) + # + # token_reuse_limit 100 + + # Only allow one valid access token obtained via client credentials + # per client. If a new access token is obtained before the old one + # expired, the old one gets revoked (disabled by default) + # + # When enabling this option, make sure that you do not expect multiple processes + # using the same credentials at the same time (e.g. web servers spanning + # multiple machines and/or processes). + # + # revoke_previous_client_credentials_token + + # Hash access and refresh tokens before persisting them. + # This will disable the possibility to use +reuse_access_token+ + # since plain values can no longer be retrieved. + # + # Note: If you are already a user of doorkeeper and have existing tokens + # in your installation, they will be invalid without adding 'fallback: :plain'. + # + # hash_token_secrets + # By default, token secrets will be hashed using the + # +Doorkeeper::Hashing::SHA256+ strategy. + # + # If you wish to use another hashing implementation, you can override + # this strategy as follows: + # + # hash_token_secrets using: '::Doorkeeper::Hashing::MyCustomHashImpl' + # + # Keep in mind that changing the hashing function will invalidate all existing + # secrets, if there are any. + + # Hash application secrets before persisting them. + # + # hash_application_secrets + # + # By default, applications will be hashed + # with the +Doorkeeper::SecretStoring::SHA256+ strategy. + # + # If you wish to use bcrypt for application secret hashing, uncomment + # this line instead: + # + # hash_application_secrets using: '::Doorkeeper::SecretStoring::BCrypt' + + # When the above option is enabled, and a hashed token or secret is not found, + # you can allow to fall back to another strategy. For users upgrading + # doorkeeper and wishing to enable hashing, you will probably want to enable + # the fallback to plain tokens. + # + # This will ensure that old access tokens and secrets + # will remain valid even if the hashing above is enabled. + # + # This can be done by adding 'fallback: plain', e.g. : + # + # hash_application_secrets using: '::Doorkeeper::SecretStoring::BCrypt', fallback: :plain + + # Issue access tokens with refresh token (disabled by default), you may also + # pass a block which accepts `context` to customize when to give a refresh + # token or not. Similar to +custom_access_token_expires_in+, `context` has + # the following properties: + # + # `client` - the OAuth client application (see Doorkeeper::OAuth::Client) + # `grant_type` - the grant type of the request (see Doorkeeper::OAuth) + # `scopes` - the requested scopes (see Doorkeeper::OAuth::Scopes) + # + # use_refresh_token + + # Provide support for an owner to be assigned to each registered application (disabled by default) + # Optional parameter confirmation: true (default: false) if you want to enforce ownership of + # a registered application + # NOTE: you must also run the rails g doorkeeper:application_owner generator + # to provide the necessary support + # + # enable_application_owner confirmation: false + + # Define access token scopes for your provider + # For more information go to + # https://doorkeeper.gitbook.io/guides/ruby-on-rails/scopes + # + # default_scopes :public + # optional_scopes :write, :update + + # Allows to restrict only certain scopes for grant_type. + # By default, all the scopes will be available for all the grant types. + # + # Keys to this hash should be the name of grant_type and + # values should be the array of scopes for that grant type. + # Note: scopes should be from configured_scopes (i.e. default or optional) + # + # scopes_by_grant_type password: [:write], client_credentials: [:update] + + # Forbids creating/updating applications with arbitrary scopes that are + # not in configuration, i.e. +default_scopes+ or +optional_scopes+. + # (disabled by default) + # + # enforce_configured_scopes + + # Change the way client credentials are retrieved from the request object. + # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then + # falls back to the `:client_id` and `:client_secret` params from the `params` object. + # Check out https://github.com/doorkeeper-gem/doorkeeper/wiki/Changing-how-clients-are-authenticated + # for more information on customization + # + # client_credentials :from_basic, :from_params + + # Change the way access token is authenticated from the request object. + # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then + # falls back to the `:access_token` or `:bearer_token` params from the `params` object. + # Check out https://github.com/doorkeeper-gem/doorkeeper/wiki/Changing-how-clients-are-authenticated + # for more information on customization + # + # access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param + + # Forces the usage of the HTTPS protocol in non-native redirect uris (enabled + # by default in non-development environments). OAuth2 delegates security in + # communication to the HTTPS protocol so it is wise to keep this enabled. + # + # Callable objects such as proc, lambda, block or any object that responds to + # #call can be used in order to allow conditional checks (to allow non-SSL + # redirects to localhost for example). + # + # force_ssl_in_redirect_uri !Rails.env.development? + # + # force_ssl_in_redirect_uri { |uri| uri.host != 'localhost' } + + # Specify what redirect URI's you want to block during Application creation. + # Any redirect URI is allowed by default. + # + # You can use this option in order to forbid URI's with 'javascript' scheme + # for example. + # + # forbid_redirect_uri { |uri| uri.scheme.to_s.downcase == 'javascript' } + + # Allows to set blank redirect URIs for Applications in case Doorkeeper configured + # to use URI-less OAuth grant flows like Client Credentials or Resource Owner + # Password Credentials. The option is on by default and checks configured grant + # types, but you **need** to manually drop `NOT NULL` constraint from `redirect_uri` + # column for `oauth_applications` database table. + # + # You can completely disable this feature with: + # + # allow_blank_redirect_uri false + # + # Or you can define your custom check: + # + # allow_blank_redirect_uri do |grant_flows, client| + # client.superapp? + # end + + # Specify how authorization errors should be handled. + # By default, doorkeeper renders json errors when access token + # is invalid, expired, revoked or has invalid scopes. + # + # If you want to render error response yourself (i.e. rescue exceptions), + # set +handle_auth_errors+ to `:raise` and rescue Doorkeeper::Errors::InvalidToken + # or following specific errors: + # + # Doorkeeper::Errors::TokenForbidden, Doorkeeper::Errors::TokenExpired, + # Doorkeeper::Errors::TokenRevoked, Doorkeeper::Errors::TokenUnknown + # + # handle_auth_errors :raise + + # Customize token introspection response. + # Allows to add your own fields to default one that are required by the OAuth spec + # for the introspection response. It could be `sub`, `aud` and so on. + # This configuration option can be a proc, lambda or any Ruby object responds + # to `.call` method and result of it's invocation must be a Hash. + # + # custom_introspection_response do |token, context| + # { + # "sub": "Z5O3upPC88QrAjx00dis", + # "aud": "https://protected.example.net/resource", + # "username": User.find(token.resource_owner_id).username + # } + # end + # + # or + # + # custom_introspection_response CustomIntrospectionResponder + + # Specify what grant flows are enabled in array of Strings. The valid + # strings and the flows they enable are: + # + # "authorization_code" => Authorization Code Grant Flow + # "implicit" => Implicit Grant Flow + # "password" => Resource Owner Password Credentials Grant Flow + # "client_credentials" => Client Credentials Grant Flow + # + # If not specified, Doorkeeper enables authorization_code and + # client_credentials. + # + # implicit and password grant flows have risks that you should understand + # before enabling: + # https://datatracker.ietf.org/doc/html/rfc6819#section-4.4.2 + # https://datatracker.ietf.org/doc/html/rfc6819#section-4.4.3 + # + # + grant_flows %w[authorization_code client_credentials password] + + # Allows to customize OAuth grant flows that +each+ application support. + # You can configure a custom block (or use a class respond to `#call`) that must + # return `true` in case Application instance supports requested OAuth grant flow + # during the authorization request to the server. This configuration +doesn't+ + # set flows per application, it only allows to check if application supports + # specific grant flow. + # + # For example you can add an additional database column to `oauth_applications` table, + # say `t.array :grant_flows, default: []`, and store allowed grant flows that can + # be used with this application there. Then when authorization requested Doorkeeper + # will call this block to check if specific Application (passed with client_id and/or + # client_secret) is allowed to perform the request for the specific grant type + # (authorization, password, client_credentials, etc). + # + # Example of the block: + # + # ->(flow, client) { client.grant_flows.include?(flow) } + # + # In case this option invocation result is `false`, Doorkeeper server returns + # :unauthorized_client error and stops the request. + # + # @param allow_grant_flow_for_client [Proc] Block or any object respond to #call + # @return [Boolean] `true` if allow or `false` if forbid the request + # + # allow_grant_flow_for_client do |grant_flow, client| + # # `grant_flows` is an Array column with grant + # # flows that application supports + # + # client.grant_flows.include?(grant_flow) + # end + + # If you need arbitrary Resource Owner-Client authorization you can enable this option + # and implement the check your need. Config option must respond to #call and return + # true in case resource owner authorized for the specific application or false in other + # cases. + # + # Be default all Resource Owners are authorized to any Client (application). + # + # authorize_resource_owner_for_client do |client, resource_owner| + # resource_owner.admin? || client.owners_allowlist.include?(resource_owner) + # end + + # Hook into the strategies' request & response life-cycle in case your + # application needs advanced customization or logging: + # + # before_successful_strategy_response do |request| + # puts "BEFORE HOOK FIRED! #{request}" + # end + # + # after_successful_strategy_response do |request, response| + # puts "AFTER HOOK FIRED! #{request}, #{response}" + # end + + # Hook into Authorization flow in order to implement Single Sign Out + # or add any other functionality. Inside the block you have an access + # to `controller` (authorizations controller instance) and `context` + # (Doorkeeper::OAuth::Hooks::Context instance) which provides pre auth + # or auth objects with issued token based on hook type (before or after). + # + # before_successful_authorization do |controller, context| + # Rails.logger.info(controller.request.params.inspect) + # + # Rails.logger.info(context.pre_auth.inspect) + # end + # + # after_successful_authorization do |controller, context| + # controller.session[:logout_urls] << + # Doorkeeper::Application + # .find_by(controller.request.params.slice(:redirect_uri)) + # .logout_uri + # + # Rails.logger.info(context.auth.inspect) + # Rails.logger.info(context.issued_token) + # end + + # Under some circumstances you might want to have applications auto-approved, + # so that the user skips the authorization step. + # For example if dealing with a trusted application. + # + # skip_authorization do |resource_owner, client| + # client.superapp? or resource_owner.admin? + # end + + # Configure custom constraints for the Token Introspection request. + # By default this configuration option allows to introspect a token by another + # token of the same application, OR to introspect the token that belongs to + # authorized client (from authenticated client) OR when token doesn't + # belong to any client (public token). Otherwise requester has no access to the + # introspection and it will return response as stated in the RFC. + # + # Block arguments: + # + # @param token [Doorkeeper::AccessToken] + # token to be introspected + # + # @param authorized_client [Doorkeeper::Application] + # authorized client (if request is authorized using Basic auth with + # Client Credentials for example) + # + # @param authorized_token [Doorkeeper::AccessToken] + # Bearer token used to authorize the request + # + # In case the block returns `nil` or `false` introspection responses with 401 status code + # when using authorized token to introspect, or you'll get 200 with { "active": false } body + # when using authorized client to introspect as stated in the + # RFC 7662 section 2.2. Introspection Response. + # + # Using with caution: + # Keep in mind that these three parameters pass to block can be nil as following case: + # `authorized_client` is nil if and only if `authorized_token` is present, and vice versa. + # `token` will be nil if and only if `authorized_token` is present. + # So remember to use `&` or check if it is present before calling method on + # them to make sure you doesn't get NoMethodError exception. + # + # You can define your custom check: + # + # allow_token_introspection do |token, authorized_client, authorized_token| + # if authorized_token + # # customize: require `introspection` scope + # authorized_token.application == token&.application || + # authorized_token.scopes.include?("introspection") + # elsif token.application + # # `protected_resource` is a new database boolean column, for example + # authorized_client == token.application || authorized_client.protected_resource? + # else + # # public token (when token.application is nil, token doesn't belong to any application) + # true + # end + # end + # + # Or you can completely disable any token introspection: + # + # allow_token_introspection false + # + # If you need to block the request at all, then configure your routes.rb or web-server + # like nginx to forbid the request. + + # WWW-Authenticate Realm (default: "Doorkeeper"). + # + # realm "Doorkeeper" +end + + +Doorkeeper::JWT.configure do + # Set the payload for the JWT token. This should contain unique information + # about the user. Defaults to a randomly generated token in a hash: + # { token: "RANDOM-TOKEN" } + token_payload do |opts| + user = User.find(opts[:resource_owner_id]) + + { + iss: 'My App', + iat: Time.current.utc.to_i, + + # @see JWT reserved claims - https://tools.ietf.org/html/draft-jones-json-web-token-07#page-7 + jti: SecureRandom.uuid, + + user: { + id: user.id, + login: user.login, + mail: user.mail + } + } + end + + # Optionally set additional headers for the JWT. See + # https://tools.ietf.org/html/rfc7515#section-4.1 + token_headers do |opts| + { kid: opts[:application][:uid] } + end + + # Use the application secret specified in the access grant token. Defaults to + # `false`. If you specify `use_application_secret true`, both `secret_key` and + # `secret_key_path` will be ignored. + use_application_secret false + + # Set the encryption secret. This would be shared with any other applications + # that should be able to read the payload of the token. Defaults to "secret". + secret_key ENV['JWT_SECRET'] + + # If you want to use RS* encoding specify the path to the RSA key to use for + # signing. If you specify a `secret_key_path` it will be used instead of + # `secret_key`. + secret_key_path File.join('path', 'to', 'file.pem') + + # Specify encryption type (https://github.com/progrium/ruby-jwt). Defaults to + # `nil`. + encryption_method :hs512 +end diff --git a/config/locales/doorkeeper.en.yml b/config/locales/doorkeeper.en.yml new file mode 100644 index 00000000..99fa3d4d --- /dev/null +++ b/config/locales/doorkeeper.en.yml @@ -0,0 +1,151 @@ +en: + activerecord: + attributes: + doorkeeper/application: + name: 'Name' + redirect_uri: 'Redirect URI' + errors: + models: + doorkeeper/application: + attributes: + redirect_uri: + fragment_present: 'cannot contain a fragment.' + invalid_uri: 'must be a valid URI.' + unspecified_scheme: 'must specify a scheme.' + relative_uri: 'must be an absolute URI.' + secured_uri: 'must be an HTTPS/SSL URI.' + forbidden_uri: 'is forbidden by the server.' + scopes: + not_match_configured: "doesn't match configured on the server." + + doorkeeper: + applications: + confirmations: + destroy: 'Are you sure?' + buttons: + edit: 'Edit' + destroy: 'Destroy' + submit: 'Submit' + cancel: 'Cancel' + authorize: 'Authorize' + form: + error: 'Whoops! Check your form for possible errors' + help: + confidential: 'Application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential.' + redirect_uri: 'Use one line per URI' + blank_redirect_uri: "Leave it blank if you configured your provider to use Client Credentials, Resource Owner Password Credentials or any other grant type that doesn't require redirect URI." + scopes: 'Separate scopes with spaces. Leave blank to use the default scopes.' + edit: + title: 'Edit application' + index: + title: 'Your applications' + new: 'New Application' + name: 'Name' + callback_url: 'Callback URL' + confidential: 'Confidential?' + actions: 'Actions' + confidentiality: + 'yes': 'Yes' + 'no': 'No' + new: + title: 'New Application' + show: + title: 'Application: %{name}' + application_id: 'UID' + secret: 'Secret' + secret_hashed: 'Secret hashed' + scopes: 'Scopes' + confidential: 'Confidential' + callback_urls: 'Callback urls' + actions: 'Actions' + not_defined: 'Not defined' + + authorizations: + buttons: + authorize: 'Authorize' + deny: 'Deny' + error: + title: 'An error has occurred' + new: + title: 'Authorization required' + prompt: 'Authorize %{client_name} to use your account?' + able_to: 'This application will be able to' + show: + title: 'Authorization code' + form_post: + title: 'Submit this form' + + authorized_applications: + confirmations: + revoke: 'Are you sure?' + buttons: + revoke: 'Revoke' + index: + title: 'Your authorized applications' + application: 'Application' + created_at: 'Created At' + date_format: '%Y-%m-%d %H:%M:%S' + + pre_authorization: + status: 'Pre-authorization' + + errors: + messages: + # Common error messages + invalid_request: + unknown: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.' + missing_param: 'Missing required parameter: %{value}.' + request_not_authorized: 'Request need to be authorized. Required parameter for authorizing request is missing or invalid.' + invalid_redirect_uri: "The requested redirect uri is malformed or doesn't match client redirect URI." + unauthorized_client: 'The client is not authorized to perform this request using this method.' + access_denied: 'The resource owner or authorization server denied the request.' + invalid_scope: 'The requested scope is invalid, unknown, or malformed.' + invalid_code_challenge_method: 'The code challenge method must be plain or S256.' + server_error: 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.' + temporarily_unavailable: 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.' + + # Configuration error messages + credential_flow_not_configured: 'Resource Owner Password Credentials flow failed due to Doorkeeper.configure.resource_owner_from_credentials being unconfigured.' + resource_owner_authenticator_not_configured: 'Resource Owner find failed due to Doorkeeper.configure.resource_owner_authenticator being unconfigured.' + admin_authenticator_not_configured: 'Access to admin panel is forbidden due to Doorkeeper.configure.admin_authenticator being unconfigured.' + + # Access grant errors + unsupported_response_type: 'The authorization server does not support this response type.' + unsupported_response_mode: 'The authorization server does not support this response mode.' + + # Access token errors + invalid_client: 'Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method.' + invalid_grant: 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.' + unsupported_grant_type: 'The authorization grant type is not supported by the authorization server.' + + invalid_token: + revoked: "The access token was revoked" + expired: "The access token expired" + unknown: "The access token is invalid" + revoke: + unauthorized: "You are not authorized to revoke this token" + + forbidden_token: + missing_scope: 'Access to this resource requires scope "%{oauth_scopes}".' + + flash: + applications: + create: + notice: 'Application created.' + destroy: + notice: 'Application deleted.' + update: + notice: 'Application updated.' + authorized_applications: + destroy: + notice: 'Application revoked.' + + layouts: + admin: + title: 'Doorkeeper' + nav: + oauth2_provider: 'OAuth2 Provider' + applications: 'Applications' + home: 'Home' + application: + title: 'OAuth authorization required' diff --git a/config/locales/doorkeeper.zh-CN.yml b/config/locales/doorkeeper.zh-CN.yml new file mode 100644 index 00000000..3a00820a --- /dev/null +++ b/config/locales/doorkeeper.zh-CN.yml @@ -0,0 +1,126 @@ +zh-CN: + activerecord: + attributes: + doorkeeper/application: + name: '名称' + redirect_uri: '登录回调地址' + scopes: '权限范围' + errors: + models: + doorkeeper/application: + attributes: + redirect_uri: + fragment_present: '不能包含片段(#)' + invalid_uri: '必须是有效的 URL 格式' + relative_uri: '必须是绝对的 URL 地址' + secured_uri: '必须是 HTTPS/SSL 的 URL 地址' + + doorkeeper: + applications: + confirmations: + destroy: '确定要删除应用吗?' + buttons: + edit: '编辑' + destroy: '删除' + submit: '提交' + cancel: '取消' + authorize: '授权' + form: + error: '抱歉! 提交信息的时候遇到了下面的错误' + help: + redirect_uri: '每行只能有一个 URL' + native_redirect_uri: '使用 %{native_redirect_uri} 作为本地测试' + scopes: '用空格隔开权限范围,留空则使用默认设置' + edit: + title: '修改应用' + index: + title: '你的应用' + new: '创建新应用' + name: '名称' + callback_url: '登录回调地址' + new: + title: '创建新应用' + show: + title: '应用:%{name}' + application_id: '应用 ID' + secret: '私钥' + scopes: '权限范围' + callback_urls: '登录回调地址' + confidential: 'Confidential' + actions: '操作' + + authorizations: + buttons: + authorize: '授权' + deny: '拒绝' + error: + title: '存在错误' + new: + title: '需要你授权' + prompt: '授权 %{client_name} 使用你的帐号?' + able_to: '此应用将会' + show: + title: '授权码' + + authorized_applications: + confirmations: + revoke: '确定要注销此应用的认证信息吗?' + buttons: + revoke: '注销' + index: + title: '你授权的应用列表' + application: '应用' + created_at: '授权时间' + date_format: '%Y-%m-%d %H:%M:%S' + + errors: + messages: + # Common error messages + invalid_request: '这个请求缺少必要的参数,或者参数值、格式不正确' + invalid_redirect_uri: '无效的登录回调地址' + unauthorized_client: '未授权的应用,请求无法执行' + access_denied: '用户或服务器拒绝了请求' + invalid_scope: '请求范围无效、未知或格式不正确' + server_error: '服务器异常,无法处理请求' + temporarily_unavailable: '服务器维护中或负载过高,暂时无法处理请求' + + #configuration error messages + credential_flow_not_configured: 'Resource Owner Password Credentials flow failed,原因是 Doorkeeper.configure.resource_owner_from_credentials 尚未设置。' + resource_owner_authenticator_not_configured: 'Resource Owner find failed,原因是 Doorkeeper.configure.resource_owner_authenticator 尚未设置。' + + # Access grant errors + unsupported_response_type: '服务器不支持这种响应类型' + + # Access token errors + invalid_client: '由于未知、不支持或没有客户端,认证失败' + invalid_grant: '授权方式无效,或者登录回调地址无效、过期或已被撤销' + unsupported_grant_type: '服务器不支持此类型的授权方式' + + # Password Access token errors + invalid_resource_owner: '资源所有者认证无效或没有所有者' + + invalid_token: + revoked: "访问令牌已被吊销" + expired: "访问令牌已过期" + unknown: "访问令牌无效" + + flash: + applications: + create: + notice: '应用创建成功' + destroy: + notice: '应用删除成功' + update: + notice: '应用修改成功' + authorized_applications: + destroy: + notice: '已成功注销了应用的认证信息' + + layouts: + admin: + nav: + oauth2_provider: 'OAuth2 提供商' + applications: '应用' + home: '首页' + application: + title: 'OAuth 认证' diff --git a/config/routes.rb b/config/routes.rb index b585f077..4b434f77 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,6 @@ Rails.application.routes.draw do + use_doorkeeper require 'sidekiq/web' require 'sidekiq/cron/web' require 'admin_constraint' diff --git a/db/migrate/20220616040105_create_doorkeeper_tables.rb b/db/migrate/20220616040105_create_doorkeeper_tables.rb new file mode 100644 index 00000000..b4ec636e --- /dev/null +++ b/db/migrate/20220616040105_create_doorkeeper_tables.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +class CreateDoorkeeperTables < ActiveRecord::Migration[5.2] + def change + create_table :oauth_applications do |t| + t.string :name, null: false + t.string :uid, null: false + t.string :secret, null: false + + # Remove `null: false` if you are planning to use grant flows + # that doesn't require redirect URI to be used during authorization + # like Client Credentials flow or Resource Owner Password. + t.text :redirect_uri, null: false + t.string :scopes, null: false, default: '' + t.boolean :confidential, null: false, default: true + t.timestamps null: false + end + + add_index :oauth_applications, :uid, unique: true + + create_table :oauth_access_grants do |t| + t.references :resource_owner, null: false + t.references :application, null: false + t.string :token, null: false + t.integer :expires_in, null: false + t.text :redirect_uri, null: false + t.datetime :created_at, null: false + t.datetime :revoked_at + t.string :scopes, null: false, default: '' + end + + add_index :oauth_access_grants, :token, unique: true + add_foreign_key( + :oauth_access_grants, + :oauth_applications, + column: :application_id + ) + + create_table :oauth_access_tokens do |t| + t.references :resource_owner, index: true + + # Remove `null: false` if you are planning to use Password + # Credentials Grant flow that doesn't require an application. + t.references :application, null: false + + # If you use a custom token generator you may need to change this column + # from string to text, so that it accepts tokens larger than 255 + # characters. More info on custom token generators in: + # https://github.com/doorkeeper-gem/doorkeeper/tree/v3.0.0.rc1#custom-access-token-generator + # + # t.text :token, null: false + t.string :token, null: false + + t.string :refresh_token + t.integer :expires_in + t.datetime :revoked_at + t.datetime :created_at, null: false + t.string :scopes + + # The authorization server MAY issue a new refresh token, in which case + # *the client MUST discard the old refresh token* and replace it with the + # new refresh token. The authorization server MAY revoke the old + # refresh token after issuing a new refresh token to the client. + # @see https://datatracker.ietf.org/doc/html/rfc6749#section-6 + # + # Doorkeeper implementation: if there is a `previous_refresh_token` column, + # refresh tokens will be revoked after a related access token is used. + # If there is no `previous_refresh_token` column, previous tokens are + # revoked as soon as a new access token is created. + # + # Comment out this line if you want refresh tokens to be instantly + # revoked after use. + t.string :previous_refresh_token, null: false, default: "" + end + + add_index :oauth_access_tokens, :token, unique: true + add_index :oauth_access_tokens, :refresh_token, unique: true + add_foreign_key( + :oauth_access_tokens, + :oauth_applications, + column: :application_id + ) + + # Uncomment below to ensure a valid reference to the resource owner's table + # add_foreign_key :oauth_access_grants, , column: :resource_owner_id + # add_foreign_key :oauth_access_tokens, , column: :resource_owner_id + end +end From 21ccedab9c4fe6195a24b5ec07f052567f5f6df5 Mon Sep 17 00:00:00 2001 From: yystopf Date: Fri, 17 Jun 2022 18:33:37 +0800 Subject: [PATCH 069/208] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20doorkeeper=20?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E7=94=9F=E6=88=90jwt=20token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile.lock | 7 + config/initializers/doorkeeper.rb | 11 +- config/locales/doorkeeper.zh-CN.yml | 177 +++++++++--------- ...oauth_access_tokens_token_column_length.rb | 5 + 4 files changed, 112 insertions(+), 88 deletions(-) create mode 100644 db/migrate/20220617103002_change_oauth_access_tokens_token_column_length.rb diff --git a/Gemfile.lock b/Gemfile.lock index b1dc2cca..99f40ad9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -106,6 +106,10 @@ GEM activerecord (>= 3.1.0, < 7) diff-lcs (1.3) diffy (3.3.0) + doorkeeper (5.5.4) + railties (>= 5) + doorkeeper-jwt (0.4.1) + jwt (>= 2.1) e2mmap (0.1.0) elasticsearch (7.5.0) elasticsearch-api (= 7.5.0) @@ -450,6 +454,8 @@ DEPENDENCIES chromedriver-helper deep_cloneable (~> 3.0.0) diffy + doorkeeper + doorkeeper-jwt enumerize faraday (~> 0.15.4) font-awesome-sass (= 4.7.0) @@ -458,6 +464,7 @@ DEPENDENCIES harmonious_dictionary (~> 0.0.1) jbuilder (~> 2.5) jquery-rails + jwt kaminari (~> 1.1, >= 1.1.1) letter_avatar listen (>= 3.0.5, < 3.2) diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index 196e2503..4f9db8cd 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -7,7 +7,7 @@ Doorkeeper.configure do # This block will be called to check whether the resource owner is authenticated or not. resource_owner_authenticator do - raise "Please configure doorkeeper resource_owner_authenticator block located in #{__FILE__}" + # raise "Please configure doorkeeper resource_owner_authenticator block located in #{__FILE__}" # Put your resource owner authentication logic here. # Example implementation: User.find_by(id: session[:www_user_id]) || redirect_to(new_user_session_url) @@ -228,7 +228,7 @@ Doorkeeper.configure do # `grant_type` - the grant type of the request (see Doorkeeper::OAuth) # `scopes` - the requested scopes (see Doorkeeper::OAuth::Scopes) # - # use_refresh_token + use_refresh_token # Provide support for an owner to be assigned to each registered application (disabled by default) # Optional parameter confirmation: true (default: false) if you want to enforce ownership of @@ -304,7 +304,7 @@ Doorkeeper.configure do # # You can completely disable this feature with: # - # allow_blank_redirect_uri false + allow_blank_redirect_uri true # # Or you can define your custom check: # @@ -444,6 +444,9 @@ Doorkeeper.configure do # skip_authorization do |resource_owner, client| # client.superapp? or resource_owner.admin? # end + skip_authorization do + true + end # Configure custom constraints for the Token Introspection request. # By default this configuration option allows to introspect a token by another @@ -540,7 +543,7 @@ Doorkeeper::JWT.configure do # Set the encryption secret. This would be shared with any other applications # that should be able to read the payload of the token. Defaults to "secret". - secret_key ENV['JWT_SECRET'] + secret_key ENV['JWT_SECRET'] || "forgeplus" # If you want to use RS* encoding specify the path to the RSA key to use for # signing. If you specify a `secret_key_path` it will be used instead of diff --git a/config/locales/doorkeeper.zh-CN.yml b/config/locales/doorkeeper.zh-CN.yml index 3a00820a..f05f65a5 100644 --- a/config/locales/doorkeeper.zh-CN.yml +++ b/config/locales/doorkeeper.zh-CN.yml @@ -2,125 +2,134 @@ zh-CN: activerecord: attributes: doorkeeper/application: - name: '名称' - redirect_uri: '登录回调地址' - scopes: '权限范围' + name: 应用名称 + redirect_uri: 重定向 URI errors: models: doorkeeper/application: attributes: redirect_uri: - fragment_present: '不能包含片段(#)' - invalid_uri: '必须是有效的 URL 格式' - relative_uri: '必须是绝对的 URL 地址' - secured_uri: '必须是 HTTPS/SSL 的 URL 地址' - + fragment_present: 不能包含网址片段(#) + invalid_uri: 必须是有效的 URI 格式 + unspecified_scheme: must specify a scheme. + relative_uri: 必须是绝对的 URI 地址 + secured_uri: 必须是 HTTPS/SSL 的 URI 地址 + forbidden_uri: 被服务器禁止。 + scopes: + not_match_configured: 不匹配服务器上的配置。 doorkeeper: applications: confirmations: - destroy: '确定要删除应用吗?' + destroy: 确定要删除应用吗? buttons: - edit: '编辑' - destroy: '删除' - submit: '提交' - cancel: '取消' - authorize: '授权' + edit: 编辑 + destroy: 删除 + submit: 提交 + cancel: 取消 + authorize: 授权 form: - error: '抱歉! 提交信息的时候遇到了下面的错误' + error: 抱歉! 提交信息的时候遇到了下面的错误 help: - redirect_uri: '每行只能有一个 URL' - native_redirect_uri: '使用 %{native_redirect_uri} 作为本地测试' - scopes: '用空格隔开权限范围,留空则使用默认设置' + confidential: 应用程序的client secret可以保密,但原生移动应用和单页应用将无法保护client secret。 + redirect_uri: 每行只能有一个 URI + blank_redirect_uri: Leave it blank if you configured your provider to use Client Credentials, Resource Owner Password Credentials or any other grant type that doesn't require redirect URI. + scopes: 用空格分割权限范围,留空则使用默认设置 edit: - title: '修改应用' + title: 修改应用 index: - title: '你的应用' - new: '创建新应用' - name: '名称' - callback_url: '登录回调地址' + title: 你的应用 + new: 创建新应用 + name: 名称 + callback_url: 回调 URL + confidential: Confidential? + actions: 动作 + confidentiality: + 'yes': 是 + 'no': 沒有 new: - title: '创建新应用' + title: 创建新应用 show: - title: '应用:%{name}' - application_id: '应用 ID' - secret: '私钥' - scopes: '权限范围' - callback_urls: '登录回调地址' - confidential: 'Confidential' - actions: '操作' - + title: 应用:%{name} + application_id: 应用 UID + secret: 应用密钥 + secret_hashed: Secret hashed + scopes: 权限范围 + confidential: Confidential + callback_urls: 回调 URL + actions: 操作 + not_defined: Not defined authorizations: buttons: - authorize: '授权' - deny: '拒绝' + authorize: 同意授权 + deny: 拒绝授权 error: - title: '存在错误' + title: 发生错误 new: - title: '需要你授权' - prompt: '授权 %{client_name} 使用你的帐号?' - able_to: '此应用将会' + title: 需要授权 + prompt: 授权 %{client_name} 使用你的帐户? + able_to: 此应用将能够 show: - title: '授权码' - + title: 授权代码 + form_post: + title: Submit this form authorized_applications: confirmations: - revoke: '确定要注销此应用的认证信息吗?' + revoke: 确定要撤销对此应用的授权吗? buttons: - revoke: '注销' + revoke: 撤销授权 index: - title: '你授权的应用列表' - application: '应用' - created_at: '授权时间' - date_format: '%Y-%m-%d %H:%M:%S' - + title: 已授权的应用 + application: 应用 + created_at: 授权时间 + date_format: "%Y-%m-%d %H:%M:%S" + pre_authorization: + status: 预授权 errors: messages: - # Common error messages - invalid_request: '这个请求缺少必要的参数,或者参数值、格式不正确' - invalid_redirect_uri: '无效的登录回调地址' - unauthorized_client: '未授权的应用,请求无法执行' - access_denied: '用户或服务器拒绝了请求' - invalid_scope: '请求范围无效、未知或格式不正确' - server_error: '服务器异常,无法处理请求' - temporarily_unavailable: '服务器维护中或负载过高,暂时无法处理请求' - - #configuration error messages - credential_flow_not_configured: 'Resource Owner Password Credentials flow failed,原因是 Doorkeeper.configure.resource_owner_from_credentials 尚未设置。' - resource_owner_authenticator_not_configured: 'Resource Owner find failed,原因是 Doorkeeper.configure.resource_owner_authenticator 尚未设置。' - - # Access grant errors - unsupported_response_type: '服务器不支持这种响应类型' - - # Access token errors - invalid_client: '由于未知、不支持或没有客户端,认证失败' - invalid_grant: '授权方式无效,或者登录回调地址无效、过期或已被撤销' - unsupported_grant_type: '服务器不支持此类型的授权方式' - - # Password Access token errors - invalid_resource_owner: '资源所有者认证无效或没有所有者' - + invalid_request: + unknown: 请求缺少必要的参数,或者参数值、格式不正确。 + missing_param: 'Missing required parameter: %{value}.' + request_not_authorized: Request need to be authorized. Required parameter for authorizing request is missing or invalid. + invalid_redirect_uri: 无效的登录回调地址。 + unauthorized_client: 未授权的应用,请求无法执行。 + access_denied: 资源所有者或服务器拒绝了请求。 + invalid_scope: 请求的权限范围无效、未知或格式不正确。 + invalid_code_challenge_method: The code challenge method must be plain or S256. + server_error: 服务器异常,无法处理请求。 + temporarily_unavailable: 服务器维护中或负载过高,暂时无法处理请求。 + credential_flow_not_configured: 由于 Doorkeeper.configure.resource_owner_from_credentials 尚未配置,应用验证授权流程失败。 + resource_owner_authenticator_not_configured: 由于 Doorkeeper.configure.resource_owner_authenticator 尚未配置,查找资源所有者失败。 + admin_authenticator_not_configured: 由于 Doorkeeper.configure.admin_authenticator 尚未配置,禁止访问管理员面板。 + unsupported_response_type: 服务器不支持这种响应类型。 + unsupported_response_mode: The authorization server does not support this response mode. + invalid_client: 由于应用信息未知、未提交认证信息或使用了不支持的认证方式,认证失败。 + invalid_grant: 授权方式无效、过期或已被撤销、与授权请求中的回调地址不一致,或使用了其他应用的回调地址。 + unsupported_grant_type: 服务器不支持此类型的授权方式。 invalid_token: - revoked: "访问令牌已被吊销" - expired: "访问令牌已过期" - unknown: "访问令牌无效" - + revoked: 访问令牌已被吊销 + expired: 访问令牌已过期 + unknown: 访问令牌无效 + revoke: + unauthorized: You are not authorized to revoke this token + forbidden_token: + missing_scope: Access to this resource requires scope "%{oauth_scopes}". flash: applications: create: - notice: '应用创建成功' + notice: 应用创建成功。 destroy: - notice: '应用删除成功' + notice: 应用删除成功。 update: - notice: '应用修改成功' + notice: 应用修改成功。 authorized_applications: destroy: - notice: '已成功注销了应用的认证信息' - + notice: 已成功撤销对此应用的授权。 layouts: admin: + title: Doorkeeper nav: - oauth2_provider: 'OAuth2 提供商' - applications: '应用' - home: '首页' + oauth2_provider: OAuth2 提供商 + applications: 应用 + home: 首页 application: - title: 'OAuth 认证' + title: 需要 OAuth 认证 \ No newline at end of file diff --git a/db/migrate/20220617103002_change_oauth_access_tokens_token_column_length.rb b/db/migrate/20220617103002_change_oauth_access_tokens_token_column_length.rb new file mode 100644 index 00000000..9c78ac28 --- /dev/null +++ b/db/migrate/20220617103002_change_oauth_access_tokens_token_column_length.rb @@ -0,0 +1,5 @@ +class ChangeOauthAccessTokensTokenColumnLength < ActiveRecord::Migration[5.2] + def change + change_column :oauth_access_tokens, :token, :string, limit: 500 + end +end From 45b7f70d9f391a9b77ed4734ffc9d6f710fbcdce Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 20 Jun 2022 10:16:39 +0800 Subject: [PATCH 070/208] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E9=82=80=E8=AF=B7=E9=93=BE=E6=8E=A5=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E9=99=90=E5=88=B6=EF=BC=8C=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?show=5Flink=E6=8E=A5=E5=8F=A3=E6=96=B0=E5=A2=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project_invite_links_controller.rb | 17 +- app/docs/slate/source/includes/_projects.md | 96 +++++- app/models/project_invite_link.rb | 8 +- .../_detail.json.jbuilder | 2 +- .../show_link.json.jbuilder | 1 + config/routes.rb | 1 + public/docs/api.html | 309 +++++++++++++++--- 7 files changed, 371 insertions(+), 63 deletions(-) create mode 100644 app/views/projects/project_invite_links/show_link.json.jbuilder diff --git a/app/controllers/projects/project_invite_links_controller.rb b/app/controllers/projects/project_invite_links_controller.rb index c177367c..14bdc959 100644 --- a/app/controllers/projects/project_invite_links_controller.rb +++ b/app/controllers/projects/project_invite_links_controller.rb @@ -1,24 +1,31 @@ class Projects::ProjectInviteLinksController < Projects::BaseController - before_action :require_manager!, except: [:redirect_link] + before_action :require_manager!, except: [:show_link, :redirect_link] before_action :require_login def current_link - @project_invite_link = ProjectInviteLink.find_by(user_id: current_user.id, project_id: @project.id) - @project_invite_link = ProjectInviteLink.build!(@project, current_user) unless @project_invite_link.present? + role = params[:role] + is_apply = params[:is_apply] + return render_error('请输入正确的参数!') unless role.present? && is_apply.present? + @project_invite_link = ProjectInviteLink.find_by(user_id: current_user.id, project_id: @project.id, role: role, is_apply: is_apply) + @project_invite_link = ProjectInviteLink.build!(@project, current_user, role, is_apply) unless @project_invite_link.present? end def generate_link ActiveRecord::Base.transaction do params_data = link_params.merge({user_id: current_user.id, project_id: @project.id}) - puts params_data Projects::ProjectInviteLinks::CreateForm.new(params_data).validate! - @project_invite_link = ProjectInviteLink.create!(params_data.merge(sign: ProjectInviteLink.random_hex_sign)) + @project_invite_link = ProjectInviteLink.build!(project, user, params_data[:role], params_data[:is_apply]) end rescue Exception => e uid_logger_error(e.message) tip_exception(e.message) end + def show_link + @project_invite_link = ProjectInviteLink.find_by(sign: params[:invite_sign]) + return render_not_found unless @project_invite_link.present? + end + def redirect_link Projects::LinkJoinService.call(current_user, @project, params[:invite_sign]) render_ok diff --git a/app/docs/slate/source/includes/_projects.md b/app/docs/slate/source/includes/_projects.md index 7e700457..906982b3 100644 --- a/app/docs/slate/source/includes/_projects.md +++ b/app/docs/slate/source/includes/_projects.md @@ -1,5 +1,5 @@ # Projects -## 获取项目邀请链接 +## 获取项目邀请链接(项目管理员) 当前登录(管理员)用户获取项目邀请链接的接口(第一次请求会默认生成role类型为developer和is_apply为true的链接) > 示例: @@ -15,6 +15,12 @@ await octokit.request('GET /api/yystopf/kellect/project_invite_links/current_lin ### HTTP 请求 `GET /api/:owner/:repo/project_invite_links/current_link.json` +### 请求参数 +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|role |是| |string |项目权限,reporter: 报告者, developer: 开发者,manager:管理员 | +|is_apply |是| |boolean |是否需要审核 | + ### 返回字段说明 参数 | 类型 | 字段说明 --------- | ----------- | ----------- @@ -22,6 +28,7 @@ await octokit.request('GET /api/yystopf/kellect/project_invite_links/current_lin |role |string |邀请角色| |is_apply |boolean |是否需要审核 | |sign |string |邀请标识(放在链接后面即可)| +|expired_at |string |链接过期时间| |user.id |int |链接创建者的id | |user.type |string |链接创建者的类型 | |user.name |string |链接创建者的名称 | @@ -46,6 +53,7 @@ await octokit.request('GET /api/yystopf/kellect/project_invite_links/current_lin "role": "developer", "is_apply": false, "sign": "6b6b454843c291d4e52e60853cb8ad9f", + "expired_at": "2022-06-23 10:08", "user": { "id": 2, "type": "User", @@ -69,7 +77,7 @@ await octokit.request('GET /api/yystopf/kellect/project_invite_links/current_lin } } ``` -## 生成项目邀请链接 +## 生成项目邀请链接(项目管理员) 当前登录(管理员)用户生成的项目邀请链接,可选role和is_apply参数 > 示例: @@ -108,6 +116,7 @@ await octokit.request('POST /api/yystopf/kellect/project_invite_links/generate_l |role |string |邀请角色| |is_apply |boolean |是否需要审核 | |sign |string |邀请标识(放在链接后面即可)| +|expired_at |string |链接过期时间| |user.id |int |链接创建者的id | |user.type |string |链接创建者的类型 | |user.name |string |链接创建者的名称 | @@ -132,6 +141,7 @@ await octokit.request('POST /api/yystopf/kellect/project_invite_links/generate_l "role": "developer", "is_apply": false, "sign": "6b6b454843c291d4e52e60853cb8ad9f", + "expired_at": "2022-06-23 10:08", "user": { "id": 2, "type": "User", @@ -156,7 +166,85 @@ await octokit.request('POST /api/yystopf/kellect/project_invite_links/generate_l } ``` -## 请求项目邀请链接 +## 获取邀请链接信息(被邀请用户) +用户请求邀请链接时,通过该接口来获取链接的信息 + +> 示例: + +```shell +curl -X GET http://localhost:3000/api/yystopf/kellect/project_invite_links/show_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6 +``` + +```javascript +await octokit.request('POST /api/yystopf/kellect/project_invite_links/show_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6') +``` + +### HTTP 请求 +`POST /api/:owner/:repo/project_invite_links/show_link.json?invite_sign=xxx` + +### 请求参数 +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|invite_sign |是| |string |项目邀请链接的标识 | + +### 返回字段说明 +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|id |int |链接id | +|role |string |邀请角色| +|is_apply |boolean |是否需要审核 | +|sign |string |邀请标识(放在链接后面即可)| +|expired_at |string |链接过期时间| +|user.id |int |链接创建者的id | +|user.type |string |链接创建者的类型 | +|user.name |string |链接创建者的名称 | +|user.login |string |链接创建者的标识 | +|user.image_url |string |链接创建者头像 | +|project.id |int |链接关联项目的id | +|project.identifier |string |链接关联项目的标识 | +|project.name |string |链接关联项目的名称 | +|project.description |string |链接关联项目的描述 | +|project.is_public |bool |链接关联项目是否公开 | +|project.owner.id |bool |链接关联项目拥有者id | +|project.owner.type |string |链接关联项目拥有者类型 | +|project.owner.name |string |链接关联项目拥有者昵称 | +|project.owner.login |string |链接关联项目拥有者标识 | +|project.owner.image_url|string |链接关联项目拥有者头像 | + +> 返回的JSON示例: + +```json +{ + "id": 7, + "role": "developer", + "is_apply": false, + "sign": "6b6b454843c291d4e52e60853cb8ad9f", + "expired_at": "2022-06-23 10:08", + "user": { + "id": 2, + "type": "User", + "name": "heh", + "login": "yystopf", + "image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png" + }, + "project": { + "id": 474, + "identifier": "kellect", + "name": "kellect", + "description": null, + "is_public": true, + "owner": { + "id": 2, + "type": "User", + "name": "heh", + "login": "yystopf", + "image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png" + } + } +} +``` + +## 接受项目邀请链接(被邀请用户) 当前登录(非项目)用户加入项目的接口,如果项目链接不需要审核,请求成功后即加入项目,如果需要审核,那么会提交一个申请,需要项目管理员审核 > 示例: @@ -170,7 +258,7 @@ await octokit.request('POST /api/yystopf/kellect/project_invite_links/redirect_l ``` ### HTTP 请求 -`POST /api/:owner/:repo/project_invite_links/generate_link.json?invite_sign=xxx` +`POST /api/:owner/:repo/project_invite_links/redirect_link.json?invite_sign=xxx` ### 请求参数 参数 | 必选 | 默认 | 类型 | 字段说明 diff --git a/app/models/project_invite_link.rb b/app/models/project_invite_link.rb index fa5c0c44..ea77b951 100644 --- a/app/models/project_invite_link.rb +++ b/app/models/project_invite_link.rb @@ -35,7 +35,8 @@ class ProjectInviteLink < ApplicationRecord before_create :set_old_data_expired_at def self.random_hex_sign - SecureRandom.hex + hex = (SecureRandom.hex(32)) + return hex unless ProjectInviteLink.where(sign: hex).exists? end def self.build!(project, user, role="developer", is_apply=true) @@ -44,13 +45,14 @@ class ProjectInviteLink < ApplicationRecord user_id: user&.id, role: role, is_apply: is_apply, - sign: random_hex_sign + sign: random_hex_sign, + expired_at: Time.now + 3.days ) end private def set_old_data_expired_at - ProjectInviteLink.where(user_id: self.user_id, project_id: self.project).update_all(expired_at: Time.now) + ProjectInviteLink.where(user_id: self.user_id, project_id: self.project, role: self.role, is_apply: self.is_apply).update_all(expired_at: Time.now) end diff --git a/app/views/projects/project_invite_links/_detail.json.jbuilder b/app/views/projects/project_invite_links/_detail.json.jbuilder index 7c9f78dd..c8e840d0 100644 --- a/app/views/projects/project_invite_links/_detail.json.jbuilder +++ b/app/views/projects/project_invite_links/_detail.json.jbuilder @@ -1,5 +1,5 @@ json.(project_invite_link, :id, :role, :is_apply, :sign) - +json.expired_at format_time(project_invite_link&.expired_at) json.user do json.partial! "/users/user_simple", locals: {user: project_invite_link.user} end diff --git a/app/views/projects/project_invite_links/show_link.json.jbuilder b/app/views/projects/project_invite_links/show_link.json.jbuilder new file mode 100644 index 00000000..1903e10a --- /dev/null +++ b/app/views/projects/project_invite_links/show_link.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'detail', locals: { project_invite_link: @project_invite_link } diff --git a/config/routes.rb b/config/routes.rb index fd89ee08..d3b2e7f3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -608,6 +608,7 @@ Rails.application.routes.draw do collection do get :current_link post :generate_link + get :show_link post :redirect_link end end diff --git a/public/docs/api.html b/public/docs/api.html index bd3f49eb..82b751e9 100644 --- a/public/docs/api.html +++ b/public/docs/api.html @@ -426,13 +426,16 @@ Projects
      参数
      + + + + + + + + + + + + + + + + + + + + + + +
      参数必选默认类型字段说明
      rolestring项目权限,reporter: 报告者, developer: 开发者,manager:管理员
      is_applyboolean是否需要审核

      返回字段说明

      @@ -4438,6 +4466,11 @@ Success — a happy kitten is an authenticated kitten! + + + + + @@ -4522,6 +4555,7 @@ Success — a happy kitten is an authenticated kitten! "role": "developer", "is_apply": false, "sign": "6b6b454843c291d4e52e60853cb8ad9f", + "expired_at": "2022-06-23 10:08", "user": { "id": 2, "type": "User", @@ -4544,7 +4578,7 @@ Success — a happy kitten is an authenticated kitten! } } } -

      生成项目邀请链接

      +

      生成项目邀请链接(项目管理员)

      当前登录(管理员)用户生成的项目邀请链接,可选role和is_apply参数

      @@ -4554,7 +4588,7 @@ Success — a happy kitten is an authenticated kitten!
      await octokit.request('POST /api/yystopf/kellect/project_invite_links/generate_link.json')
       

      HTTP 请求

      POST /api/:owner/:repo/project_invite_links/generate_link.json

      -

      请求参数

      +

      请求参数

      邀请标识(放在链接后面即可)
      expired_atstring链接过期时间
      user.id int 链接创建者的id
      @@ -4616,6 +4650,11 @@ Success — a happy kitten is an authenticated kitten! + + + + + @@ -4700,6 +4739,7 @@ Success — a happy kitten is an authenticated kitten! "role": "developer", "is_apply": false, "sign": "6b6b454843c291d4e52e60853cb8ad9f", + "expired_at": "2022-06-23 10:08", "user": { "id": 2, "type": "User", @@ -4722,7 +4762,176 @@ Success — a happy kitten is an authenticated kitten! } } } -

      请求项目邀请链接

      +

      获取邀请链接信息(被邀请用户)

      +

      用户请求邀请链接时,通过该接口来获取链接的信息

      + +
      +

      示例:

      +
      +
      curl -X GET http://localhost:3000/api/yystopf/kellect/project_invite_links/show_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6
      +
      await octokit.request('POST /api/yystopf/kellect/project_invite_links/show_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6')
      +

      HTTP 请求

      +

      POST /api/:owner/:repo/project_invite_links/show_link.json?invite_sign=xxx

      +

      请求参数

      +
      参数邀请标识(放在链接后面即可)
      expired_atstring链接过期时间
      user.id int 链接创建者的id
      + + + + + + + + + + + + + + + +
      参数必选默认类型字段说明
      invite_signstring项目邀请链接的标识
      +

      返回字段说明

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      参数类型字段说明
      idint链接id
      rolestring邀请角色
      is_applyboolean是否需要审核
      signstring邀请标识(放在链接后面即可)
      expired_atstring链接过期时间
      user.idint链接创建者的id
      user.typestring链接创建者的类型
      user.namestring链接创建者的名称
      user.loginstring链接创建者的标识
      user.image_urlstring链接创建者头像
      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链接关联项目拥有者头像
      + +
      +

      返回的JSON示例:

      +
      +
      {
      +    "id": 7,
      +    "role": "developer",
      +    "is_apply": false,
      +    "sign": "6b6b454843c291d4e52e60853cb8ad9f",
      +    "expired_at": "2022-06-23 10:08",
      +    "user": {
      +        "id": 2,
      +        "type": "User",
      +        "name": "heh",
      +        "login": "yystopf",
      +        "image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
      +    },
      +    "project": {
      +        "id": 474,
      +        "identifier": "kellect",
      +        "name": "kellect",
      +        "description": null,
      +        "is_public": true,
      +        "owner": {
      +            "id": 2,
      +            "type": "User",
      +            "name": "heh",
      +            "login": "yystopf",
      +            "image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
      +        }
      +    }
      +}
      +

      接受项目邀请链接(被邀请用户)

      当前登录(非项目)用户加入项目的接口,如果项目链接不需要审核,请求成功后即加入项目,如果需要审核,那么会提交一个申请,需要项目管理员审核

      @@ -4730,9 +4939,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X POST http://localhost:3000/api/yystopf/kellect/project_invite_links/redirect_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6
       
      await octokit.request('POST /api/yystopf/kellect/project_invite_links/redirect_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6')
      -

      HTTP 请求

      -

      POST /api/:owner/:repo/project_invite_links/generate_link.json?invite_sign=xxx

      -

      请求参数

      +

      HTTP 请求

      +

      POST /api/:owner/:repo/project_invite_links/redirect_link.json?invite_sign=xxx

      +

      请求参数

      @@ -4766,9 +4975,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X POST http://localhost:3000/api/applied_projects.json
       
      await octokit.request('POST /api/appliedr_projects.json')
      -

      HTTP 请求

      +

      HTTP 请求

      POST /api/applied_projects.json

      -

      请求参数

      +

      请求参数

      参数
      @@ -4803,7 +5012,7 @@ Success — a happy kitten is an authenticated kitten! "role": "developer" } } -

      返回字段说明

      +

      返回字段说明

      参数
      @@ -4944,9 +5153,9 @@ Success — a happy kitten is an authenticated kitten! -d"limit=5"\ http://localhost:3000/api/projects | jq
      await octokit.request('GET /api/projects')
      -

      HTTP 请求

      +

      HTTP 请求

      GET api/projects

      -

      请求参数

      +

      请求参数

      参数
      @@ -5013,7 +5222,7 @@ http://localhost:3000/api/projects | jq
      参数项目类型, 取值为:common、mirror; common:开源托管项目, mirror:开源镜像项目
      -

      返回字段说明

      +

      返回字段说明

      @@ -5165,9 +5374,9 @@ Remember — a happy kitten is an authenticated kitten!
      curl -X GET \
       http://localhost:3000/api/projects/recommend  | jq
       
      await octokit.request('GET /api/projects/recommend.json')
      -

      HTTP 请求

      +

      HTTP 请求

      GET api/projects/recommend

      -

      返回字段说明

      +

      返回字段说明

      参数
      @@ -5301,9 +5510,9 @@ Remember — a happy kitten is an authenticated kitten!
      curl -X GET \
       http://localhost:3000/api/yystopf/ceshi/menu_list  | jq
       
      await octokit.request('GET /api/yystopf/ceshi/menu_list')
      -

      HTTP 请求

      +

      HTTP 请求

      GET api/:owner/:repo/menu_list

      -

      请求参数

      +

      请求参数

      参数
      @@ -5328,7 +5537,7 @@ http://localhost:3000/api/yystopf/ceshi/menu_list | jq
      参数项目标识identifier
      -

      返回字段说明

      +

      返回字段说明

      @@ -5369,9 +5578,9 @@ http://localhost:3000/api/yystopf/ceshi/menu_list | jq
      curl -X GET \
       http://localhost:3000/api/jasder/forgeplus/about  | jq
       
      await octokit.request('GET /api/jasder/forgeplus/about')
      -

      HTTP 请求

      +

      HTTP 请求

      GET api/:owner/:repo/about

      -

      请求参数

      +

      请求参数

      参数
      @@ -5396,7 +5605,7 @@ http://localhost:3000/api/jasder/forgeplus/about | jq
      参数项目标识identifier
      -

      返回字段说明

      +

      返回字段说明

      @@ -5442,7 +5651,7 @@ Remember — a happy kitten is an authenticated kitten!
      curl -X GET \
       http://localhost:3000/api/yystopf/ceshi/project_units.json
       
      await octokit.request('GET /api/yystopf/ceshi/project_units')
      -

      HTTP 请求

      +

      HTTP 请求

      GET /api/yystopf/ceshi/project_units

      返回字段说明:

      参数
      @@ -5485,9 +5694,9 @@ http://localhost:3000/api/yystopf/ceshi/project_units.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 请求

      +

      HTTP 请求

      POST /api/yystopf/ceshi/project_units

      -

      请求参数

      +

      请求参数

      @@ -5549,9 +5758,9 @@ http://localhost:3000/api/yystopf/ceshi/project_units.json -d"license_id=1"\ http://localhost:3000/api/projects.json
      await octokit.request('GET /api/projects.json')
      -

      HTTP 请求

      +

      HTTP 请求

      POST api/projects

      -

      请求参数

      +

      请求参数

      参数
      @@ -5625,7 +5834,7 @@ http://localhost:3000/api/projects.json
      参数项目是否私有, true:为私有,false: 公开,默认为公开
      -

      返回字段说明

      +

      返回字段说明

      @@ -5667,9 +5876,9 @@ http://localhost:3000/api/projects.json -d"project_language_id=2"\ http://localhost:3000/api/projects/migrate.json
      await octokit.request('GET /api/projects/migrate.json')
      -

      HTTP 请求

      +

      HTTP 请求

      POST api/projects/migrate.json

      -

      请求参数

      +

      请求参数

      参数
      @@ -5757,7 +5966,7 @@ http://localhost:3000/api/projects/migrate.json
      参数项目是否私有, true:为私有,false: 非私有,默认为公开
      -

      返回字段说明

      +

      返回字段说明

      @@ -5792,9 +6001,9 @@ http://localhost:3000/api/projects/migrate.json
      curl -X POST http://localhost:3000/api/repositories/1244/sync_mirror.json
       
      await octokit.request('POST /api/repositories/1244/sync_mirror.json')
      -

      HTTP 请求

      +

      HTTP 请求

      POST api/repositories/:id/sync_mirror.json

      -

      请求参数

      +

      请求参数

      参数
      @@ -5812,7 +6021,7 @@ http://localhost:3000/api/projects/migrate.json
      参数仓库id
      -

      返回字段说明

      +

      返回字段说明

      @@ -5847,9 +6056,9 @@ http://localhost:3000/api/projects/migrate.json
      curl -X POST http://localhost:3000/api/jasder/forgeplus/forks.json
       
      await octokit.request('POST /api/jaser/jasder_test/forks.json')
      -

      HTTP 请求

      +

      HTTP 请求

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

      -

      请求参数

      +

      请求参数

      参数
      @@ -5874,7 +6083,7 @@ http://localhost:3000/api/projects/migrate.json
      参数项目标识identifier
      -

      返回字段说明

      +

      返回字段说明

      @@ -5910,9 +6119,9 @@ http://localhost:3000/api/projects/migrate.json
      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 请求

      +

      HTTP 请求

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

      -

      请求参数

      +

      请求参数

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

      返回字段说明

      +

      返回字段说明

      @@ -6004,9 +6213,9 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat
      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 请求

      +

      HTTP 请求

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

      -

      请求参数

      +

      请求参数

      参数
      @@ -6038,7 +6247,7 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat
      参数迁移对象标识
      -

      返回字段说明

      +

      返回字段说明

      @@ -6208,9 +6417,9 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat
      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 请求

      +

      HTTP 请求

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

      -

      请求参数

      +

      请求参数

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

      返回字段说明

      +

      返回字段说明

      @@ -6405,9 +6614,9 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat
      curl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/quit.json
       
      await octokit.request('POST /api/:owner/:repo/quit.json')
      -

      HTTP 请求

      +

      HTTP 请求

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

      -

      请求参数

      +

      请求参数

      参数
      From bed24690145120790e0ce7aeadd7db01a36be36a Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 20 Jun 2022 10:34:25 +0800 Subject: [PATCH 071/208] fix: change error --- Gemfile.lock | 2 +- ...0617103002_change_oauth_access_tokens_token_column_length.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 99f40ad9..e27c504a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -106,7 +106,7 @@ GEM activerecord (>= 3.1.0, < 7) diff-lcs (1.3) diffy (3.3.0) - doorkeeper (5.5.4) + doorkeeper (5.5.1) railties (>= 5) doorkeeper-jwt (0.4.1) jwt (>= 2.1) diff --git a/db/migrate/20220617103002_change_oauth_access_tokens_token_column_length.rb b/db/migrate/20220617103002_change_oauth_access_tokens_token_column_length.rb index 9c78ac28..f387e6ea 100644 --- a/db/migrate/20220617103002_change_oauth_access_tokens_token_column_length.rb +++ b/db/migrate/20220617103002_change_oauth_access_tokens_token_column_length.rb @@ -1,5 +1,5 @@ class ChangeOauthAccessTokensTokenColumnLength < ActiveRecord::Migration[5.2] def change - change_column :oauth_access_tokens, :token, :string, limit: 500 + change_column :oauth_access_tokens, :token, :text end end From 531495429ce7ad95b91d6f758de1c133dd7aa269 Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 20 Jun 2022 13:05:25 +0800 Subject: [PATCH 072/208] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E6=AD=A3=E5=B8=B8=E6=9B=B4=E6=94=B9token=E9=95=BF?= =?UTF-8?q?=E5=BA=A6=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/migrate/20220616040105_create_doorkeeper_tables.rb | 6 +++--- ...103002_change_oauth_access_tokens_token_column_length.rb | 5 ----- 2 files changed, 3 insertions(+), 8 deletions(-) delete mode 100644 db/migrate/20220617103002_change_oauth_access_tokens_token_column_length.rb diff --git a/db/migrate/20220616040105_create_doorkeeper_tables.rb b/db/migrate/20220616040105_create_doorkeeper_tables.rb index b4ec636e..890b31d8 100644 --- a/db/migrate/20220616040105_create_doorkeeper_tables.rb +++ b/db/migrate/20220616040105_create_doorkeeper_tables.rb @@ -48,8 +48,8 @@ class CreateDoorkeeperTables < ActiveRecord::Migration[5.2] # characters. More info on custom token generators in: # https://github.com/doorkeeper-gem/doorkeeper/tree/v3.0.0.rc1#custom-access-token-generator # - # t.text :token, null: false - t.string :token, null: false + t.text :token, null: false + # t.string :token, null: false t.string :refresh_token t.integer :expires_in @@ -73,7 +73,7 @@ class CreateDoorkeeperTables < ActiveRecord::Migration[5.2] t.string :previous_refresh_token, null: false, default: "" end - add_index :oauth_access_tokens, :token, unique: true + # add_index :oauth_access_tokens, :token, unique: true add_index :oauth_access_tokens, :refresh_token, unique: true add_foreign_key( :oauth_access_tokens, diff --git a/db/migrate/20220617103002_change_oauth_access_tokens_token_column_length.rb b/db/migrate/20220617103002_change_oauth_access_tokens_token_column_length.rb deleted file mode 100644 index f387e6ea..00000000 --- a/db/migrate/20220617103002_change_oauth_access_tokens_token_column_length.rb +++ /dev/null @@ -1,5 +0,0 @@ -class ChangeOauthAccessTokensTokenColumnLength < ActiveRecord::Migration[5.2] - def change - change_column :oauth_access_tokens, :token, :text - end -end From 9fe8df3c1ba90a20351dd0ffd70319e5352a879d Mon Sep 17 00:00:00 2001 From: xiaoxiaoqiong Date: Mon, 20 Jun 2022 15:38:42 +0800 Subject: [PATCH 073/208] =?UTF-8?q?auth=20token=E8=BF=87=E6=9C=9F=E6=97=B6?= =?UTF-8?q?=E9=97=B47=E5=A4=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/initializers/doorkeeper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index 4f9db8cd..d80dcac6 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -101,12 +101,12 @@ Doorkeeper.configure do # Authorization Code expiration time (default: 10 minutes). # - # authorization_code_expires_in 10.minutes + authorization_code_expires_in 7.days # Access token expiration time (default: 2 hours). # If you want to disable expiration, set this to `nil`. # - # access_token_expires_in 2.hours + access_token_expires_in 7.days # Assign custom TTL for access tokens. Will be used instead of access_token_expires_in # option if defined. In case the block returns `nil` value Doorkeeper fallbacks to From 29e3a55a0ca2fe613cb3cf3154cbffc266a66378 Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 20 Jun 2022 16:36:01 +0800 Subject: [PATCH 074/208] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E9=82=80=E8=AF=B7=E9=93=BE=E6=8E=A5=E7=A7=81=E6=9C=89?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E6=97=A0=E6=B3=95=E8=AE=BF=E9=97=AE=E7=9A=84?= =?UTF-8?q?=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/application_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2ccd10ff..952fa448 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -688,7 +688,7 @@ class ApplicationController < ActionController::Base @project, @owner = Project.find_with_namespace(namespace, id) - if @project and current_user.can_read_project?(@project) + if @project and current_user.can_read_project?(@project) and controller_path == "projects/project_invite_links" logger.info "###########: has project and can read project" @project # elsif @project && current_user.is_a?(AnonymousUser) From ba1fa8b685e29300d0b91e7a2756709231570498 Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 20 Jun 2022 16:37:59 +0800 Subject: [PATCH 075/208] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E9=82=80=E8=AF=B7=E9=93=BE=E6=8E=A5=E7=A7=81=E6=9C=89?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E6=97=A0=E6=B3=95=E8=AE=BF=E9=97=AE=E7=9A=84?= =?UTF-8?q?=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/application_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 952fa448..f71a7501 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -688,7 +688,7 @@ class ApplicationController < ActionController::Base @project, @owner = Project.find_with_namespace(namespace, id) - if @project and current_user.can_read_project?(@project) and controller_path == "projects/project_invite_links" + if @project and (current_user.can_read_project?(@project) || controller_path == "projects/project_invite_links") logger.info "###########: has project and can read project" @project # elsif @project && current_user.is_a?(AnonymousUser) From 9221e55752d02321cc6ee3b90b760cb44bc8d090 Mon Sep 17 00:00:00 2001 From: yystopf Date: Tue, 21 Jun 2022 11:18:14 +0800 Subject: [PATCH 076/208] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E9=82=80?= =?UTF-8?q?=E8=AF=B7=E9=93=BE=E6=8E=A5=E8=B7=B3=E8=BD=AC=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=B7=B2=E5=8A=A0=E5=85=A5=E4=BB=93=E5=BA=93=E7=9A=84=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E4=BB=A5=E5=8F=8A=E9=A1=B9=E7=9B=AE=E7=94=B3=E8=AF=B7?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E5=8F=AA=E5=88=9B=E5=BB=BA=E4=B8=80=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/projects/project_invite_links_controller.rb | 2 +- app/services/projects/link_join_service.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/projects/project_invite_links_controller.rb b/app/controllers/projects/project_invite_links_controller.rb index 14bdc959..ddf88e6b 100644 --- a/app/controllers/projects/project_invite_links_controller.rb +++ b/app/controllers/projects/project_invite_links_controller.rb @@ -31,7 +31,7 @@ class Projects::ProjectInviteLinksController < Projects::BaseController render_ok rescue Exception => e uid_logger_error(e.message) - tip_exception(e.message) + normal_status(-1, e.message) end diff --git a/app/services/projects/link_join_service.rb b/app/services/projects/link_join_service.rb index e586933f..682fd141 100644 --- a/app/services/projects/link_join_service.rb +++ b/app/services/projects/link_join_service.rb @@ -28,11 +28,11 @@ class Projects::LinkJoinService < ApplicationService raise Error, 'invite_sign必须存在!' if invite_sign.blank? raise Error, '邀请链接不存在!' unless invite_link.present? raise Error, '邀请链接已失效!' unless invite_user_in_project - raise Error, '用户已加入该项目!' if project.member?(user.id) + raise Error, '您已是仓库成员' if project.member?(user.id) end def create_applied_project! - user.applied_projects.create!(project: project, role: role_value, project_invite_link_id: invite_link&.id) + user.applied_projects.find_or_create_by!(status: 'common', project: project, role: role_value, project_invite_link_id: invite_link&.id) end def create_member! From 3c2c742121af00587d39dc77732e1e1fcd19efb1 Mon Sep 17 00:00:00 2001 From: yystopf Date: Tue, 21 Jun 2022 11:37:41 +0800 Subject: [PATCH 077/208] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E9=82=80=E8=AF=B7=E9=93=BE=E6=8E=A5=E7=94=9F=E6=88=90?= =?UTF-8?q?=E5=A4=9A=E6=AC=A1=E8=BF=94=E5=9B=9E=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/services/projects/link_join_service.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/services/projects/link_join_service.rb b/app/services/projects/link_join_service.rb index 682fd141..70e23d14 100644 --- a/app/services/projects/link_join_service.rb +++ b/app/services/projects/link_join_service.rb @@ -29,6 +29,16 @@ class Projects::LinkJoinService < ApplicationService raise Error, '邀请链接不存在!' unless invite_link.present? raise Error, '邀请链接已失效!' unless invite_user_in_project raise Error, '您已是仓库成员' if project.member?(user.id) + raise Error, '您的申请管理员正在审核中,请勿重复申请!' if user.applied_projects.exists?(applied_project_params) + end + + def applied_project_params + { + status: 'common', + project: project, + role: role_value, + project_invite_link_id: invite_link&.id + } end def create_applied_project! From aefd6c08dc76d9214709205544af2fc25facb93c Mon Sep 17 00:00:00 2001 From: yystopf Date: Tue, 21 Jun 2022 17:39:11 +0800 Subject: [PATCH 078/208] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20gitea-client?= =?UTF-8?q?=E5=BC=95=E5=85=A5=E4=BB=A5=E5=8F=8A=E8=B7=AF=E7=94=B1=E5=AE=9A?= =?UTF-8?q?=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile | 2 ++ Gemfile.lock | 20 ++++++++++++++++++++ app/controllers/api/v1/base_controller.rb | 2 ++ app/controllers/api/v1/repos_controller.rb | 6 ++++++ config/initializers/gitea_client.rb | 11 +++++++++++ config/routes.rb | 6 ++++++ config/routes/api.rb | 7 +++++++ 7 files changed, 54 insertions(+) create mode 100644 app/controllers/api/v1/base_controller.rb create mode 100644 app/controllers/api/v1/repos_controller.rb create mode 100644 config/initializers/gitea_client.rb create mode 100644 config/routes/api.rb diff --git a/Gemfile b/Gemfile index effb2be6..0347de45 100644 --- a/Gemfile +++ b/Gemfile @@ -134,3 +134,5 @@ gem 'jwt' gem 'doorkeeper' gem 'doorkeeper-jwt' + +gem 'gitea-client', '~> 0.5.1' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index e27c504a..6e4f079f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -106,6 +106,8 @@ GEM activerecord (>= 3.1.0, < 7) diff-lcs (1.3) diffy (3.3.0) + domain_name (0.5.20190701) + unf (>= 0.0.5, < 1.0.0) doorkeeper (5.5.1) railties (>= 5) doorkeeper-jwt (0.4.1) @@ -133,6 +135,8 @@ GEM fugit (1.4.1) et-orbi (~> 1.1, >= 1.1.8) raabro (~> 1.4) + gitea-client (0.5.1) + rest-client (~> 2.1.0) globalid (0.4.2) activesupport (>= 4.2.0) grape-entity (0.7.1) @@ -143,6 +147,9 @@ GEM harmonious_dictionary (0.0.1) hashie (3.6.0) htmlentities (4.3.4) + http-accept (1.7.0) + http-cookie (1.0.5) + domain_name (~> 0.5) i18n (1.8.2) concurrent-ruby (~> 1.0) io-like (0.3.1) @@ -180,6 +187,9 @@ GEM mimemagic (~> 0.3.2) maruku (0.7.3) method_source (0.9.2) + mime-types (3.4.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2022.0105) mimemagic (0.3.10) nokogiri (~> 1) rake @@ -193,6 +203,7 @@ GEM mustermann (1.1.1) ruby2_keywords (~> 0.0.1) mysql2 (0.5.3) + netrc (0.11.0) nio4r (2.5.2) nokogiri (1.10.8) mini_portile2 (~> 2.4.0) @@ -292,6 +303,11 @@ GEM regexp_parser (1.7.0) request_store (1.5.0) rack (>= 1.4) + rest-client (2.1.0) + http-accept (>= 1.7.0, < 2.0) + http-cookie (>= 1.0.2, < 2.0) + mime-types (>= 1.16, < 4.0) + netrc (~> 0.8) reverse_markdown (1.4.0) nokogiri roo (2.8.3) @@ -418,6 +434,9 @@ GEM thread_safe (~> 0.1) uglifier (4.2.0) execjs (>= 0.3.0, < 3) + unf (0.1.4) + unf_ext + unf_ext (0.0.8.2) unicode-display_width (1.6.1) web-console (3.7.0) actionview (>= 5.0) @@ -459,6 +478,7 @@ DEPENDENCIES enumerize faraday (~> 0.15.4) font-awesome-sass (= 4.7.0) + gitea-client (~> 0.5.1) grape-entity (~> 0.7.1) groupdate (~> 4.1.0) harmonious_dictionary (~> 0.0.1) diff --git a/app/controllers/api/v1/base_controller.rb b/app/controllers/api/v1/base_controller.rb new file mode 100644 index 00000000..76c527d8 --- /dev/null +++ b/app/controllers/api/v1/base_controller.rb @@ -0,0 +1,2 @@ +class Api::V1::BaseController < ApplicationController +end \ No newline at end of file diff --git a/app/controllers/api/v1/repos_controller.rb b/app/controllers/api/v1/repos_controller.rb new file mode 100644 index 00000000..667f47db --- /dev/null +++ b/app/controllers/api/v1/repos_controller.rb @@ -0,0 +1,6 @@ +class Api::V1::ReposController < Api::V1::BaseController + + def index + render_ok + end +end \ No newline at end of file diff --git a/config/initializers/gitea_client.rb b/config/initializers/gitea_client.rb new file mode 100644 index 00000000..92706ffd --- /dev/null +++ b/config/initializers/gitea_client.rb @@ -0,0 +1,11 @@ +require 'gitea-client' + +config = Rails.application.config_for(:configuration).symbolize_keys! +gitea_config = config[:gitea].symbolize_keys! + +$gitea_client = Gitea::Api::Client.new({ + domain: gitea_config[:domain], + base_url: gitea_config[:base_url], + username: gitea_config[:username], + password: gitea_config[:password] +}) \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index d3b2e7f3..c0e845a8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,11 @@ Rails.application.routes.draw do + def draw(routes_name) + instance_eval(File.read(Rails.root.join("config/routes/#{routes_name}.rb"))) + end + + draw :api + use_doorkeeper require 'sidekiq/web' require 'sidekiq/cron/web' diff --git a/config/routes/api.rb b/config/routes/api.rb new file mode 100644 index 00000000..718f705a --- /dev/null +++ b/config/routes/api.rb @@ -0,0 +1,7 @@ +defaults format: :json do + namespace :api do + namespace :v1 do + resources :repos + end + end +end \ No newline at end of file From d7dd3901b8f60f76283d00c7f8d9a18bf0e776d0 Mon Sep 17 00:00:00 2001 From: yystopf Date: Wed, 22 Jun 2022 19:08:14 +0800 Subject: [PATCH 079/208] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E8=AF=A6=E6=83=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile | 2 +- Gemfile.lock | 4 +- app/controllers/api/v1/base_controller.rb | 4 ++ app/controllers/api/v1/projects_controller.rb | 11 +++++ app/controllers/api/v1/repos_controller.rb | 6 --- .../concerns/api/project_helper.rb | 20 ++++++++ app/services/api/v1/projects/get_service.rb | 48 +++++++++++++++++++ .../v1/projects/_simple_detail.json.jbuilder | 10 ++++ app/views/api/v1/projects/show.json.jbuilder | 5 ++ .../api/v1/users/_simple_user.json.jbuilder | 9 ++++ config/routes/api.rb | 16 ++++++- 11 files changed, 125 insertions(+), 10 deletions(-) create mode 100644 app/controllers/api/v1/projects_controller.rb delete mode 100644 app/controllers/api/v1/repos_controller.rb create mode 100644 app/controllers/concerns/api/project_helper.rb create mode 100644 app/services/api/v1/projects/get_service.rb create mode 100644 app/views/api/v1/projects/_simple_detail.json.jbuilder create mode 100644 app/views/api/v1/projects/show.json.jbuilder create mode 100644 app/views/api/v1/users/_simple_user.json.jbuilder diff --git a/Gemfile b/Gemfile index 0347de45..1542856c 100644 --- a/Gemfile +++ b/Gemfile @@ -135,4 +135,4 @@ gem 'doorkeeper' gem 'doorkeeper-jwt' -gem 'gitea-client', '~> 0.5.1' \ No newline at end of file +gem 'gitea-client', '~> 0.6.2' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index 6e4f079f..467e19a2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -135,7 +135,7 @@ GEM fugit (1.4.1) et-orbi (~> 1.1, >= 1.1.8) raabro (~> 1.4) - gitea-client (0.5.1) + gitea-client (0.6.2) rest-client (~> 2.1.0) globalid (0.4.2) activesupport (>= 4.2.0) @@ -478,7 +478,7 @@ DEPENDENCIES enumerize faraday (~> 0.15.4) font-awesome-sass (= 4.7.0) - gitea-client (~> 0.5.1) + gitea-client (~> 0.6.2) grape-entity (~> 0.7.1) groupdate (~> 4.1.0) harmonious_dictionary (~> 0.0.1) diff --git a/app/controllers/api/v1/base_controller.rb b/app/controllers/api/v1/base_controller.rb index 76c527d8..2202df8d 100644 --- a/app/controllers/api/v1/base_controller.rb +++ b/app/controllers/api/v1/base_controller.rb @@ -1,2 +1,6 @@ class Api::V1::BaseController < ApplicationController + + include Api::ProjectHelper + + skip_before_action :user_setup end \ No newline at end of file diff --git a/app/controllers/api/v1/projects_controller.rb b/app/controllers/api/v1/projects_controller.rb new file mode 100644 index 00000000..05a3750c --- /dev/null +++ b/app/controllers/api/v1/projects_controller.rb @@ -0,0 +1,11 @@ +class Api::V1::ProjectsController < Api::V1::BaseController + before_action :load_project, only: [:show] + + def index + render_ok + end + + def show + @result_object = Api::V1::Projects::GetService.call(@project, current_user.gitea_token) + end +end \ No newline at end of file diff --git a/app/controllers/api/v1/repos_controller.rb b/app/controllers/api/v1/repos_controller.rb deleted file mode 100644 index 667f47db..00000000 --- a/app/controllers/api/v1/repos_controller.rb +++ /dev/null @@ -1,6 +0,0 @@ -class Api::V1::ReposController < Api::V1::BaseController - - def index - render_ok - end -end \ No newline at end of file diff --git a/app/controllers/concerns/api/project_helper.rb b/app/controllers/concerns/api/project_helper.rb new file mode 100644 index 00000000..0b444c48 --- /dev/null +++ b/app/controllers/concerns/api/project_helper.rb @@ -0,0 +1,20 @@ +module Api::ProjectHelper + extend ActiveSupport::Concern + + def load_project + namespace = params[:owner] + repo = params[:repo] + + @project, @owner = Project.find_with_namespace(namespace, repo) + + if @project + logger.info "###########:project not founded" + @project + else + logger.info "###########:project not found" + @project = nil + render_not_found and return + end + @project + end +end \ No newline at end of file diff --git a/app/services/api/v1/projects/get_service.rb b/app/services/api/v1/projects/get_service.rb new file mode 100644 index 00000000..1244938c --- /dev/null +++ b/app/services/api/v1/projects/get_service.rb @@ -0,0 +1,48 @@ +class Api::V1::Projects::GetService < ApplicationService + + attr_reader :project, :token, :owner, :repo + attr_accessor :gitea_data, :gitea_branch_tag_count + + def initialize(project, token=nil) + @project = project + @owner = project&.owner.login + @repo = project&.identifier + @token = token + end + + def call + $gitea_client.token = token unless token.blank? + load_gitea_data + load_gitea_branch_tag_count + + $gitea_client.token = nil unless token.blank? + + result_object + rescue + raise Error, "服务器错误,请联系系统管理员!" + end + + private + + def result_object + { + full_name: "#{owner}/#{repo}", + owner: project&.owner, + ssh_url: gitea_data["ssh_url"], + clone_url: gitea_data["clone_url"], + size: gitea_data["size"], + default_branch: gitea_data["default_branch"], + empty: gitea_data["empty"], + branch_count: gitea_branch_tag_count["branch_count"], + tag_count: gitea_branch_tag_count["tag_count"], + } + end + + def load_gitea_data + @gitea_data = $gitea_client.get_repos_by_owner_repo(owner, repo) + end + + def load_gitea_branch_tag_count + @gitea_branch_tag_count = $gitea_client.get_repos_branch_tag_count_by_owner_repo(owner, repo) + end +end \ No newline at end of file diff --git a/app/views/api/v1/projects/_simple_detail.json.jbuilder b/app/views/api/v1/projects/_simple_detail.json.jbuilder new file mode 100644 index 00000000..f5f6b1db --- /dev/null +++ b/app/views/api/v1/projects/_simple_detail.json.jbuilder @@ -0,0 +1,10 @@ +if project.present? + json.type @project.project_type + json.(project, + :description, :forked_count, :forked_from_project_id, :identifier, + :issues_count, :pull_requests_count, :invite_code, :website, :platform, + :name, :open_devops, :praises_count, :is_public, :status, :watchers_count, + :ignore_id, :license_id, :project_category_id, :project_language_id) +else + json.nil! +end \ No newline at end of file diff --git a/app/views/api/v1/projects/show.json.jbuilder b/app/views/api/v1/projects/show.json.jbuilder new file mode 100644 index 00000000..40862365 --- /dev/null +++ b/app/views/api/v1/projects/show.json.jbuilder @@ -0,0 +1,5 @@ +json.owner do + json.partial! "api/v1/users/simple_user", user: @result_object[:owner] +end +json.(@result_object, :full_name, :ssh_url, :clone_url, :default_branch, :empty, :branch_count, :tag_count) +json.partial! "api/v1/projects/simple_detail", project: @project \ No newline at end of file diff --git a/app/views/api/v1/users/_simple_user.json.jbuilder b/app/views/api/v1/users/_simple_user.json.jbuilder new file mode 100644 index 00000000..ef45bcd9 --- /dev/null +++ b/app/views/api/v1/users/_simple_user.json.jbuilder @@ -0,0 +1,9 @@ +if user.present? + json.id user.id + json.type user.type + json.name user.real_name + json.login user.login + json.image_url url_to_avatar(user) +else + json.nil! +end \ No newline at end of file diff --git a/config/routes/api.rb b/config/routes/api.rb index 718f705a..cf55cfcf 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -1,7 +1,21 @@ defaults format: :json do namespace :api do namespace :v1 do - resources :repos + resources :projects, only: [:index] + # 项目相关的api + scope ':owner/:repo' do + # projects + resource :projects, path: '/', only: [:show, :update, :edit, :destroy] + + # projects文件夹下的 + scope module: :projects do + resources :issues + resources :pull_requests + resources :versions + resources :release_versions + end + end + end end end \ No newline at end of file From d14bf35ca1cdca2b32d1448dd287066662b73a36 Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 23 Jun 2022 11:33:11 +0800 Subject: [PATCH 080/208] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20gitea=20module?= =?UTF-8?q?=E5=91=BD=E5=90=8D=E9=87=8D=E5=A4=8D=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/attachments_controller.rb | 4 ++-- app/controllers/concerns/acceleratorable.rb | 6 +++--- app/controllers/repositories_controller.rb | 12 ++++++------ app/helpers/projects_helper.rb | 2 +- app/interactors/gitea/register_interactor.rb | 4 ++-- app/libs/ci/drone/server.rb | 2 +- app/libs/{gitea.rb => gitea_service.rb} | 2 +- app/services/educoder/client_service.rb | 2 +- app/services/gitea/accelerator/base_service.rb | 2 +- app/services/gitea/client_service.rb | 4 ++-- app/services/gitea/user/delete_service.rb | 4 ++-- app/services/gitea/user/update_service.rb | 2 +- app/services/repositories/create_service.rb | 2 +- lib/tasks/sync_data_to_gitea.rake | 2 +- 14 files changed, 25 insertions(+), 25 deletions(-) rename app/libs/{gitea.rb => gitea_service.rb} (96%) diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb index 3aa98257..79047b51 100644 --- a/app/controllers/attachments_controller.rb +++ b/app/controllers/attachments_controller.rb @@ -33,8 +33,8 @@ class AttachmentsController < ApplicationController normal_status(-1, "参数缺失") if params[:download_url].blank? url = URI.encode(params[:download_url].to_s.gsub("http:", "https:")) if url.starts_with?(base_url) - domain = Gitea.gitea_config[:domain] - api_url = Gitea.gitea_config[:base_url] + domain = GiteaService.gitea_config[:domain] + api_url = GiteaService.gitea_config[:base_url] url = url.split(base_url)[1].gsub("api", "repos").gsub('?filepath=', '/').gsub('&', '?') request_url = [domain, api_url, url, "?ref=#{params[:ref]}&access_token=#{current_user&.gitea_token}"].join response = Faraday.get(request_url) diff --git a/app/controllers/concerns/acceleratorable.rb b/app/controllers/concerns/acceleratorable.rb index 5243ac53..88ec4dd6 100644 --- a/app/controllers/concerns/acceleratorable.rb +++ b/app/controllers/concerns/acceleratorable.rb @@ -18,15 +18,15 @@ module Acceleratorable end def accelerator_domain - Gitea.gitea_config[:accelerator]["domain"] + GiteaService.gitea_config[:accelerator]["domain"] end def accelerator_username - Gitea.gitea_config[:accelerator]["access_key_id"] + GiteaService.gitea_config[:accelerator]["access_key_id"] end def config_accelerator? - Gitea.gitea_config[:accelerator].present? + GiteaService.gitea_config[:accelerator].present? end def is_foreign_url?(clone_addr) diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index a2ab33b2..34ac9763 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -54,7 +54,7 @@ class RepositoriesController < ApplicationController else @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}/" + @path = GiteaService.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/" end end @@ -222,7 +222,7 @@ class RepositoriesController < ApplicationController else result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token) end - @path = Gitea.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/" + @path = GiteaService.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/" @readme = result[:status] === :success ? result[:body] : nil @readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path) @readme['replace_content'] = readme_decode64_content(@readme, @owner, @repository, params[:ref], @path) @@ -240,8 +240,8 @@ class RepositoriesController < ApplicationController end def archive - domain = Gitea.gitea_config[:domain] - api_url = Gitea.gitea_config[:base_url] + domain = GiteaService.gitea_config[:domain] + api_url = GiteaService.gitea_config[:base_url] archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{Addressable::URI.escape(params[:archive])}" file_path = [domain, api_url, archive_url].join @@ -253,8 +253,8 @@ class RepositoriesController < ApplicationController end def raw - domain = Gitea.gitea_config[:domain] - api_url = Gitea.gitea_config[:base_url] + domain = GiteaService.gitea_config[:domain] + api_url = GiteaService.gitea_config[:base_url] url = "/repos/#{@owner.login}/#{@repository.identifier}/raw/#{Addressable::URI.escape(params[:filepath])}?ref=#{Addressable::URI.escape(params[:ref])}" file_path = [domain, api_url, url].join diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index cc4a3fe0..0dcb6ca1 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -30,7 +30,7 @@ module ProjectsHelper end def gitea_domain - Gitea.gitea_config[:domain] + GiteaService.gitea_config[:domain] end def find_user_by_login_or_mail(identifier) diff --git a/app/interactors/gitea/register_interactor.rb b/app/interactors/gitea/register_interactor.rb index e188809a..ebb2bc4a 100644 --- a/app/interactors/gitea/register_interactor.rb +++ b/app/interactors/gitea/register_interactor.rb @@ -44,8 +44,8 @@ module Gitea def token { - username: Gitea.gitea_config[:access_key_id], - password: Gitea.gitea_config[:access_key_secret] + username: GiteaService.gitea_config[:access_key_id], + password: GiteaService.gitea_config[:access_key_secret] } end end diff --git a/app/libs/ci/drone/server.rb b/app/libs/ci/drone/server.rb index add2e439..de23dd73 100644 --- a/app/libs/ci/drone/server.rb +++ b/app/libs/ci/drone/server.rb @@ -37,7 +37,7 @@ class Ci::Drone::Server private def gitea_url - Gitea.gitea_config[:domain] + GiteaService.gitea_config[:domain] end def database_username diff --git a/app/libs/gitea.rb b/app/libs/gitea_service.rb similarity index 96% rename from app/libs/gitea.rb rename to app/libs/gitea_service.rb index 6679b89d..b44f7948 100644 --- a/app/libs/gitea.rb +++ b/app/libs/gitea_service.rb @@ -1,4 +1,4 @@ -module Gitea +module GiteaService class << self def gitea_config gitea_config = {} diff --git a/app/services/educoder/client_service.rb b/app/services/educoder/client_service.rb index 6c5c10d9..1e77ad89 100644 --- a/app/services/educoder/client_service.rb +++ b/app/services/educoder/client_service.rb @@ -81,7 +81,7 @@ class Educoder::ClientService < ApplicationService end def access_key_secret - Gitea.gitea_config[:access_key_secret] + GiteaService.gitea_config[:access_key_secret] end def api_url(url) diff --git a/app/services/gitea/accelerator/base_service.rb b/app/services/gitea/accelerator/base_service.rb index e63d77aa..9a66eb2b 100644 --- a/app/services/gitea/accelerator/base_service.rb +++ b/app/services/gitea/accelerator/base_service.rb @@ -56,7 +56,7 @@ class Gitea::Accelerator::BaseService < ApplicationService end def accelerator - Gitea.gitea_config[:accelerator] + GiteaService.gitea_config[:accelerator] end def render_status(response) diff --git a/app/services/gitea/client_service.rb b/app/services/gitea/client_service.rb index b0c842db..e2bd9d49 100644 --- a/app/services/gitea/client_service.rb +++ b/app/services/gitea/client_service.rb @@ -96,11 +96,11 @@ class Gitea::ClientService < ApplicationService end def base_url - Gitea.gitea_config[:base_url] + GiteaService.gitea_config[:base_url] end def domain - Gitea.gitea_config[:domain] + GiteaService.gitea_config[:domain] end def api_url diff --git a/app/services/gitea/user/delete_service.rb b/app/services/gitea/user/delete_service.rb index 5df3cb6b..9011158d 100644 --- a/app/services/gitea/user/delete_service.rb +++ b/app/services/gitea/user/delete_service.rb @@ -14,8 +14,8 @@ class Gitea::User::DeleteService < Gitea::ClientService private def token { - username: Gitea.gitea_config[:access_key_id], - password: Gitea.gitea_config[:access_key_secret] + username: GiteaService.gitea_config[:access_key_id], + password: GiteaService.gitea_config[:access_key_secret] } end diff --git a/app/services/gitea/user/update_service.rb b/app/services/gitea/user/update_service.rb index 5e6108a9..9826c7ed 100644 --- a/app/services/gitea/user/update_service.rb +++ b/app/services/gitea/user/update_service.rb @@ -17,7 +17,7 @@ class Gitea::User::UpdateService < Gitea::ClientService # source_id integer($int64) # website string - def initialize(edit_username, params={}, token={username: Gitea.gitea_config[:access_key_id], password: Gitea.gitea_config[:access_key_secret]}) + def initialize(edit_username, params={}, token={username: GiteaService.gitea_config[:access_key_id], password: GiteaService.gitea_config[:access_key_secret]}) @token = token @params = params @edit_username = edit_username diff --git a/app/services/repositories/create_service.rb b/app/services/repositories/create_service.rb index f0c584e1..e7ff8bd1 100644 --- a/app/services/repositories/create_service.rb +++ b/app/services/repositories/create_service.rb @@ -63,7 +63,7 @@ class Repositories::CreateService < ApplicationService end def remote_repository_url - [Gitea.gitea_config[:domain], '/', user.login, '/', params[:identifier], ".git"].join("") + [GiteaService.gitea_config[:domain], '/', user.login, '/', params[:identifier], ".git"].join("") end def repository_params diff --git a/lib/tasks/sync_data_to_gitea.rake b/lib/tasks/sync_data_to_gitea.rake index 9e30b290..85df9c69 100644 --- a/lib/tasks/sync_data_to_gitea.rake +++ b/lib/tasks/sync_data_to_gitea.rake @@ -128,7 +128,7 @@ namespace :sync_data_to_gitea do end def remote_repository_url(username, identifier) - [Gitea.gitea_config[:domain], '/', username, '/', identifier, ".git"].join("") + [GiteaService.gitea_config[:domain], '/', username, '/', identifier, ".git"].join("") end def generate_identifier From 5d0849a1483413d1c0cb0cc75d76faaf2a651279 Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 23 Jun 2022 12:19:03 +0800 Subject: [PATCH 081/208] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E9=A1=B9=E7=9B=AE=E5=88=97=E8=A1=A8=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?helper=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/api/v1/base_controller.rb | 7 ++++ .../api/v1/users/projects_controller.rb | 7 ++++ app/controllers/api/v1/users_controller.rb | 6 ++++ app/controllers/concerns/api/user_helper.rb | 19 +++++++++++ .../v1/projects/_simple_detail.json.jbuilder | 2 +- .../api/v1/users/projects/index.json.jbuilder | 5 +++ config/routes/api.rb | 33 ++++++++++++------- 7 files changed, 66 insertions(+), 13 deletions(-) create mode 100644 app/controllers/api/v1/users/projects_controller.rb create mode 100644 app/controllers/api/v1/users_controller.rb create mode 100644 app/controllers/concerns/api/user_helper.rb create mode 100644 app/views/api/v1/users/projects/index.json.jbuilder diff --git a/app/controllers/api/v1/base_controller.rb b/app/controllers/api/v1/base_controller.rb index 2202df8d..4fb6c875 100644 --- a/app/controllers/api/v1/base_controller.rb +++ b/app/controllers/api/v1/base_controller.rb @@ -1,6 +1,13 @@ class Api::V1::BaseController < ApplicationController include Api::ProjectHelper + include Api::UserHelper + before_action :doorkeeper_authorize! skip_before_action :user_setup + + protected + def current_user + User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token + end end \ No newline at end of file diff --git a/app/controllers/api/v1/users/projects_controller.rb b/app/controllers/api/v1/users/projects_controller.rb new file mode 100644 index 00000000..d130071a --- /dev/null +++ b/app/controllers/api/v1/users/projects_controller.rb @@ -0,0 +1,7 @@ +class Api::V1::Users::ProjectsController < Api::V1::BaseController + before_action :load_observe_user + + def index + @projects = kaminari_paginate(@observe_user.projects) + end +end \ No newline at end of file diff --git a/app/controllers/api/v1/users_controller.rb b/app/controllers/api/v1/users_controller.rb new file mode 100644 index 00000000..55f5cfb2 --- /dev/null +++ b/app/controllers/api/v1/users_controller.rb @@ -0,0 +1,6 @@ +class Api::V1::UsersController < Api::V1::BaseController + + def index + render_ok + end +end \ No newline at end of file diff --git a/app/controllers/concerns/api/user_helper.rb b/app/controllers/concerns/api/user_helper.rb new file mode 100644 index 00000000..e6156ea5 --- /dev/null +++ b/app/controllers/concerns/api/user_helper.rb @@ -0,0 +1,19 @@ +module Api::UserHelper + extend ActiveSupport::Concern + + def load_observe_user + username = params[:owner] + + @observe_user = User.find_by(login: username) + + if @observe_user + logger.info "###########observe_user not founded" + @observe_user + else + logger.info "###########observe_user not found" + @observe_user = nil + render_not_found and return + end + @observe_user + end +end \ No newline at end of file diff --git a/app/views/api/v1/projects/_simple_detail.json.jbuilder b/app/views/api/v1/projects/_simple_detail.json.jbuilder index f5f6b1db..3eadaaf8 100644 --- a/app/views/api/v1/projects/_simple_detail.json.jbuilder +++ b/app/views/api/v1/projects/_simple_detail.json.jbuilder @@ -1,5 +1,5 @@ if project.present? - json.type @project.project_type + json.type project.project_type json.(project, :description, :forked_count, :forked_from_project_id, :identifier, :issues_count, :pull_requests_count, :invite_code, :website, :platform, diff --git a/app/views/api/v1/users/projects/index.json.jbuilder b/app/views/api/v1/users/projects/index.json.jbuilder new file mode 100644 index 00000000..c126d414 --- /dev/null +++ b/app/views/api/v1/users/projects/index.json.jbuilder @@ -0,0 +1,5 @@ +json.total_count @projects.total_count + +json.projects @projects do |project| + json.partial! "api/v1/projects/simple_detail", project: project +end \ No newline at end of file diff --git a/config/routes/api.rb b/config/routes/api.rb index cf55cfcf..5cefe793 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -1,20 +1,29 @@ defaults format: :json do namespace :api do namespace :v1 do - resources :projects, only: [:index] - # 项目相关的api - scope ':owner/:repo' do - # projects - resource :projects, path: '/', only: [:show, :update, :edit, :destroy] - - # projects文件夹下的 - scope module: :projects do - resources :issues - resources :pull_requests - resources :versions - resources :release_versions + scope ':owner' do + resource :users, path: '/', only: [:show, :update, :edit, :destroy] + scope module: :users do + resources :projects, only: [:index] end + + scope ':repo' do + # projects + resource :projects, path: '/', only: [:show, :update, :edit, :destroy] + + # projects文件夹下的 + scope module: :projects do + resources :issues + resources :pull_requests + resources :versions + resources :release_versions + end + end + end + + resources :projects, only: [:index] + end end From fe58175ee081edb23d274441574835842c874ddb Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 23 Jun 2022 16:14:00 +0800 Subject: [PATCH 082/208] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20webhooks=E4=BD=BF?= =?UTF-8?q?=E7=94=A8token=E8=AE=BF=E9=97=AE=E7=9A=84=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile | 2 +- Gemfile.lock | 4 +- .../api/v1/projects/webhooks_controller.rb | 55 +++++++++++++++++ .../v1/projects/webhooks/create_service.rb | 60 ++++++++++++++++++ .../v1/projects/webhooks/delete_service.rb | 30 +++++++++ .../api/v1/projects/webhooks/get_service.rb | 30 +++++++++ .../v1/projects/webhooks/hooktasks_service.rb | 30 +++++++++ .../api/v1/projects/webhooks/list_service.rb | 29 +++++++++ .../api/v1/projects/webhooks/tests_service.rb | 30 +++++++++ .../v1/projects/webhooks/update_service.rb | 61 +++++++++++++++++++ .../webhooks/_simple_detail.json.jbuilder | 3 + .../_simple_gitea_detail.json.jbuilder | 8 +++ .../v1/projects/webhooks/create.json.jbuilder | 1 + .../projects/webhooks/hooktasks.json.jbuilder | 6 ++ .../v1/projects/webhooks/index.json.jbuilder | 4 ++ .../v1/projects/webhooks/show.json.jbuilder | 1 + .../v1/projects/webhooks/update.json.jbuilder | 1 + config/routes/api.rb | 6 ++ 18 files changed, 358 insertions(+), 3 deletions(-) create mode 100644 app/controllers/api/v1/projects/webhooks_controller.rb create mode 100644 app/services/api/v1/projects/webhooks/create_service.rb create mode 100644 app/services/api/v1/projects/webhooks/delete_service.rb create mode 100644 app/services/api/v1/projects/webhooks/get_service.rb create mode 100644 app/services/api/v1/projects/webhooks/hooktasks_service.rb create mode 100644 app/services/api/v1/projects/webhooks/list_service.rb create mode 100644 app/services/api/v1/projects/webhooks/tests_service.rb create mode 100644 app/services/api/v1/projects/webhooks/update_service.rb create mode 100644 app/views/api/v1/projects/webhooks/_simple_detail.json.jbuilder create mode 100644 app/views/api/v1/projects/webhooks/_simple_gitea_detail.json.jbuilder create mode 100644 app/views/api/v1/projects/webhooks/create.json.jbuilder create mode 100644 app/views/api/v1/projects/webhooks/hooktasks.json.jbuilder create mode 100644 app/views/api/v1/projects/webhooks/index.json.jbuilder create mode 100644 app/views/api/v1/projects/webhooks/show.json.jbuilder create mode 100644 app/views/api/v1/projects/webhooks/update.json.jbuilder diff --git a/Gemfile b/Gemfile index 1542856c..1b3ff3e0 100644 --- a/Gemfile +++ b/Gemfile @@ -135,4 +135,4 @@ gem 'doorkeeper' gem 'doorkeeper-jwt' -gem 'gitea-client', '~> 0.6.2' \ No newline at end of file +gem 'gitea-client', '~> 0.8.1' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index 467e19a2..e231bbef 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -135,7 +135,7 @@ GEM fugit (1.4.1) et-orbi (~> 1.1, >= 1.1.8) raabro (~> 1.4) - gitea-client (0.6.2) + gitea-client (0.8.1) rest-client (~> 2.1.0) globalid (0.4.2) activesupport (>= 4.2.0) @@ -478,7 +478,7 @@ DEPENDENCIES enumerize faraday (~> 0.15.4) font-awesome-sass (= 4.7.0) - gitea-client (~> 0.6.2) + gitea-client (~> 0.8.1) grape-entity (~> 0.7.1) groupdate (~> 4.1.0) harmonious_dictionary (~> 0.0.1) diff --git a/app/controllers/api/v1/projects/webhooks_controller.rb b/app/controllers/api/v1/projects/webhooks_controller.rb new file mode 100644 index 00000000..bb828a70 --- /dev/null +++ b/app/controllers/api/v1/projects/webhooks_controller.rb @@ -0,0 +1,55 @@ +class Api::V1::Projects::WebhooksController < Api::V1::BaseController + before_action :load_project + before_action :find_webhook, only: [:show, :update, :destroy, :tests, :hooktasks] + + def index + # @result_object = Api::V1::Projects::Webhooks::ListService.call(@project, current_user&.gitea_token) + @webhooks = @project.webhooks + @webhooks = kaminari_paginate(@webhooks) + end + + def create + @result_object = Api::V1::Projects::Webhooks::CreateService.call(@project, webhook_params, current_user&.gitea_token) + end + + def show + @result_object = Api::V1::Projects::Webhooks::GetService.call(@project, params[:id], current_user&.gitea_token) + end + + def update + @result_object = Api::V1::Projects::Webhooks::UpdateService.call(@project, params[:id], webhook_params, current_user&.gitea_token) + end + + def destroy + @result_object = Api::V1::Projects::Webhooks::DeleteService.call(@project, params[:id], current_user&.gitea_token) + if @result_object + return render_ok + else + return render_error('删除失败!') + end + end + + def tests + @result_object = Api::V1::Projects::Webhooks::TestsService.call(@project, params[:id], current_user&.gitea_token) + if @result_object + return render_ok + else + return render_error('推送失败!') + end + end + + def hooktasks + @hooktasks = @webhook.tasks.where(is_delivered: true).order("delivered desc") + @hooktasks = kaminari_paginate(@hooktasks) + end + + private + def webhook_params + params.require(:webhook).permit(:active, :branch_filter, :http_method, :url, :content_type, :secret, events: []) + end + + def find_webhook + @webhook = Gitea::Webhook.find_by_id(params[:id]) + return render_not_found unless @webhook.present? + end +end \ No newline at end of file diff --git a/app/services/api/v1/projects/webhooks/create_service.rb b/app/services/api/v1/projects/webhooks/create_service.rb new file mode 100644 index 00000000..401ff9ff --- /dev/null +++ b/app/services/api/v1/projects/webhooks/create_service.rb @@ -0,0 +1,60 @@ +class Api::V1::Projects::Webhooks::CreateService < ApplicationService + include ActiveModel::Model + + attr_reader :project, :token, :owner, :repo, :active, :branch_filter, :content_type, :url, :http_method, :secret, :events + attr_accessor :gitea_data + + validates :url, format: { with: URI::regexp(%w[http https]), message: "请输入正确的地址" } + validates :active, inclusion: {in: [true, false]} + validates :http_method, inclusion: { in: %w(POST GET), message: "请输入正确的请求方式"} + validates :content_type, inclusion: { in: %w(json form), message: "请输入正确的Content Type"} + + def initialize(project, params, token=nil) + @project = project + @owner = project&.owner.login + @repo = project&.identifier + @active = params[:active] + @branch_filter = params[:branch_filter] + @content_type = params[:content_type] + @url = params[:url] + @http_method = params[:http_method] + @secret = params[:secret] + @events = params[:events] + @token = token + end + + def call + raise Error, errors.full_messages.join(",") unless valid? + begin + $gitea_client.token = token unless token.blank? + + excute_data_to_gitea + + $gitea_client.token = nil unless token.blank? + + gitea_data + rescue + raise Error, "服务器错误,请联系系统管理员!" + end + end + + private + def request_body + { + active: active, + branch_filter: branch_filter, + config: { + content_type: content_type, + url: url, + http_method: http_method, + secret: secret + }, + events: events || [], + type: 'gitea', + } + end + + def excute_data_to_gitea + @gitea_data = $gitea_client.post_repos_hooks_by_owner_repo(owner, repo, {body: request_body.to_json}) + end +end \ No newline at end of file diff --git a/app/services/api/v1/projects/webhooks/delete_service.rb b/app/services/api/v1/projects/webhooks/delete_service.rb new file mode 100644 index 00000000..53fd8edd --- /dev/null +++ b/app/services/api/v1/projects/webhooks/delete_service.rb @@ -0,0 +1,30 @@ +class Api::V1::Projects::Webhooks::DeleteService < ApplicationService + + attr_reader :project, :id, :token, :owner, :repo + attr_accessor :gitea_data + + def initialize(project, id, token=nil) + @project = project + @id = id + @owner = project&.owner.login + @repo = project&.identifier + @token = token + end + + def call + $gitea_client.token = token unless token.blank? + excute_data_to_gitea + + $gitea_client.token = nil unless token.blank? + + gitea_data + # rescue + # raise Error, "服务器错误,请联系系统管理员!" + end + + private + + def excute_data_to_gitea + @gitea_data = $gitea_client.delete_repos_hooks_by_owner_repo_id(owner, repo, id) + end +end \ No newline at end of file diff --git a/app/services/api/v1/projects/webhooks/get_service.rb b/app/services/api/v1/projects/webhooks/get_service.rb new file mode 100644 index 00000000..1ae5c54e --- /dev/null +++ b/app/services/api/v1/projects/webhooks/get_service.rb @@ -0,0 +1,30 @@ +class Api::V1::Projects::Webhooks::GetService < ApplicationService + + attr_reader :project, :id, :token, :owner, :repo + attr_accessor :gitea_data + + def initialize(project, id, token=nil) + @project = project + @id = id + @owner = project&.owner.login + @repo = project&.identifier + @token = token + end + + def call + $gitea_client.token = token unless token.blank? + load_gitea_data + + $gitea_client.token = nil unless token.blank? + + gitea_data + rescue + raise Error, "服务器错误,请联系系统管理员!" + end + + private + + def load_gitea_data + @gitea_data = $gitea_client.get_repos_hooks_by_owner_repo_id(owner, repo, id) + end +end \ No newline at end of file diff --git a/app/services/api/v1/projects/webhooks/hooktasks_service.rb b/app/services/api/v1/projects/webhooks/hooktasks_service.rb new file mode 100644 index 00000000..4dd90ee3 --- /dev/null +++ b/app/services/api/v1/projects/webhooks/hooktasks_service.rb @@ -0,0 +1,30 @@ +class Api::V1::Projects::Webhooks::ListService < ApplicationService + + attr_reader :project, :id, :token, :owner, :repo + attr_accessor :gitea_data + + def initialize(project, id, token=nil) + @project = project + @id = id + @owner = project&.owner.login + @repo = project&.identifier + @token = token + end + + def call + $gitea_client.token = token unless token.blank? + load_gitea_data + + $gitea_client.token = nil unless token.blank? + + gitea_data + rescue + raise Error, "服务器错误,请联系系统管理员!" + end + + private + + def load_gitea_data + @gitea_data = $gitea_client.get_repos_hooks_hooktasks_by_owner_repo(owner, repo, id) + end +end \ No newline at end of file diff --git a/app/services/api/v1/projects/webhooks/list_service.rb b/app/services/api/v1/projects/webhooks/list_service.rb new file mode 100644 index 00000000..fb746de4 --- /dev/null +++ b/app/services/api/v1/projects/webhooks/list_service.rb @@ -0,0 +1,29 @@ +class Api::V1::Projects::Webhooks::ListService < ApplicationService + + attr_reader :project, :token, :owner, :repo + attr_accessor :gitea_data + + def initialize(project, token=nil) + @project = project + @owner = project&.owner.login + @repo = project&.identifier + @token = token + end + + def call + $gitea_client.token = token unless token.blank? + load_gitea_data + + $gitea_client.token = nil unless token.blank? + + gitea_data + rescue + raise Error, "服务器错误,请联系系统管理员!" + end + + private + + def load_gitea_data + @gitea_data = $gitea_client.get_repos_hooks_by_owner_repo(owner, repo) + end +end \ No newline at end of file diff --git a/app/services/api/v1/projects/webhooks/tests_service.rb b/app/services/api/v1/projects/webhooks/tests_service.rb new file mode 100644 index 00000000..3d8b99c9 --- /dev/null +++ b/app/services/api/v1/projects/webhooks/tests_service.rb @@ -0,0 +1,30 @@ +class Api::V1::Projects::Webhooks::TestsService < ApplicationService + + attr_reader :project, :id, :token, :owner, :repo + attr_accessor :gitea_data + + def initialize(project, id, token=nil) + @project = project + @id = id + @owner = project&.owner.login + @repo = project&.identifier + @token = token + end + + def call + $gitea_client.token = token unless token.blank? + excute_data_to_gitea + + $gitea_client.token = nil unless token.blank? + + gitea_data + rescue + raise Error, "服务器错误,请联系系统管理员!" + end + + private + + def excute_data_to_gitea + @gitea_data = $gitea_client.post_repos_hooks_tests_by_owner_repo_id(owner, repo, id) + end +end \ No newline at end of file diff --git a/app/services/api/v1/projects/webhooks/update_service.rb b/app/services/api/v1/projects/webhooks/update_service.rb new file mode 100644 index 00000000..19ff4f32 --- /dev/null +++ b/app/services/api/v1/projects/webhooks/update_service.rb @@ -0,0 +1,61 @@ +class Api::V1::Projects::Webhooks::UpdateService < ApplicationService + include ActiveModel::Model + + attr_reader :project, :id, :token, :owner, :repo, :active, :branch_filter, :content_type, :url, :http_method, :secret, :events + attr_accessor :gitea_data + + validates :url, format: { with: URI::regexp(%w[http https]), message: "请输入正确的地址" } + validates :active, inclusion: {in: [true, false]} + validates :http_method, inclusion: { in: %w(POST GET), message: "请输入正确的请求方式"} + validates :content_type, inclusion: { in: %w(json form), message: "请输入正确的Content Type"} + + def initialize(project, id, params, token=nil) + @project = project + @id = id + @owner = project&.owner.login + @repo = project&.identifier + @active = params[:active] + @branch_filter = params[:branch_filter] + @content_type = params[:content_type] + @url = params[:url] + @http_method = params[:http_method] + @secret = params[:secret] + @events = params[:events] + @token = token + end + + def call + raise Error, errors.full_messages.join(",") unless valid? + begin + $gitea_client.token = token unless token.blank? + + excute_data_to_gitea + + $gitea_client.token = nil unless token.blank? + + gitea_data + rescue + raise Error, "服务器错误,请联系系统管理员!" + end + end + + private + def request_body + { + active: active, + branch_filter: branch_filter, + config: { + content_type: content_type, + url: url, + http_method: http_method, + secret: secret + }, + events: events || [], + type: 'gitea', + } + end + + def excute_data_to_gitea + @gitea_data = $gitea_client.patch_repos_hooks_by_owner_repo_id(owner, repo, id, {body: request_body.to_json}) + end +end \ No newline at end of file diff --git a/app/views/api/v1/projects/webhooks/_simple_detail.json.jbuilder b/app/views/api/v1/projects/webhooks/_simple_detail.json.jbuilder new file mode 100644 index 00000000..145b3e59 --- /dev/null +++ b/app/views/api/v1/projects/webhooks/_simple_detail.json.jbuilder @@ -0,0 +1,3 @@ +json.(webhook, :id, :url, :http_method, :is_active) +json.last_status webhook.last_status +json.create_time Time.at(webhook.created_unix).strftime("%Y-%m-%d %H:%M:%S") \ No newline at end of file diff --git a/app/views/api/v1/projects/webhooks/_simple_gitea_detail.json.jbuilder b/app/views/api/v1/projects/webhooks/_simple_gitea_detail.json.jbuilder new file mode 100644 index 00000000..ca180d8b --- /dev/null +++ b/app/views/api/v1/projects/webhooks/_simple_gitea_detail.json.jbuilder @@ -0,0 +1,8 @@ +json.id webhook["id"] +json.content_type webhook['config']['content_type'] +json.http_method webhook['config']['http_method'] +json.url webhook['config']['url'] +json.events webhook['events'] +json.active webhook['active'] +json.branch_filter webhook['branch_filter'] +json.created_at format_time(webhook['created_at'].to_time) \ No newline at end of file diff --git a/app/views/api/v1/projects/webhooks/create.json.jbuilder b/app/views/api/v1/projects/webhooks/create.json.jbuilder new file mode 100644 index 00000000..01f91963 --- /dev/null +++ b/app/views/api/v1/projects/webhooks/create.json.jbuilder @@ -0,0 +1 @@ +json.partial! "api/v1/projects/webhooks/simple_gitea_detail", webhook: @result_object \ No newline at end of file diff --git a/app/views/api/v1/projects/webhooks/hooktasks.json.jbuilder b/app/views/api/v1/projects/webhooks/hooktasks.json.jbuilder new file mode 100644 index 00000000..618a0551 --- /dev/null +++ b/app/views/api/v1/projects/webhooks/hooktasks.json.jbuilder @@ -0,0 +1,6 @@ +json.total_count @hooktasks.total_count +json.hooktasks @hooktasks.each do |task| + json.(task, :id, :event_type, :type, :uuid, :is_succeed, :is_delivered, :payload_content, :request_content) + json.response_content task.response_content_json + json.delivered_time Time.at(task.delivered*10**-9).strftime("%Y-%m-%d %H:%M:%S") +end \ No newline at end of file diff --git a/app/views/api/v1/projects/webhooks/index.json.jbuilder b/app/views/api/v1/projects/webhooks/index.json.jbuilder new file mode 100644 index 00000000..7a375619 --- /dev/null +++ b/app/views/api/v1/projects/webhooks/index.json.jbuilder @@ -0,0 +1,4 @@ +json.total_count @webhooks.total_count +json.webhooks @webhooks do |webhook| + json.partial! "api/v1/projects/webhooks/simple_detail", webhook: webhook +end \ No newline at end of file diff --git a/app/views/api/v1/projects/webhooks/show.json.jbuilder b/app/views/api/v1/projects/webhooks/show.json.jbuilder new file mode 100644 index 00000000..01f91963 --- /dev/null +++ b/app/views/api/v1/projects/webhooks/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! "api/v1/projects/webhooks/simple_gitea_detail", webhook: @result_object \ No newline at end of file diff --git a/app/views/api/v1/projects/webhooks/update.json.jbuilder b/app/views/api/v1/projects/webhooks/update.json.jbuilder new file mode 100644 index 00000000..01f91963 --- /dev/null +++ b/app/views/api/v1/projects/webhooks/update.json.jbuilder @@ -0,0 +1 @@ +json.partial! "api/v1/projects/webhooks/simple_gitea_detail", webhook: @result_object \ No newline at end of file diff --git a/config/routes/api.rb b/config/routes/api.rb index 5cefe793..32acdabd 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -17,6 +17,12 @@ defaults format: :json do resources :pull_requests resources :versions resources :release_versions + resources :webhooks do + member do + post :tests + get :hooktasks + end + end end end From 7cad953413e5282de00bb8c666ada9e204313a4b Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 23 Jun 2022 18:16:57 +0800 Subject: [PATCH 083/208] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E9=A1=B9=E7=9B=AE=E5=88=97=E8=A1=A8=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=A4=9A=E4=B8=AA=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/api/v1/base_controller.rb | 5 ++ .../api/v1/projects/webhooks_controller.rb | 2 +- .../api/v1/users/projects_controller.rb | 8 +- .../api/v1/users/projects/list_service.rb | 79 +++++++++++++++++++ .../api/v1/users/projects/index.json.jbuilder | 3 + 5 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 app/services/api/v1/users/projects/list_service.rb diff --git a/app/controllers/api/v1/base_controller.rb b/app/controllers/api/v1/base_controller.rb index 4fb6c875..9170975b 100644 --- a/app/controllers/api/v1/base_controller.rb +++ b/app/controllers/api/v1/base_controller.rb @@ -10,4 +10,9 @@ class Api::V1::BaseController < ApplicationController def current_user User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token end + + def require_manager_above + @project = load_project + return render_forbidden unless current_user.admin? && @project.manager?(current_user) + end end \ No newline at end of file diff --git a/app/controllers/api/v1/projects/webhooks_controller.rb b/app/controllers/api/v1/projects/webhooks_controller.rb index bb828a70..bb2c2172 100644 --- a/app/controllers/api/v1/projects/webhooks_controller.rb +++ b/app/controllers/api/v1/projects/webhooks_controller.rb @@ -1,5 +1,5 @@ class Api::V1::Projects::WebhooksController < Api::V1::BaseController - before_action :load_project + before_action :require_manager_above before_action :find_webhook, only: [:show, :update, :destroy, :tests, :hooktasks] def index diff --git a/app/controllers/api/v1/users/projects_controller.rb b/app/controllers/api/v1/users/projects_controller.rb index d130071a..ac4b4832 100644 --- a/app/controllers/api/v1/users/projects_controller.rb +++ b/app/controllers/api/v1/users/projects_controller.rb @@ -2,6 +2,12 @@ class Api::V1::Users::ProjectsController < Api::V1::BaseController before_action :load_observe_user def index - @projects = kaminari_paginate(@observe_user.projects) + @object_results = Api::V1::Users::Projects::ListService.call(@observe_user, query_params, current_user) + @projects = kaminari_paginate(@object_results) + end + + private + def query_params + params.permit(:category, :is_public, :project_type, :sort_by, :sort_direction, :search) end end \ No newline at end of file diff --git a/app/services/api/v1/users/projects/list_service.rb b/app/services/api/v1/users/projects/list_service.rb new file mode 100644 index 00000000..32884ac7 --- /dev/null +++ b/app/services/api/v1/users/projects/list_service.rb @@ -0,0 +1,79 @@ +class Api::V1::Users::Projects::ListService < ApplicationService + include ActiveModel::Model + + attr_reader :observe_user, :category, :is_public, :project_type, :sort_by, :sort_direction, :search, :current_user + attr_accessor :queried_projects + + validates :category, inclusion: {in: %w(all join created watched forked), message: "请输入正确的Category"} + validates :is_public, inclusion: {in: [true, false], message: '请输入正确的IsPublic'} + validates :project_type, inclusion: {in: %w(common mirror sync_mirror), message: '请输入正确的ProjectType'}, allow_nil: true + validates :sort_by, inclusion: {in: Project.column_names, message: '请输入正确的SortBy'} + validates :sort_direction, inclusion: {in: %w(asc desc), message: '请输入正确的SortDirection'} + + def initialize(observe_user, params, current_user=nil) + @observe_user = observe_user + @category = params[:category] || 'all' + @is_public = params[:is_public] || true + @project_type = params[:project_type] + @sort_by = params[:sort_by] || 'updated_on' + @sort_direction = params[:sort_direction] || 'desc' + @search = params[:search] + @current_user = current_user + end + + def call + raise Error, errors.full_messages.join(", ") unless valid? + # begin + project_query_data + + queried_projects + # rescue + # raise Error, "服务器错误,请联系系统管理员!" + # end + end + + private + def project_query_data + if current_user.admin? + projects = Project + else + projects = Project.visible + end + + case category + when 'join' + normal_projects = projects.where.not(user_id: observe_user.id).members_projects(observe_user.id).to_sql + org_projects = projects.joins(team_projects: [team: :team_users]).where(team_users: {user_id: observe_user.id}).to_sql + projects = Project.from("( #{normal_projects} UNION #{org_projects} ) AS projects").distinct + when 'created' + projects = projects.where(user_id: observe_user.id) + when 'watched' + projects = projects.where.not(user_id: observe_user.id).joins(:watchers).where(watchers: {watchable_type: "Project", user_id: observe_user.id}) + when 'forked' + fork_ids = observe_user.fork_users.select(:id, :fork_project_id).pluck(:fork_project_id) + projects = projects.where(id: fork_ids) + else + normal_projects = projects.members_projects(observe_user.id).to_sql + org_projects = projects.joins(team_projects: [team: :team_users]).where(team_users: {user_id: observe_user.id}).to_sql + projects = Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct + end + + if is_public + projects = projects.visible + else + projects = projects.is_private + end + + projects = projects.with_project_type(project_type) + + q = projects.ransack(name_or_identifier_cont: search) + + scope = q.result.includes(:project_category, :project_language,:owner, :repository, :has_pinned_users) + + + scope = scope.order("projects.#{sort_by} #{sort_direction}") + + @queried_projects = scope + end + +end \ No newline at end of file diff --git a/app/views/api/v1/users/projects/index.json.jbuilder b/app/views/api/v1/users/projects/index.json.jbuilder index c126d414..5450d2dd 100644 --- a/app/views/api/v1/users/projects/index.json.jbuilder +++ b/app/views/api/v1/users/projects/index.json.jbuilder @@ -1,5 +1,8 @@ json.total_count @projects.total_count json.projects @projects do |project| + json.owner do + json.partial! "api/v1/users/simple_user", user: project.owner + end json.partial! "api/v1/projects/simple_detail", project: project end \ No newline at end of file From 211581cef0ffababcf9b30f0090f3f35bd4da478 Mon Sep 17 00:00:00 2001 From: yystopf Date: Fri, 24 Jun 2022 09:26:01 +0800 Subject: [PATCH 084/208] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20webhooks=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E4=BD=BF=E7=94=A8=E6=96=B0api=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../slate/source/includes/_repositories.md | 108 ++++++++++-------- .../v1/projects/webhooks/delete_service.rb | 4 +- .../api/v1/users/projects/list_service.rb | 8 +- 3 files changed, 67 insertions(+), 53 deletions(-) diff --git a/app/docs/slate/source/includes/_repositories.md b/app/docs/slate/source/includes/_repositories.md index a44d2b35..9238615e 100644 --- a/app/docs/slate/source/includes/_repositories.md +++ b/app/docs/slate/source/includes/_repositories.md @@ -1115,15 +1115,15 @@ await octokit.request('GET /api/yystopf/csfjkkj/contributors.json') ```shell curl -X GET \ -http://localhost:3000/api/yystopf/ceshi/webhooks.json +http://localhost:3000/api/v1/yystopf/ceshi/webhooks.json ``` ```javascript -await octokit.request('GET /api/yystopf/ceshi/webhooks.json') +await octokit.request('GET /api/v1/yystopf/ceshi/webhooks.json') ``` ### HTTP 请求 -`GET /api/:owner/:repo/webhooks.json` +`GET /api/v1/:owner/:repo/webhooks.json` ### 请求参数: 参数 | 必选 | 默认 | 类型 | 字段说明 @@ -1200,15 +1200,15 @@ await octokit.request('GET /api/yystopf/ceshi/webhooks.json') ```shell curl -X GET \ -http://localhost:3000/api/yystopf/ceshi/webhooks/3/edit.json +http://localhost:3000/api/v1/yystopf/ceshi/webhooks/3.json ``` ```javascript -await octokit.request('GET /api/yystopf/ceshi/webhooks/3/edit.json') +await octokit.request('GET /api/v1/yystopf/ceshi/webhooks/3.json') ``` ### HTTP 请求 -`GET /api/:owner/:repo/webhooks/:id/edit.json` +`GET /api/v1/:owner/:repo/webhooks/:id.json` ### 请求参数: 参数 | 必选 | 默认 | 类型 | 字段说明 @@ -1226,12 +1226,10 @@ await octokit.request('GET /api/yystopf/ceshi/webhooks/3/edit.json') |content_type |string|POST Content Type| |http_method |string|请求方式| |secret| |string|密钥| -|is_active |bool |是否激活| -|type |string|类型| -|last_status |string|最后一次推送的状态, waiting 等待,fail 失败,succeed 成功| +|active |bool |是否激活| |branch_filter |string|分支过滤| |events |string|触发条件| -|create_time |string|创建时间| +|create_at |string|创建时间| 参数| 含义| @@ -1266,11 +1264,8 @@ await octokit.request('GET /api/yystopf/ceshi/webhooks/3/edit.json') "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", + "active": true, + "create_at": "2021-07-26 10:03", "branch_filter": "*", "events": [ "create", @@ -1305,15 +1300,15 @@ await octokit.request('GET /api/yystopf/ceshi/webhooks/3/edit.json') ```shell curl -X POST \ -http://localhost:3000/api/yystopf/ceshi/webhooks.json +http://localhost:3000/api/v1/yystopf/ceshi/webhooks.json ``` ```javascript -await octokit.request('POST /api/yystopf/ceshi/webhooks.json') +await octokit.request('POST /api/v1/yystopf/ceshi/webhooks.json') ``` ### HTTP 请求 -`POST /api/:owner/:repo/webhooks.json` +`POST /api/v1/:owner/:repo/webhooks.json` ### 请求参数: 参数 | 必选 | 默认 | 类型 | 字段说明 @@ -1321,7 +1316,6 @@ await octokit.request('POST /api/yystopf/ceshi/webhooks.json') |owner |是| | string |用户登录名 | |repo |是| | string |项目标识identifier | |webhook.url |是| | string |目标url | -|webhook.type |否| | string |类型| |webhook.http_method |是| | string | http方法, POST和GET | |webhook.content_type |是| | string | POST Content Type | |webhook.secret |否| | string |密钥文本| @@ -1335,24 +1329,24 @@ await octokit.request('POST /api/yystopf/ceshi/webhooks.json') --------- | ------- | ------- | |create|创建分支或标签| |delete|分支或标签删除| -|fork|仓库被fork| + |push|git仓库推送| -|issue|疑修已打开、已关闭、已重新打开或编辑| + |pull_request|合并请求| |pull_request_assign|合并请求被指派| -|pull_request_label|合并请求被贴上标签| + |pull_request_review_approved|合并请求被批准| |pull_request_review_rejected|合并请求被拒绝| -|pull_request_review_comment|合并请求被提出审查意见| + > 请求的JSON示例: @@ -1385,15 +1379,22 @@ await octokit.request('POST /api/yystopf/ceshi/webhooks.json') ```json { - "id": 18, - "type": "gitea", + "id": 68, "content_type": "json", - "url": "http://localhost:10000", + "http_method": "GET", + "url": "http://127.0.0.1:3000", "events": [ - "push" + "create", + "delete", + "push", + "pull_request", + "pull_request_assign", + "pull_request_review_approved", + "pull_request_review_rejected" ], "active": true, - "create_time": "2021-07-26 18:53:43" + "branch_filter": "*", + "created_at": "2022-06-23 15:52" } ```
      参数
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      参数类型字段说明
      loginstring用户的用户名
      categorystring归属项目,默认为全部项目, join: 参与项目, created: 创建项目, manage: 管理项目, watched: 关注项目, forked: 复刻项目
      is_publicboolean公/私有项目,true为公开项目,false为私有项目
      project_typestring项目类型,common为托管项目, mirror为镜像项目, sync_mirror为同步镜像项目
      sort_bystring项目的排序字段,比如 created_on, updated_on等
      sort_directionstring排序的类型,desc 倒序,asc 正序
      limitinteger每页个数
      pageinteger页码
      +

      返回字段说明:

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      参数类型字段说明
      total_countinteger项目总数
      projects.owner.idinteger项目拥有者id
      projects.owner.typestring项目拥有者类型,User 用户,Organization 组织
      projects.owner.namestring项目拥有者名称
      projects.owner.loginstring项目拥有者用户名
      projects.owner.image_urlstring项目拥有者头像地址
      typestring项目类型,common 托管项目,mirror 镜像项目, sync 同步镜像项目
      descriptionstring项目描述
      forked_countinteger项目复刻数量
      forked_from_project_idinteger项目复刻来源项目
      identifierstring项目标识
      issues_countinteger项目issues数量
      pull_requests_countinteger项目合并请求数量
      invite_codestring项目邀请码
      websitestring项目网址
      platformstring项目平台
      namestring项目名称
      open_devopsboolean项目是否开启工作流
      praises_countinteger项目点赞数量
      is_publicboolean项目是否公开
      statusinteger项目状态
      watchers_countinteger项目关注数量
      ignore_idinteger项目ignoreID
      license_idinteger项目许可证ID
      project_category_idinteger项目分类ID
      project_language_idinteger项目语言ID
      + +
      +

      返回的JSON示例:

      +
      +
      "total_count": 1,
      +"projects": [
      +    {
      +        "owner": {
      +            "id": 2,
      +            "type": "User",
      +            "name": "heh",
      +            "login": "yystopf",
      +            "image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
      +        },
      +        "type": "common",
      +        "description": null,
      +        "forked_count": 1,
      +        "forked_from_project_id": null,
      +        "identifier": "hahahah",
      +        "issues_count": 5,
      +        "pull_requests_count": 3,
      +        "invite_code": "8zfKtM",
      +        "website": null,
      +        "platform": "forge",
      +        "name": "hahahah",
      +        "open_devops": false,
      +        "praises_count": 0,
      +        "is_public": true,
      +        "status": 1,
      +        "watchers_count": 0,
      +        "ignore_id": null,
      +        "license_id": null,
      +        "project_category_id": null,
      +        "project_language_id": null
      +    }
      +]
      +
      +

      用户消息列表

      获取用户消息列表

      @@ -1044,9 +1285,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X GET http://localhost:3000/api/users/:login/messages.json
       
      await octokit.request('GET /api/users/:login/messages.json')
      -

      HTTP 请求

      +

      HTTP 请求

      GET api/users/yystopf/messages.json

      -

      请求字段说明:

      +

      请求字段说明:

      @@ -1075,7 +1316,7 @@ Success — a happy kitten is an authenticated kitten!
      参数页码
      -

      返回字段说明:

      +

      返回字段说明:

      @@ -1363,9 +1604,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X POST http://localhost:3000/api/users/yystopf/system_notification_histories.json
       
      await octokit.request('GET /api/users/:login/system_notification_histories.json')
      -

      HTTP 请求

      +

      HTTP 请求

      POST /api/users/:login/system_notification_histories.json

      -

      请求字段说明:

      +

      请求字段说明:

      参数
      @@ -1395,9 +1636,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X POST http://localhost:3000/api/users/:login/messages.json
       
      await octokit.request('POST /api/users/:login/messages.json')
      -

      HTTP 请求

      +

      HTTP 请求

      POST api/users/yystopf/messages.json

      -

      请求字段说明:

      +

      请求字段说明:

      参数
      @@ -1456,9 +1697,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X POST http://localhost:3000/api/users/:login/messages/read.json
       
      await octokit.request('POST /api/users/:login/messages/read.json')
      -

      HTTP 请求

      +

      HTTP 请求

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

      -

      请求字段说明:

      +

      请求字段说明:

      参数
      @@ -1497,9 +1738,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X DELETE http://localhost:3000/api/users/:login/messages.json
       
      await octokit.request('DELETE /api/users/:login/messages.json')
      -

      HTTP 请求

      +

      HTTP 请求

      DELETE api/users/yystopf/messages.json

      -

      请求字段说明:

      +

      请求字段说明:

      参数
      @@ -1538,9 +1779,9 @@ 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')
      -

      HTTP 请求

      +

      HTTP 请求

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

      -

      请求字段说明:

      +

      请求字段说明:

      参数
      @@ -1636,9 +1877,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X GET http://localhost:3000/api/template_message_settings.json
       
      await octokit.request('GET /api/template_message_settings.json')
      -

      HTTP 请求

      +

      HTTP 请求

      GET /api/template_message_settings.json

      -

      返回字段说明:

      +

      返回字段说明:

      参数
      @@ -1779,9 +2020,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X GET http://localhost:3000/api/users/yystopf/template_message_settings.json
       
      await octokit.request('GET /api/uses/yystopf/template_message_settings.json')
      -

      HTTP 请求

      +

      HTTP 请求

      GET /api/users/:user_id/template_message_settings.json

      -

      返回字段说明:

      +

      返回字段说明:

      参数
      @@ -1849,9 +2090,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X POST http://localhost:3000/api/users/yystopf/template_message_settings/update_setting.json
       
      await octokit.request('POST /api/uses/yystopf/template_message_settings/update_setting.json')
      -

      HTTP 请求

      +

      HTTP 请求

      POST /api/users/:user_id/template_message_settings/update_setting.json

      -

      请求字段说明:

      +

      请求字段说明:

      参数
      @@ -1900,7 +2141,7 @@ Success — a happy kitten is an authenticated kitten! } } } -

      返回字段说明:

      +

      返回字段说明:

      参数
      @@ -1968,9 +2209,9 @@ Success — a happy kitten is an authenticated kitten!
      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 请求

      +

      HTTP 请求

      GET api/users/:login/is_pinned_projects.json

      -

      返回字段说明:

      +

      返回字段说明:

      参数
      @@ -2155,9 +2396,9 @@ Success — a happy kitten is an authenticated kitten!
      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 请求

      +

      HTTP 请求

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

      -

      请求字段说明:

      同时设定多个星标项目

      +

      请求字段说明:

      同时设定多个星标项目

      参数
      @@ -2201,9 +2442,9 @@ Success — a happy kitten is an authenticated kitten!
      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 请求

      +

      HTTP 请求

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

      -

      请求字段说明:

      +

      请求字段说明:

      参数
      @@ -2242,9 +2483,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X GET http://localhost:3000/api/users/yystopf/statistics/activity.json
       
      await octokit.request('GET /api/users/:login/statistics/activity.json')
      -

      HTTP 请求

      +

      HTTP 请求

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

      -

      返回字段说明:

      +

      返回字段说明:

      参数
      @@ -2331,9 +2572,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X GET http://localhost:3000/api/users/yystopf/headmaps.json
       
      await octokit.request('GET /api/users/:login/headmaps.json')
      -

      HTTP 请求

      +

      HTTP 请求

      GET api/users/:login/headmaps.json

      -

      请求字段说明:

      +

      请求字段说明:

      参数
      @@ -2347,7 +2588,7 @@ Success — a happy kitten is an authenticated kitten!
      参数年份
      -

      返回字段说明:

      +

      返回字段说明:

      @@ -2476,9 +2717,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X GET http://localhost:3000/api/users/yystopf/project_trends.json
       
      await octokit.request('GET /api/users/:login/project_trends.json')
      -

      HTTP 请求

      +

      HTTP 请求

      GET api/users/:login/project_trends.json

      -

      请求字段说明:

      +

      请求字段说明:

      参数
      @@ -2492,7 +2733,7 @@ Success — a happy kitten is an authenticated kitten!
      参数日期,格式: 2021-05-28
      -

      返回字段说明:

      +

      返回字段说明:

      @@ -2793,9 +3034,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X GET http://localhost:3000/api/users/yystopf/statistics/develop.json
       
      await octokit.request('GET /api/users/:login/statistics/develop.json')
      -

      HTTP 请求

      +

      HTTP 请求

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

      -

      请求字段说明:

      +

      请求字段说明:

      参数
      @@ -2814,7 +3055,7 @@ Success — a happy kitten is an authenticated kitten!
      参数时间戳,结束时间,格式:1622131200
      -

      返回字段说明:

      +

      返回字段说明:

      @@ -2936,9 +3177,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X GET http://localhost:3000/api/users/yystopf/statistics/role.json
       
      await octokit.request('GET /api/users/:login/statistics/role.json')
      -

      HTTP 请求

      +

      HTTP 请求

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

      -

      请求字段说明:

      +

      请求字段说明:

      参数
      @@ -2957,7 +3198,7 @@ Success — a happy kitten is an authenticated kitten!
      参数时间戳,结束时间,格式:1622131200
      -

      返回字段说明:

      +

      返回字段说明:

      @@ -3018,9 +3259,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X GET http://localhost:3000/api/users/yystopf/statistics/major.json
       
      await octokit.request('GET /api/users/:login/statistics/major.json')
      -

      HTTP 请求

      +

      HTTP 请求

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

      -

      请求字段说明:

      +

      请求字段说明:

      参数
      @@ -3039,7 +3280,7 @@ Success — a happy kitten is an authenticated kitten!
      参数时间戳,结束时间,格式:1622131200
      -

      返回字段说明:

      +

      返回字段说明:

      @@ -3079,9 +3320,9 @@ Success — a happy kitten is an authenticated kitten!
      curl -X GET http://localhost:3000/api/users/yystopf/applied_messages.json
       
      await octokit.request('GET /api/users/:login/applied_messages.json')
      -

      HTTP 请求

      +

      HTTP 请求

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

      -

      请求字段说明:

      +

      请求字段说明:

      参数
      @@ -3095,7 +3336,7 @@ Success — a happy kitten is an authenticated kitten!
      参数用户标识
      -

      返回字段说明:

      +

      返回字段说明:

      @@ -3358,9 +3599,9 @@ Success — a happy kitten is an authenticated kitten!
      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 请求

      +

      HTTP 请求

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

      -

      请求字段说明:

      +

      请求字段说明:

      参数
      @@ -3374,7 +3615,7 @@ Success — a happy kitten is an authenticated kitten!
      参数用户标识
      -

      返回字段说明:

      +

      返回字段说明:

      @@ -3550,199 +3791,8 @@ Success — a happy kitten is an authenticated kitten!
      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

      +

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

      请求字段说明:

      @@ -3924,16 +3974,16 @@ Success — a happy kitten is an authenticated kitten! "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')
      +
      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_projects.json

      +

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

      请求字段说明:

      @@ -3947,6 +3997,11 @@ Success — a happy kitten is an authenticated kitten! + + + + +
      string 用户标识
      idint迁移id

      返回字段说明:

      @@ -3959,6 +4014,192 @@ Success — a happy kitten is an authenticated kitten! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      id int迁移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用户标识
      +

      返回字段说明:

      + + + + + + + + + + @@ -4092,9 +4333,9 @@ Success — a happy kitten is an authenticated kitten!
      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 请求

      +

      HTTP 请求

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

      -

      请求字段说明:

      +

      请求字段说明:

      参数类型字段说明
      idint 申请id
      @@ -4113,7 +4354,7 @@ Success — a happy kitten is an authenticated kitten!
      参数申请id
      -

      返回字段说明:

      +

      返回字段说明:

      @@ -4251,9 +4492,9 @@ Success — a happy kitten is an authenticated kitten!
      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 请求

      +

      HTTP 请求

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

      -

      请求字段说明:

      +

      请求字段说明:

      参数
      @@ -4272,7 +4513,7 @@ Success — a happy kitten is an authenticated kitten!
      参数申请id
      -

      返回字段说明:

      +

      返回字段说明:

      @@ -8715,10 +8956,10 @@ http://localhost:3000/api/yystopf/csfjkkj/contributors.json

      示例:

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

      HTTP 请求

      -

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

      +

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

      请求参数:

      参数
      @@ -8773,11 +9014,6 @@ http://localhost:3000/api/yystopf/ceshi/webhooks.json - - - - - @@ -8800,7 +9036,6 @@ http://localhost:3000/api/yystopf/ceshi/webhooks.json "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" }, @@ -8809,7 +9044,6 @@ http://localhost:3000/api/yystopf/ceshi/webhooks.json "url": "http://localhost:3000", "http_method": "GET", "is_active": true, - "type": "gitea", "last_status": "succeed", "create_time": "2021-07-26 10:03:45" }, @@ -8818,7 +9052,6 @@ http://localhost:3000/api/yystopf/ceshi/webhooks.json "url": "http://localhost:10081", "http_method": "POST", "is_active": true, - "type": "gitea", "last_status": "waiting", "create_time": "2021-07-26 16:56:53" }, @@ -8827,7 +9060,6 @@ http://localhost:3000/api/yystopf/ceshi/webhooks.json "url": "http://localhost:3001", "http_method": "POST", "is_active": true, - "type": "gitea", "last_status": "fail", "create_time": "2021-07-26 16:58:23" } @@ -8844,10 +9076,10 @@ http://localhost:3000/api/yystopf/ceshi/webhooks.json

      示例:

      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://localhost:3000/api/v1/yystopf/ceshi/webhooks/3.json
      +
      await octokit.request('GET /api/v1/yystopf/ceshi/webhooks/3.json')
       

      HTTP 请求

      -

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

      +

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

      请求参数:

      是否激活
      typestring类型
      last_status string 最后一次推送的状态
      @@ -8914,21 +9146,11 @@ http://localhost:3000/api/yystopf/ceshi/webhooks/3/edit.json - + - - - - - - - - - - @@ -8939,7 +9161,7 @@ http://localhost:3000/api/yystopf/ceshi/webhooks/3/edit.json - + @@ -8960,34 +9182,10 @@ http://localhost:3000/api/yystopf/ceshi/webhooks/3/edit.json - - - - - - - - - - - - - - - - - - - - - - - - @@ -8996,18 +9194,6 @@ http://localhost:3000/api/yystopf/ceshi/webhooks/3/edit.json - - - - - - - - - - - - @@ -9015,22 +9201,6 @@ http://localhost:3000/api/yystopf/ceshi/webhooks/3/edit.json - - - - - - - - - - - - - - - -
      string
      is_activeactive bool 是否激活
      typestring类型
      last_statusstring最后一次推送的状态, waiting 等待,fail 失败,succeed 成功
      branch_filter string 分支过滤触发条件
      create_timecreate_at string 创建时间
      分支或标签删除
      fork仓库被fork
      push git仓库推送
      issue疑修已打开、已关闭、已重新打开或编辑
      issue_assign疑修被指派
      issue_label疑修标签被更新或删除
      issue_milestone疑修被收入里程碑
      issue_comment疑修评论
      pull_request 合并请求
      合并请求被指派
      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版本发布
      @@ -9041,31 +9211,15 @@ http://localhost:3000/api/yystopf/ceshi/webhooks/3/edit.json "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", + "active": true, + "create_at": "2021-07-26 10:03", "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" ] } @@ -9079,10 +9233,10 @@ http://localhost:3000/api/yystopf/ceshi/webhooks/3/edit.json

      示例:

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

      HTTP 请求

      -

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

      +

      POST /api/v1/:owner/:repo/webhooks.json

      请求参数:

      @@ -9115,13 +9269,6 @@ http://localhost:3000/api/yystopf/ceshi/webhooks.json - - - - - - - @@ -9182,34 +9329,10 @@ http://localhost:3000/api/yystopf/ceshi/webhooks.json - - - - - - - - - - - - - - - - - - - - - - - - @@ -9218,18 +9341,6 @@ http://localhost:3000/api/yystopf/ceshi/webhooks.json - - - - - - - - - - - - @@ -9237,22 +9348,6 @@ http://localhost:3000/api/yystopf/ceshi/webhooks.json - - - - - - - - - - - - - - - -
      目标url
      webhook.typestring类型
      webhook.http_method 分支或标签删除
      fork仓库被fork
      push git仓库推送
      issue疑修已打开、已关闭、已重新打开或编辑
      issue_assign疑修被指派
      issue_label疑修标签被更新或删除
      issue_milestone疑修被收入里程碑
      issue_comment疑修评论
      pull_request 合并请求
      合并请求被指派
      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版本发布
      @@ -9316,15 +9411,22 @@ http://localhost:3000/api/yystopf/ceshi/webhooks.json

      返回的JSON示例:

      {
      -    "id": 18,
      -    "type": "gitea",
      +    "id": 68,
           "content_type": "json",
      -    "url": "http://localhost:10000",
      +    "http_method": "GET",
      +    "url": "http://127.0.0.1:3000",
           "events": [
      -        "push"
      +        "create",
      +        "delete",
      +        "push",
      +        "pull_request",
      +        "pull_request_assign",
      +        "pull_request_review_approved",
      +        "pull_request_review_rejected"
           ],
           "active": true,
      -    "create_time": "2021-07-26 18:53:43"
      +    "branch_filter": "*",
      +    "created_at": "2022-06-23 15:52"
       }