Merge pull request '20250225版本' (#346) from Trustie/forgeplus:trustie_server into master
This commit is contained in:
commit
377adf901f
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
# Ignore lock config file
|
# Ignore lock config file
|
||||||
*.log
|
*.log
|
||||||
|
.rubocop.yml
|
||||||
.env
|
.env
|
||||||
# mac
|
# mac
|
||||||
*.DS_Store
|
*.DS_Store
|
||||||
|
|
9
Gemfile
9
Gemfile
|
@ -1,5 +1,6 @@
|
||||||
#source 'https://gems.ruby-china.com'
|
#source 'https://gems.ruby-china.com'
|
||||||
source 'https://mirrors.cloud.tencent.com/rubygems/'
|
source 'https://mirrors.cloud.tencent.com/rubygems/'
|
||||||
|
#source 'https://rubygems.org'
|
||||||
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
||||||
|
|
||||||
gem 'rails', '~> 5.2.0'
|
gem 'rails', '~> 5.2.0'
|
||||||
|
@ -26,7 +27,7 @@ gem 'roo-xls'
|
||||||
gem 'simple_xlsx_reader', '~>1.0.4'
|
gem 'simple_xlsx_reader', '~>1.0.4'
|
||||||
|
|
||||||
gem 'rubyzip'
|
gem 'rubyzip'
|
||||||
|
gem 'sonarqube', :git => 'https://gitlink.org.cn/KingChan/sonarqube.git'
|
||||||
gem 'spreadsheet'
|
gem 'spreadsheet'
|
||||||
gem 'ruby-ole'
|
gem 'ruby-ole'
|
||||||
# 导出为xlsx
|
# 导出为xlsx
|
||||||
|
@ -70,6 +71,7 @@ group :development do
|
||||||
gem 'web-console', '>= 3.3.0'
|
gem 'web-console', '>= 3.3.0'
|
||||||
gem 'listen', '>= 3.0.5', '< 3.2'
|
gem 'listen', '>= 3.0.5', '< 3.2'
|
||||||
gem 'spring'
|
gem 'spring'
|
||||||
|
gem 'pry-rails'
|
||||||
gem 'spring-watcher-listen', '~> 2.0.0'
|
gem 'spring-watcher-listen', '~> 2.0.0'
|
||||||
gem "annotate", "~> 2.6.0"
|
gem "annotate", "~> 2.6.0"
|
||||||
end
|
end
|
||||||
|
@ -114,7 +116,6 @@ gem 'aasm'
|
||||||
gem 'enumerize'
|
gem 'enumerize'
|
||||||
|
|
||||||
gem 'diffy'
|
gem 'diffy'
|
||||||
|
|
||||||
gem 'deep_cloneable', '~> 3.0.0'
|
gem 'deep_cloneable', '~> 3.0.0'
|
||||||
|
|
||||||
# oauth2
|
# oauth2
|
||||||
|
@ -141,4 +142,6 @@ gem 'doorkeeper'
|
||||||
|
|
||||||
gem 'doorkeeper-jwt'
|
gem 'doorkeeper-jwt'
|
||||||
|
|
||||||
gem 'gitea-client', '~> 1.5.8'
|
gem 'gitea-client', '~> 1.6.1'
|
||||||
|
|
||||||
|
gem 'loofah', '~> 2.20.0'
|
24
Gemfile.lock
24
Gemfile.lock
|
@ -1,3 +1,11 @@
|
||||||
|
GIT
|
||||||
|
remote: https://gitlink.org.cn/KingChan/sonarqube.git
|
||||||
|
revision: 80f07d427322ef02c0714c77a382e87aed0bef81
|
||||||
|
specs:
|
||||||
|
sonarqube (1.3.0)
|
||||||
|
httparty (~> 0.14, >= 0.14.0)
|
||||||
|
terminal-table (~> 1.5, >= 1.5.1)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://mirrors.cloud.tencent.com/rubygems/
|
remote: https://mirrors.cloud.tencent.com/rubygems/
|
||||||
specs:
|
specs:
|
||||||
|
@ -135,7 +143,7 @@ GEM
|
||||||
fugit (1.4.1)
|
fugit (1.4.1)
|
||||||
et-orbi (~> 1.1, >= 1.1.8)
|
et-orbi (~> 1.1, >= 1.1.8)
|
||||||
raabro (~> 1.4)
|
raabro (~> 1.4)
|
||||||
gitea-client (1.4.2)
|
gitea-client (1.4.6)
|
||||||
rest-client (~> 2.1.0)
|
rest-client (~> 2.1.0)
|
||||||
globalid (0.4.2)
|
globalid (0.4.2)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
|
@ -150,6 +158,9 @@ GEM
|
||||||
http-accept (1.7.0)
|
http-accept (1.7.0)
|
||||||
http-cookie (1.0.5)
|
http-cookie (1.0.5)
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
|
httparty (0.21.0)
|
||||||
|
mini_mime (>= 1.0.0)
|
||||||
|
multi_xml (>= 0.5.2)
|
||||||
i18n (1.8.2)
|
i18n (1.8.2)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
io-like (0.3.1)
|
io-like (0.3.1)
|
||||||
|
@ -187,9 +198,9 @@ GEM
|
||||||
mimemagic (~> 0.3.2)
|
mimemagic (~> 0.3.2)
|
||||||
maruku (0.7.3)
|
maruku (0.7.3)
|
||||||
method_source (0.9.2)
|
method_source (0.9.2)
|
||||||
mime-types (3.4.1)
|
mime-types (3.5.2)
|
||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2023.0218.1)
|
mime-types-data (3.2024.0507)
|
||||||
mimemagic (0.3.10)
|
mimemagic (0.3.10)
|
||||||
nokogiri (~> 1)
|
nokogiri (~> 1)
|
||||||
rake
|
rake
|
||||||
|
@ -437,6 +448,8 @@ GEM
|
||||||
actionpack (>= 4.0)
|
actionpack (>= 4.0)
|
||||||
activesupport (>= 4.0)
|
activesupport (>= 4.0)
|
||||||
sprockets (>= 3.0.0)
|
sprockets (>= 3.0.0)
|
||||||
|
terminal-table (1.8.0)
|
||||||
|
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||||
thor (1.0.1)
|
thor (1.0.1)
|
||||||
thread_safe (0.3.6)
|
thread_safe (0.3.6)
|
||||||
tilt (2.0.10)
|
tilt (2.0.10)
|
||||||
|
@ -449,7 +462,7 @@ GEM
|
||||||
execjs (>= 0.3.0, < 3)
|
execjs (>= 0.3.0, < 3)
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
unf_ext (0.0.8.2)
|
unf_ext (0.0.9.1)
|
||||||
unicode-display_width (1.6.1)
|
unicode-display_width (1.6.1)
|
||||||
web-console (3.7.0)
|
web-console (3.7.0)
|
||||||
actionview (>= 5.0)
|
actionview (>= 5.0)
|
||||||
|
@ -491,7 +504,7 @@ DEPENDENCIES
|
||||||
enumerize
|
enumerize
|
||||||
faraday (~> 0.15.4)
|
faraday (~> 0.15.4)
|
||||||
font-awesome-sass (= 4.7.0)
|
font-awesome-sass (= 4.7.0)
|
||||||
gitea-client (~> 1.4.2)
|
gitea-client (~> 1.4.3)
|
||||||
grape-entity (~> 0.7.1)
|
grape-entity (~> 0.7.1)
|
||||||
groupdate (~> 4.1.0)
|
groupdate (~> 4.1.0)
|
||||||
harmonious_dictionary (~> 0.0.1)
|
harmonious_dictionary (~> 0.0.1)
|
||||||
|
@ -539,6 +552,7 @@ DEPENDENCIES
|
||||||
simple_xlsx_reader (~> 1.0.4)
|
simple_xlsx_reader (~> 1.0.4)
|
||||||
sinatra
|
sinatra
|
||||||
solargraph (~> 0.38.0)
|
solargraph (~> 0.38.0)
|
||||||
|
sonarqube!
|
||||||
spreadsheet
|
spreadsheet
|
||||||
spring
|
spring
|
||||||
spring-watcher-listen (~> 2.0.0)
|
spring-watcher-listen (~> 2.0.0)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
// All this logic will automatically be available in application.js.
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
// All this logic will automatically be available in application.js.
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
// All this logic will automatically be available in application.js.
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
// All this logic will automatically be available in application.js.
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
// All this logic will automatically be available in application.js.
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
// All this logic will automatically be available in application.js.
|
|
@ -40,6 +40,13 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.editormd .CodeMirror{
|
||||||
|
margin-top: 35px!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-gutter .CodeMirror-linenumbers {
|
||||||
|
width: 28px!important;
|
||||||
|
}
|
||||||
|
|
||||||
input.form-control {
|
input.form-control {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the api/pm/issue_links controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the api/pm/projects controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the api/v1/pm_issues controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the api/v1/sonarqube/issues controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the api/v1/sonarqubes controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the pm/journals controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
|
@ -1,6 +1,7 @@
|
||||||
class AccountsController < ApplicationController
|
class AccountsController < ApplicationController
|
||||||
before_action :require_login, only: [:login_check, :simple_update]
|
before_action :require_login, only: [:login_check, :simple_update, :change_password]
|
||||||
include ApplicationHelper
|
include ApplicationHelper
|
||||||
|
include AesCryptHelper
|
||||||
|
|
||||||
#skip_before_action :check_account, :only => [:logout]
|
#skip_before_action :check_account, :only => [:logout]
|
||||||
|
|
||||||
|
@ -143,7 +144,8 @@ class AccountsController < ApplicationController
|
||||||
|
|
||||||
user = Users::RegisterService.call(register_params)
|
user = Users::RegisterService.call(register_params)
|
||||||
user.mail = "#{user.login}@example.org" if user.mail.blank?
|
user.mail = "#{user.login}@example.org" if user.mail.blank?
|
||||||
password = register_params[:password].strip
|
password = decrypt(register_params[:password]) rescue ""
|
||||||
|
password = password.strip
|
||||||
|
|
||||||
# gitea用户注册, email, username, password
|
# gitea用户注册, email, username, password
|
||||||
interactor = Gitea::RegisterInteractor.call({username: user.login, email: user.mail, password: password})
|
interactor = Gitea::RegisterInteractor.call({username: user.login, email: user.mail, password: password})
|
||||||
|
@ -193,8 +195,9 @@ class AccountsController < ApplicationController
|
||||||
|
|
||||||
# 用户登录
|
# 用户登录
|
||||||
def login
|
def login
|
||||||
Users::LoginForm.new(login_params).validate!
|
password = decrypt(login_params[:password]) rescue ""
|
||||||
@user = User.try_to_login(params[:login], params[:password])
|
Users::LoginForm.new(login_params.merge!({password: password})).validate!
|
||||||
|
@user = User.try_to_login(params[:login], password)
|
||||||
|
|
||||||
return normal_status(-2, "错误的账号或密码") if @user.blank?
|
return normal_status(-2, "错误的账号或密码") if @user.blank?
|
||||||
# user is already in local database
|
# user is already in local database
|
||||||
|
@ -203,7 +206,7 @@ class AccountsController < ApplicationController
|
||||||
login_control = LimitForbidControl::UserLogin.new(@user)
|
login_control = LimitForbidControl::UserLogin.new(@user)
|
||||||
return normal_status(-2, "登录密码出错已达上限,账号已被锁定,请#{login_control.forbid_expires/60}分钟后重新登录或找回密码") if login_control.forbid?
|
return normal_status(-2, "登录密码出错已达上限,账号已被锁定,请#{login_control.forbid_expires/60}分钟后重新登录或找回密码") if login_control.forbid?
|
||||||
|
|
||||||
password_ok = @user.check_password?(params[:password].to_s)
|
password_ok = @user.check_password?(password.to_s)
|
||||||
unless password_ok
|
unless password_ok
|
||||||
if login_control.remain_times-1 == 0
|
if login_control.remain_times-1 == 0
|
||||||
normal_status(-2, "登录密码出错已达上限,账号已被锁定,请#{login_control.forbid_expires/60}分钟后重新登录或找回密码")
|
normal_status(-2, "登录密码出错已达上限,账号已被锁定,请#{login_control.forbid_expires/60}分钟后重新登录或找回密码")
|
||||||
|
@ -216,20 +219,24 @@ class AccountsController < ApplicationController
|
||||||
|
|
||||||
LimitForbidControl::UserLogin.new(@user).clear
|
LimitForbidControl::UserLogin.new(@user).clear
|
||||||
successful_authentication(@user)
|
successful_authentication(@user)
|
||||||
sync_pwd_to_gitea!(@user, {password: params[:password].to_s}) # TODO用户密码未同步
|
sync_pwd_to_gitea!(@user, {password: password.to_s}) # TODO用户密码未同步
|
||||||
|
|
||||||
# session[:user_id] = @user.id
|
# session[:user_id] = @user.id
|
||||||
end
|
end
|
||||||
|
|
||||||
def change_password
|
def change_password
|
||||||
return render_error("两次输入的密码不一致") if params[:password].to_s != params[:new_password_repeat].to_s
|
password = decrypt(params[:password]) rescue ""
|
||||||
|
new_password_repeat = decrypt(params[:new_password_repeat]) rescue ""
|
||||||
|
old_password = decrypt(params[:old_password]) rescue ""
|
||||||
|
return render_error("两次输入的密码不一致") if password.to_s != new_password_repeat.to_s
|
||||||
@user = User.find_by(login: params[:login])
|
@user = User.find_by(login: params[:login])
|
||||||
|
return render_forbidden unless User.current.login == @user&.login
|
||||||
return render_error("此用户禁止修改密码!") if @user.id.to_i === 104691
|
return render_error("此用户禁止修改密码!") if @user.id.to_i === 104691
|
||||||
return render_error("未找到相关用户!") if @user.blank?
|
return render_error("未找到相关用户!") if @user.blank?
|
||||||
return render_error("旧密码不正确") unless @user.check_password?(params[:old_password])
|
return render_error("旧密码不正确") unless @user.check_password?(old_password)
|
||||||
|
|
||||||
sync_params = {
|
sync_params = {
|
||||||
password: params[:password].to_s,
|
password: password.to_s,
|
||||||
email: @user.mail,
|
email: @user.mail,
|
||||||
login_name: @user.name,
|
login_name: @user.name,
|
||||||
source_id: 0
|
source_id: 0
|
||||||
|
@ -237,7 +244,7 @@ class AccountsController < ApplicationController
|
||||||
|
|
||||||
interactor = Gitea::User::UpdateInteractor.call(@user.login, sync_params)
|
interactor = Gitea::User::UpdateInteractor.call(@user.login, sync_params)
|
||||||
if interactor.success?
|
if interactor.success?
|
||||||
@user.update_attribute(:password, params[:password])
|
@user.update_attribute(:password, password)
|
||||||
render_ok
|
render_ok
|
||||||
else
|
else
|
||||||
render_error(interactor.error)
|
render_error(interactor.error)
|
||||||
|
|
|
@ -6,7 +6,7 @@ class Action::NodeInputsController < ApplicationController
|
||||||
@node_inputs = @node.action_node_inputs
|
@node_inputs = @node.action_node_inputs
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html
|
format.html
|
||||||
format.json
|
format.json{ render_ok(data: @node_inputs.as_json) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,10 @@ class Action::NodeTypesController < ApplicationController
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@node_types = Action::NodeType.all
|
@node_types = Action::NodeType.all
|
||||||
|
respond_to do |format|
|
||||||
|
format.html
|
||||||
|
format.json { render_ok(data: @node_types.as_json) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -20,7 +24,10 @@ class Action::NodeTypesController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
respond_to do |format|
|
||||||
|
format.html
|
||||||
|
format.json { render_ok(data: @node_type.as_json) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
|
@ -45,7 +52,10 @@ class Action::NodeTypesController < ApplicationController
|
||||||
else
|
else
|
||||||
flash[:danger] = '删除失败'
|
flash[:danger] = '删除失败'
|
||||||
end
|
end
|
||||||
redirect_to action_node_types_path
|
respond_to do |format|
|
||||||
|
format.html { redirect_to action_node_types_path }
|
||||||
|
format.json { render_ok }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -1,18 +1,25 @@
|
||||||
class Action::NodesController < ApplicationController
|
class Action::NodesController < ApplicationController
|
||||||
before_action :require_admin, except: [:index]
|
# before_action :require_admin, except: [:index]
|
||||||
|
before_action :require_login
|
||||||
before_action :find_action_node, except: [:index, :create, :new]
|
before_action :find_action_node, except: [:index, :create, :new]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@node_types = Action::NodeType.all
|
@node_types = Action::NodeType.all
|
||||||
|
no_node_type = Action::NodeType.find_by(name: "未分类")
|
||||||
@no_type_nodes = Action::Node.where(action_node_types_id: nil)
|
@no_type_nodes = Action::Node.where(action_node_types_id: nil)
|
||||||
|
@no_type_nodes = Action::Node.where(action_node_types_id: nil).or(Action::Node.where(action_node_types_id: no_node_type.id)) if no_node_type.present?
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { @nodes = Action::Node.all }
|
format.html { @nodes = Action::Node.where("name LIKE :search OR full_name LIKE :search", :search => "%#{params[:search]}%") }
|
||||||
format.json
|
format.json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@node = Action::Node.new(node_params)
|
@node = Action::Node.new(node_params)
|
||||||
|
if params.require(:node).present? && params.require(:node)[:link_type_array].present?
|
||||||
|
@node.link_type = (params.require(:node)[:link_type_array] - [""]).join(",")
|
||||||
|
end
|
||||||
|
@node.user_id = current_user.id
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @node.save
|
if @node.save
|
||||||
format.html { redirect_to action_nodes_path, notice: '创建成功.' }
|
format.html { redirect_to action_nodes_path, notice: '创建成功.' }
|
||||||
|
@ -33,10 +40,16 @@ class Action::NodesController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
|
if @node.link_type.present?
|
||||||
|
@node.link_type_array = @node.link_type.to_s.split(",")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
|
if params.require(:node).present? && params.require(:node)[:link_type_array].present?
|
||||||
|
@node.link_type = (params.require(:node)[:link_type_array] - [""]).join(",")
|
||||||
|
end
|
||||||
|
@node.user_id = current_user.id if @node.user_id.blank?
|
||||||
@node.update(node_params)
|
@node.update(node_params)
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to action_nodes_path, notice: '更新成功.' }
|
format.html { redirect_to action_nodes_path, notice: '更新成功.' }
|
||||||
|
@ -50,7 +63,10 @@ class Action::NodesController < ApplicationController
|
||||||
else
|
else
|
||||||
flash[:danger] = '删除失败'
|
flash[:danger] = '删除失败'
|
||||||
end
|
end
|
||||||
redirect_to action_nodes_path
|
respond_to do |format|
|
||||||
|
format.html { redirect_to action_nodes_path }
|
||||||
|
format.json { render_ok() }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -61,9 +77,11 @@ class Action::NodesController < ApplicationController
|
||||||
|
|
||||||
def node_params
|
def node_params
|
||||||
if params.require(:action_node)
|
if params.require(:action_node)
|
||||||
params.require(:action_node).permit(:name, :full_name, :description, :icon, :action_node_types_id, :is_local, :local_url, :yaml, :sort_no)
|
params.require(:action_node).permit(:name, :label, :full_name, :description, :icon, :action_node_types_id,
|
||||||
|
:is_local, :local_url, :yaml, :sort_no, :node_type, :is_mutil_link, :link_type, :link_type_array)
|
||||||
else
|
else
|
||||||
params.permit(:name, :full_name, :description, :icon, :action_node_types_id, :is_local, :local_url, :yaml, :sort_no)
|
params.permit(:name, :label, :full_name, :description, :icon, :action_node_types_id, :is_local, :local_url,
|
||||||
|
:yaml, :sort_no, :node_type, :is_mutil_link, :link_type, :link_type_array)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -49,7 +49,10 @@ class Action::TemplatesController < ApplicationController
|
||||||
else
|
else
|
||||||
flash[:danger] = '删除失败'
|
flash[:danger] = '删除失败'
|
||||||
end
|
end
|
||||||
redirect_to action_templates_path
|
respond_to do |format|
|
||||||
|
format.html { redirect_to action_templates_path }
|
||||||
|
format.json { render_ok }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -73,26 +73,26 @@ class Admins::DashboardsController < Admins::BaseController
|
||||||
@subject_icon = ["fa-user","fa-git", "fa-sitemap", "fa-warning", "fa-comments", "fa-share-alt", "fa-upload"]
|
@subject_icon = ["fa-user","fa-git", "fa-sitemap", "fa-warning", "fa-comments", "fa-share-alt", "fa-upload"]
|
||||||
@subject_data = [@user_count, @project_count, @organization_count, @issue_count, @comment_count, @pr_count, @commit_count]
|
@subject_data = [@user_count, @project_count, @organization_count, @issue_count, @comment_count, @pr_count, @commit_count]
|
||||||
|
|
||||||
|
if EduSetting.get("open_baidu_tongji").to_s == "true"
|
||||||
tongji_service = Baidu::TongjiService.new
|
tongji_service = Baidu::TongjiService.new
|
||||||
@access_token = tongji_service.access_token
|
@access_token = tongji_service.access_token
|
||||||
Rails.logger.info "baidu_tongji_auth access_token ===== #{@access_token}"
|
Rails.logger.info "baidu_tongji_auth access_token ===== #{@access_token}"
|
||||||
# @overview_data = tongji_service.api_overview
|
# @overview_data = tongji_service.api_overview
|
||||||
last_date = DailyPlatformStatistic.order(:date).last
|
last_date = DailyPlatformStatistic.order(:date).last || Time.now
|
||||||
start_date = last_date.date
|
start_date = last_date.date
|
||||||
end_date = Time.now
|
end_date = Time.now
|
||||||
if @access_token.present?
|
if @access_token.present?
|
||||||
@overview_data = Rails.cache.fetch("dashboardscontroller:baidu_tongji:overview_data", expires_in: 10.minutes) do
|
@overview_data = Rails.cache.fetch("dashboardscontroller:baidu_tongji:overview_data", expires_in: 10.minutes) do
|
||||||
tongji_service.source_from_batch_add(start_date, end_date)
|
tongji_service.source_from_batch_add(start_date, end_date)
|
||||||
@overview_data = tongji_service.overview_batch_add(start_date, end_date)
|
@overview_data = tongji_service.overview_batch_add(start_date, end_date)
|
||||||
@overview_data
|
@overview_data
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@current_week_statistic = DailyPlatformStatistic.where(date: current_week)
|
||||||
|
@pre_week_statistic = DailyPlatformStatistic.where(date: pre_week)
|
||||||
end
|
end
|
||||||
|
|
||||||
@current_week_statistic = DailyPlatformStatistic.where(date: current_week)
|
|
||||||
@pre_week_statistic = DailyPlatformStatistic.where(date: pre_week)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ class Admins::NpsController < Admins::BaseController
|
||||||
before_action :require_business
|
before_action :require_business
|
||||||
def index
|
def index
|
||||||
@on_off_switch = EduSetting.get("nps-on-off-switch").to_s == 'true'
|
@on_off_switch = EduSetting.get("nps-on-off-switch").to_s == 'true'
|
||||||
@user_nps = UserNp.joins(:user).order(created_at: :desc)
|
@user_nps = UserNp.order(created_at: :desc)
|
||||||
keyword = params[:keyword].to_s.strip.presence
|
keyword = params[:keyword].to_s.strip.presence
|
||||||
if keyword
|
if keyword
|
||||||
sql = 'CONCAT(users.lastname, users.firstname) LIKE :keyword OR users.nickname LIKE :keyword OR users.login LIKE :keyword OR users.mail LIKE :keyword OR users.phone LIKE :keyword'
|
sql = 'CONCAT(users.lastname, users.firstname) LIKE :keyword OR users.nickname LIKE :keyword OR users.login LIKE :keyword OR users.mail LIKE :keyword OR users.phone LIKE :keyword'
|
||||||
|
|
|
@ -6,7 +6,7 @@ class Admins::ProjectsController < Admins::BaseController
|
||||||
sort_by = Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_on'
|
sort_by = Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_on'
|
||||||
sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc'
|
sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc'
|
||||||
search = params[:search].to_s.strip
|
search = params[:search].to_s.strip
|
||||||
projects = Project.where("name like ? OR identifier LIKE ?", "%#{search}%", "%#{search}%").order("#{sort_by} #{sort_direction}")
|
projects = Project.where("id = ? OR name like ? OR identifier LIKE ?", search, "%#{search}%", "%#{search}%").order("#{sort_by} #{sort_direction}")
|
||||||
case params[:category]
|
case params[:category]
|
||||||
when 'public'
|
when 'public'
|
||||||
projects = projects.where(is_public: true)
|
projects = projects.where(is_public: true)
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
class Admins::UserActionsController < Admins::BaseController
|
||||||
|
before_action :require_admin
|
||||||
|
|
||||||
|
def index
|
||||||
|
@user_actions = UserAction.order(created_at: :desc)
|
||||||
|
@user_actions = @user_actions.where(action_type: params[:action_type]) if params[:action_type].present?
|
||||||
|
keyword = params[:keyword].to_s.strip.presence
|
||||||
|
if keyword
|
||||||
|
sql = 'login LIKE :keyword OR phone LIKE :keyword OR email LIKE :keyword'
|
||||||
|
@user_actions = @user_actions.where(sql, keyword: "%#{keyword}%")
|
||||||
|
end
|
||||||
|
@user_actions = paginate @user_actions
|
||||||
|
end
|
||||||
|
end
|
|
@ -27,10 +27,30 @@ class Admins::UsersController < Admins::BaseController
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
UserAction.create(action_id: @user.id, action_type: "DestroyUser", user_id: current_user.id, :ip => request.remote_ip, data_bank: @user.attributes.to_json)
|
UserAction.create(action_id: @user.id, action_type: "DestroyUser", user_id: current_user.id, :ip => request.remote_ip, data_bank: @user.attributes.to_json)
|
||||||
@user.destroy!
|
# org_ids = TeamUser.where(user_id: @user.id).pluck(:organization_id) | OrganizationUser.where(user_id: @user.id).pluck(:organization_id)
|
||||||
Gitea::User::DeleteService.call(@user.login)
|
# organizations = Organization.where(id: org_ids)
|
||||||
|
# organizations.each do |org|
|
||||||
render_delete_success
|
# # org.team_users.joins(:team).where(user_id: @user.id, teams: {authorize: %w(owner)})
|
||||||
|
# owner_count = org.team_users.joins(:team).where(teams: {authorize: %w(owner)}).count
|
||||||
|
# # 多个owner时,仅将用户从组织移除, 一个时直接删除
|
||||||
|
# if owner_count > 1
|
||||||
|
# org.team_users.joins(:team).where(user_id: @user.id, teams: {authorize: %w(owner)}).destroy_all
|
||||||
|
# org.organization_users.where(user_id: @user.id, organization_id: org.id).destroy_all
|
||||||
|
# else
|
||||||
|
# org.destroy
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# @user.destroy!
|
||||||
|
# Gitea::User::DeleteService.call(@user.login, true)
|
||||||
|
#
|
||||||
|
# render_delete_success
|
||||||
|
|
||||||
|
@result_object = Api::V1::Users::DeleteUserService.call(@user)
|
||||||
|
if @result_object
|
||||||
|
render_delete_success
|
||||||
|
else
|
||||||
|
render_js_error('删除失败!')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def lock
|
def lock
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
class Api::Pm::ActionRunsController < Api::Pm::BaseController
|
||||||
|
before_action :require_login
|
||||||
|
|
||||||
|
def index
|
||||||
|
tip_exception('请输入workflows') if params[:workflows].blank?
|
||||||
|
@owner = Owner.find_by(login: params[:owner_id].to_s) || Owner.find_by(id: params[:owner_id].to_s)
|
||||||
|
tip_exception('组织未找到') if @owner.blank?
|
||||||
|
action_runs = Gitea::ActionRun.where(owner_id: @owner.gitea_uid)
|
||||||
|
group_data = action_runs.where(workflow_id: params[:workflows].to_s.split(",")).where(status: [1,2]).group(:workflow_id, :status).count
|
||||||
|
@result = []
|
||||||
|
params[:workflows].to_s.split(",").each do |file|
|
||||||
|
last_action_run = action_runs.where(workflow_id: file).order(updated: :desc).first
|
||||||
|
last_action_run_json = last_action_run.present? ? {
|
||||||
|
id: last_action_run.id,
|
||||||
|
schedule: last_action_run.schedule_id > 0,
|
||||||
|
title: last_action_run.title,
|
||||||
|
index: last_action_run.index,
|
||||||
|
status: last_action_run.status,
|
||||||
|
started: last_action_run.started,
|
||||||
|
stopped: last_action_run.stopped,
|
||||||
|
length: last_action_run.stopped - last_action_run.started,
|
||||||
|
created: last_action_run.created,
|
||||||
|
updated: last_action_run.updated,
|
||||||
|
} : {}
|
||||||
|
|
||||||
|
total = 0
|
||||||
|
success = 0
|
||||||
|
failure = 0
|
||||||
|
group_data.each do |k,v|
|
||||||
|
total += v if k[0] == file
|
||||||
|
success += v if k[0] == file && k[1] == 1
|
||||||
|
failure += v if k[0] == file && k[1] == 2
|
||||||
|
end
|
||||||
|
|
||||||
|
@result << {
|
||||||
|
name: file,
|
||||||
|
total: total,
|
||||||
|
success: success,
|
||||||
|
failure: failure
|
||||||
|
}.merge(last_action_run_json)
|
||||||
|
end
|
||||||
|
render :json => {data: @result}
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,60 @@
|
||||||
|
class Api::Pm::BaseController < ApplicationController
|
||||||
|
|
||||||
|
include Api::ProjectHelper
|
||||||
|
include Api::UserHelper
|
||||||
|
include Api::PullHelper
|
||||||
|
|
||||||
|
# before_action :doorkeeper_authorize!
|
||||||
|
# skip_before_action :user_setup
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def kaminary_select_paginate(relation)
|
||||||
|
limit = params[:limit] || params[:per_page]
|
||||||
|
limit = (limit.to_i.zero? || limit.to_i > 200) ? 200 : limit.to_i
|
||||||
|
page = params[:page].to_i.zero? ? 1 : params[:page].to_i
|
||||||
|
|
||||||
|
relation.page(page).per(limit)
|
||||||
|
end
|
||||||
|
|
||||||
|
def limit
|
||||||
|
params.fetch(:limit, 15)
|
||||||
|
end
|
||||||
|
|
||||||
|
def page
|
||||||
|
params.fetch(:page, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_project
|
||||||
|
@project = Project.find_by_id(params[:project_id]) || Project.new(id: 0, user_id: 0, name: 'pm_mm', identifier: 'pm_mm', is_public:true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_issue
|
||||||
|
return render_parameter_missing if params[:pm_project_id].blank?
|
||||||
|
@issue = Issue.issue_issue.where(pm_project_id: params[:pm_project_id]).find_by_id(params[:issue_id])
|
||||||
|
render_not_found('疑修不存在!') if @issue.blank?
|
||||||
|
end
|
||||||
|
# 具有对仓库的管理权限
|
||||||
|
def require_manager_above
|
||||||
|
@project = load_project
|
||||||
|
return render_forbidden if !current_user.admin? && !@project.manager?(current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
# 具有对仓库的操作权限
|
||||||
|
def require_operate_above
|
||||||
|
@project = load_project
|
||||||
|
return render_forbidden if !current_user.admin? && !@project.operator?(current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
# 具有仓库的操作权限或者fork仓库的操作权限
|
||||||
|
def require_operate_above_or_fork_project
|
||||||
|
@project = load_project
|
||||||
|
return render_forbidden if !current_user.admin? && !@project.operator?(current_user) && !(@project.fork_project.present? && @project.fork_project.operator?(current_user))
|
||||||
|
end
|
||||||
|
|
||||||
|
# 具有对仓库的访问权限
|
||||||
|
def require_public_and_member_above
|
||||||
|
@project = load_project
|
||||||
|
return render_forbidden if !@project.is_public && !current_user.admin? && !@project.member?(current_user)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,137 @@
|
||||||
|
class Api::Pm::DashboardsController < Api::Pm::BaseController
|
||||||
|
before_action :require_login
|
||||||
|
def index
|
||||||
|
end
|
||||||
|
|
||||||
|
def todo
|
||||||
|
return render_error('请输入正确的pm_project_ids.') if params[:pm_project_ids].blank?
|
||||||
|
pm_project_ids = params[:pm_project_ids].split(",") rescue []
|
||||||
|
date = params[:date].present? ? params[:date].to_date : Date.today rescue Date.today
|
||||||
|
@issues = Issue.where("start_date <= ? and due_date >= ?", date, date)
|
||||||
|
@issues = @issues.where(pm_project_id: pm_project_ids).joins(:issue_participants).where(issue_participants: {participant_id: current_user.id, participant_type: 'assigned'})
|
||||||
|
@issues = @issues.where.not(status_id: 5)
|
||||||
|
@issues = kaminari_paginate(@issues.distinct.pm_includes)
|
||||||
|
end
|
||||||
|
|
||||||
|
def my_issues
|
||||||
|
return render_error('请输入正确的pm_project_ids.') if params[:pm_project_ids].blank?
|
||||||
|
return render_error('请输入正确的pm_issue_types.') if params[:pm_issue_types].blank?
|
||||||
|
pm_project_ids = params[:pm_project_ids].split(",") rescue []
|
||||||
|
pm_issue_types = params[:pm_issue_types].split(",") rescue []
|
||||||
|
@all_issues = Issue.where(pm_project_id: pm_project_ids, pm_issue_type: pm_issue_types)
|
||||||
|
@issues = @all_issues.joins(:issue_participants).where(issue_participants: {participant_id: current_user.id})
|
||||||
|
|
||||||
|
@issues = kaminari_paginate(@issues.distinct.pm_includes)
|
||||||
|
@my_assign_requirements_count = @all_issues.where(pm_issue_type: 1).joins(:issue_participants).where(issue_participants: {participant_id: current_user.id, participant_type: 'assigned'}).size
|
||||||
|
@my_assign_tasks_count = @all_issues.where(pm_issue_type: 2).joins(:issue_participants).where(issue_participants: {participant_id: current_user.id, participant_type: 'assigned'}).size
|
||||||
|
@my_assign_bugs_count = @all_issues.where(pm_issue_type: 3).joins(:issue_participants).where(issue_participants: {participant_id: current_user.id, participant_type: 'assigned'}).size
|
||||||
|
end
|
||||||
|
|
||||||
|
def my_pm_projects
|
||||||
|
return render_error('请输入正确的pm_project_id.') if params[:pm_project_id].blank?
|
||||||
|
@all_issues = Issue.where(pm_project_id: params[:pm_project_id])
|
||||||
|
time_now = Time.now
|
||||||
|
@last_week_create_issues_count = @all_issues.where("created_on > ? and created_on < ?", time_now - 7.days, time_now).size
|
||||||
|
@before_last_week_create_issue_count = @all_issues.where("created_on > ? and created_on < ?", time_now - 14.days, time_now - 7.days).size
|
||||||
|
@compare_last_week_create_issues = @before_last_week_create_issue_count.zero? ? 0 :(@last_week_create_issues_count - @before_last_week_create_issue_count).to_f / @before_last_week_create_issue_count rescue 0
|
||||||
|
@last_week_close_issues_count = @all_issues.where(status_id: 5).where("updated_on > ? and updated_on < ?", time_now - 7.days, time_now).size
|
||||||
|
@before_last_week_close_issue_count = @all_issues.where(status_id: 5).where("updated_on > ? and updated_on < ?", time_now - 14.days, time_now - 7.days).size
|
||||||
|
@compare_last_week_close_issues = @before_last_week_close_issue_count.zero? ? 0 :(@last_week_close_issues_count - @before_last_week_close_issue_count).to_f / @before_last_week_close_issue_count rescue 0
|
||||||
|
@all_requirement_issues_count = @all_issues.where(pm_issue_type: 1).size
|
||||||
|
@open_requirement_issues_count = @all_issues.where(pm_issue_type: 1).where.not(status_id: 5).size
|
||||||
|
@last_week_close_requirement_issues_count = @all_issues.where(pm_issue_type: 1).where(status_id: 5).where("updated_on > ? and updated_on < ?", time_now - 7.days, time_now).size
|
||||||
|
@last_month_close_requirement_issues_count = @all_issues.where(pm_issue_type: 1).where(status_id: 5).where("updated_on > ? and updated_on < ?", time_now - 30.days, time_now).size
|
||||||
|
@all_task_issues_count = @all_issues.where(pm_issue_type: 2).size
|
||||||
|
@open_task_issues_count = @all_issues.where(pm_issue_type: 2).where.not(status_id: 5).size
|
||||||
|
@last_week_close_tast_issues_count = @all_issues.where(pm_issue_type: 2).where(status_id: 5).where("updated_on > ? and updated_on < ?", time_now - 7.days, time_now).size
|
||||||
|
@last_month_close_task_issues_count = @all_issues.where(pm_issue_type: 2).where(status_id: 5).where("updated_on > ? and updated_on < ?", time_now - 30.days, time_now).size
|
||||||
|
@all_bug_issues_count = @all_issues.where(pm_issue_type: 3).size
|
||||||
|
@open_bug_issues_count = @all_issues.where(pm_issue_type: 3).where.not(status_id: 5).size
|
||||||
|
@last_week_close_bug_issues_count = @all_issues.where(pm_issue_type: 3).where(status_id: 5).where("updated_on > ? and updated_on < ?", time_now - 7.days, time_now).size
|
||||||
|
@last_month_close_bug_issues_count = @all_issues.where(pm_issue_type: 3).where(status_id: 5).where("updated_on > ? and updated_on < ?", time_now - 30.days, time_now).size
|
||||||
|
|
||||||
|
|
||||||
|
@requirement_close_trend = [[],[]]
|
||||||
|
@task_close_trend = [[],[]]
|
||||||
|
@bug_close_trend = [[],[]]
|
||||||
|
((time_now-29.days).to_date..time_now.to_date).to_a.each do |i|
|
||||||
|
@requirement_close_trend[0] << i.strftime("%Y.%m.%d")
|
||||||
|
@task_close_trend[0] << i.strftime("%Y.%m.%d")
|
||||||
|
@bug_close_trend[0] << i.strftime("%Y.%m.%d")
|
||||||
|
@requirement_close_trend[1] << @all_issues.where(pm_issue_type: 1, status_id: 5).where("DATE(updated_on) = ?", i).size
|
||||||
|
@task_close_trend[1] << @all_issues.where(pm_issue_type: 2, status_id: 5).where("DATE(updated_on) = ?", i).size
|
||||||
|
@bug_close_trend[1] << @all_issues.where(pm_issue_type: 3, status_id: 5).where("DATE(updated_on) = ?", i).size
|
||||||
|
end
|
||||||
|
@close_trend = {requirement: @requirement_close_trend, task: @task_close_trend, bug: @bug_close_trend}
|
||||||
|
|
||||||
|
render_ok(data: {
|
||||||
|
last_week_close_issues_count: @last_week_close_issues_count,
|
||||||
|
before_last_week_close_issue_count: @before_last_week_close_issue_count,
|
||||||
|
compare_last_week_close_issues: @compare_last_week_close_issues,
|
||||||
|
last_week_create_issues_count: @last_week_create_issues_count,
|
||||||
|
before_last_week_create_issue_count: @before_last_week_create_issue_count,
|
||||||
|
compare_last_week_create_issues: @compare_last_week_create_issues,
|
||||||
|
all_requirement_issues_count: @all_requirement_issues_count,
|
||||||
|
open_requirement_issues_count: @open_requirement_issues_count,
|
||||||
|
last_week_close_requirement_issues_count: @last_week_close_requirement_issues_count,
|
||||||
|
last_month_close_requirement_issues_count: @last_month_close_requirement_issues_count,
|
||||||
|
all_task_issues_count: @all_task_issues_count,
|
||||||
|
open_task_issues_count: @open_task_issues_count,
|
||||||
|
last_week_close_task_issues_count: @last_week_close_tast_issues_count,
|
||||||
|
last_month_close_task_issues_count: @last_month_close_task_issues_count,
|
||||||
|
all_bug_issues_count: @all_bug_issues_count,
|
||||||
|
open_bug_issues_count: @open_bug_issues_count,
|
||||||
|
last_week_close_bug_issues_count: @last_week_close_bug_issues_count,
|
||||||
|
last_month_close_bug_issues_count: @last_month_close_bug_issues_count,
|
||||||
|
close_trend: @close_trend
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def my_projects
|
||||||
|
return render_error('请输入正确的project_id.') if params[:project_id].blank?
|
||||||
|
|
||||||
|
@project = Project.find_by_id params[:project_id]
|
||||||
|
return render_error('请输入正确的project_id.') unless @project.present?
|
||||||
|
time_now = Time.now
|
||||||
|
|
||||||
|
branch_tag_result = $gitea_hat_client.get_repos_branch_tag_count_by_owner_repo(@project&.owner&.login, @project&.identifier) rescue {}
|
||||||
|
languages_result = $gitea_client.get_repos_languages_by_owner_repo(@project&.owner&.login, @project&.identifier) rescue {}
|
||||||
|
|
||||||
|
@open_pull_requests_count = @project.pull_requests.opening.size
|
||||||
|
@last_week_close_pull_requests_count = @project.pull_requests.where(status: 1).where("updated_at > ? and updated_at < ?", time_now - 7.days, time_now).size
|
||||||
|
@last_month_close_pull_requets_count = @project.pull_requests.where(status: 1).where("updated_at > ? and updated_at < ?", time_now - 30.days, time_now).size
|
||||||
|
|
||||||
|
@commits_count = @project.commit_logs.size
|
||||||
|
@last_week_commits_count = @project.commit_logs.where("created_at > ? and created_at < ?", time_now - 7.days, time_now).size
|
||||||
|
@last_month_commits_count = @project.commit_logs.where("created_at > ? and created_at < ?", time_now - 30.days, time_now).size
|
||||||
|
|
||||||
|
render_ok(data: {
|
||||||
|
branch_count: branch_tag_result["branch_count"].to_i,
|
||||||
|
tag_count: branch_tag_result["tag_count"].to_i,
|
||||||
|
license_name: @project.license&.name,
|
||||||
|
open_pull_requests_count: @open_pull_requests_count,
|
||||||
|
last_week_close_pull_requests_count: @last_week_close_pull_requests_count,
|
||||||
|
last_month_close_pull_requets_count: @last_month_close_pull_requets_count,
|
||||||
|
commits_count: @commits_count,
|
||||||
|
last_week_commits_count: @last_week_commits_count,
|
||||||
|
last_month_commits_count: @last_month_commits_count,
|
||||||
|
language: hash_transform_precentagable(languages_result),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def my_operate_journals
|
||||||
|
return render_error('请输入正确的pm_project_id.') if params[:pm_project_id].blank?
|
||||||
|
@journals = Journal.operate_journals.joins(:issue).where(issues: {pm_project_id: params[:pm_project_id], pm_issue_type: [1,2,3]})
|
||||||
|
|
||||||
|
@journals = kaminari_paginate(@journals.order(updated_on: :desc))
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def hash_transform_precentagable(hash)
|
||||||
|
total_byte_size = hash.values.sum
|
||||||
|
hash.transform_values { |v|
|
||||||
|
ActionController::Base.helpers
|
||||||
|
.number_to_percentage((v * 100.0 / total_byte_size), precision: 1)
|
||||||
|
}.select{|k,v| v != "0.0%"}
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,46 @@
|
||||||
|
class Api::Pm::IssueLinksController < Api::Pm::BaseController
|
||||||
|
before_action :load_project
|
||||||
|
before_action :load_issue
|
||||||
|
def index
|
||||||
|
@links = PmLink.where(be_linkable_id: @issue.id,be_linkable_type: 'Issue').or(PmLink.where(linkable_id: @issue.id,linkable_type: 'Issue'))
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
begin
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
params[:link_ids].each do |e|
|
||||||
|
@issue.pm_links.find_or_create_by!(be_linkable_type: 'Issue', be_linkable_id: e)
|
||||||
|
tag_issue = Issue.find_by_id(e)
|
||||||
|
next unless tag_issue.present?
|
||||||
|
journal = tag_issue.journals.create!({user_id: current_user.id})
|
||||||
|
journal.journal_details.create!({property: "tag_link_issue", prop_key: "1", value: @issue.id.to_s})
|
||||||
|
end
|
||||||
|
journal = @issue.journals.create!({user_id: current_user.id})
|
||||||
|
journal.journal_details.create!({property: "tag_link_issue", prop_key: "#{params[:link_ids].size}", value: params[:link_ids].join(",")})
|
||||||
|
end
|
||||||
|
render_ok
|
||||||
|
rescue
|
||||||
|
render_error('创建失败!')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
begin
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
@links = PmLink.where(be_linkable_id: @issue.id, be_linkable_type: 'Issue', linkable_id: params[:id], linkable_type: 'Issue').or(PmLink.where(linkable_id: @issue.id, linkable_type: 'Issue', be_linkable_id: params[:id], be_linkable_type: 'Issue'))
|
||||||
|
journal = @issue.journals.create!({user_id: current_user.id})
|
||||||
|
journal.journal_details.create!({property: "tag_link_issue", prop_key: "1", old_value: params[:id].to_s})
|
||||||
|
another_issue = Issue.find_by_id(params[:id])
|
||||||
|
if another_issue.present?
|
||||||
|
journal = another_issue.journals.create!({user_id: current_user.id})
|
||||||
|
journal.journal_details.create!({property: "tag_link_issue", prop_key: "1", old_value: @issue.id.to_s})
|
||||||
|
end
|
||||||
|
@link = @links.last
|
||||||
|
@link.destroy!
|
||||||
|
end
|
||||||
|
render_ok
|
||||||
|
rescue
|
||||||
|
render_error('删除失败!')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,71 @@
|
||||||
|
class Api::Pm::IssueTagsController < Api::Pm::BaseController
|
||||||
|
|
||||||
|
def index
|
||||||
|
@issue_tags = IssueTag.pm_able
|
||||||
|
if params[:organization_id].present?
|
||||||
|
IssueTag.pm_org_init_data(params[:organization_id]) unless $redis_cache.hget("pm_org_init_issue_tags", params[:organization_id])
|
||||||
|
@issue_tags = @issue_tags.where(organization_id: params[:organization_id])
|
||||||
|
end
|
||||||
|
@issue_tags = @issue_tags.where(pm_project_id: params[:pm_project_id]) if params[:pm_project_id].present?
|
||||||
|
@issue_tags = @issue_tags.ransack(name_cont: params[:keyword]).result if params[:keyword].present?
|
||||||
|
@issue_tags = @issue_tags.reorder("#{tag_sort_by} #{tag_sort_direction}")
|
||||||
|
@issue_tags = kaminari_paginate(@issue_tags)
|
||||||
|
render "api/v1/issues/issue_tags/index"
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
return render_error("请输入正确的OrganizationID") unless Organization.exists?(id: issue_tag_create_params[:organization_id])
|
||||||
|
return render_error("项目标记名称不能为空!") unless issue_tag_create_params[:name].present?
|
||||||
|
@issue_tag = IssueTag.new(issue_tag_create_params.merge!(project_id: 0))
|
||||||
|
if @issue_tag.save!
|
||||||
|
render_ok
|
||||||
|
else
|
||||||
|
render_error("创建标记失败!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
before_action :load_issue_tag, only: [:update, :destroy]
|
||||||
|
|
||||||
|
def update
|
||||||
|
@issue_tag.attributes = issue_tag_update_params
|
||||||
|
if @issue_tag.save!
|
||||||
|
render_ok
|
||||||
|
else
|
||||||
|
render_error("更新标记失败!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
if @issue_tag.destroy!
|
||||||
|
render_ok
|
||||||
|
else
|
||||||
|
render_error("删除标记失败!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
private
|
||||||
|
def tag_sort_by
|
||||||
|
sort_by = params.fetch(:sort_by, "created_at")
|
||||||
|
sort_by = IssueTag.column_names.include?(sort_by) ? sort_by : "created_at"
|
||||||
|
sort_by
|
||||||
|
end
|
||||||
|
|
||||||
|
def tag_sort_direction
|
||||||
|
sort_direction = params.fetch(:sort_direction, "desc")&.downcase
|
||||||
|
sort_direction = %w(desc asc).include?(sort_direction) ? sort_direction : "desc"
|
||||||
|
sort_direction
|
||||||
|
end
|
||||||
|
|
||||||
|
def issue_tag_create_params
|
||||||
|
params.permit(:name, :description, :color, :pm_project_id, :organization_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def issue_tag_update_params
|
||||||
|
params.permit(:name, :description, :color)
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_issue_tag
|
||||||
|
@issue_tag = IssueTag.pm_able.find_by_id(params[:id])
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,368 @@
|
||||||
|
class Api::Pm::IssuesController < Api::Pm::BaseController
|
||||||
|
before_action :require_login, except: [:index]
|
||||||
|
before_action :load_project
|
||||||
|
before_action :load_issue, only: %i[show update destroy link_index link_issues parent_issues]
|
||||||
|
before_action :load_issues, only: %i[batch_update batch_destroy]
|
||||||
|
before_action :check_issue_operate_permission, only: %i[update destroy]
|
||||||
|
|
||||||
|
def index
|
||||||
|
@object_result = Api::V1::Issues::ListService.call(@project, query_params, current_user)
|
||||||
|
@total_issues_count = @object_result[:total_issues_count]
|
||||||
|
@opened_issues_count = @object_result[:opened_issues_count]
|
||||||
|
@closed_issues_count = @object_result[:closed_issues_count]
|
||||||
|
@complete_issues_count = @object_result[:complete_issues_count]
|
||||||
|
if params[:only_name].present?
|
||||||
|
@issues = kaminary_select_paginate(
|
||||||
|
@object_result[:data].select(:id, :subject, :project_issues_index, :updated_on, :created_on))
|
||||||
|
else
|
||||||
|
@issues = kaminari_paginate(@object_result[:data])
|
||||||
|
end
|
||||||
|
render 'api/v1/issues/index'
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_index
|
||||||
|
pm_issue_type = params[:pm_issue_type] || [1, 2, 3]
|
||||||
|
not_join_id = case params[:issue_filter_type]
|
||||||
|
when 'leaf_issue'
|
||||||
|
Issue.where(root_id: @issue.id).pluck(:id)
|
||||||
|
when 'link_issue'
|
||||||
|
@issue.pm_links.pluck(:be_linkable_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
not_join_id << @issue.id
|
||||||
|
object_issues = Issue.where(
|
||||||
|
pm_project_id: params[:pm_project_id],
|
||||||
|
pm_issue_type: pm_issue_type
|
||||||
|
).where.not(id: not_join_id).order(updated_on: :desc)
|
||||||
|
|
||||||
|
object_issues = object_issues.where(root_id: nil, child_count: 0) if params[:issue_filter_type] == 'leaf_issue'
|
||||||
|
@issues = kaminari_paginate(object_issues)
|
||||||
|
render 'api/v1/issues/index'
|
||||||
|
end
|
||||||
|
|
||||||
|
def parent_issues
|
||||||
|
@issues = Issue.where(pm_project_id: params[:pm_project_id])
|
||||||
|
.where.not(id: @issue.id)
|
||||||
|
.where.not(id: Issue.full_children_issues(@issue).map{|i|i.id})
|
||||||
|
@issues = @issues.where(pm_issue_type: params[:pm_issue_type]) if params[:pm_issue_type].present?
|
||||||
|
@issues = @issues.ransack(id_or_project_issues_index_eq: params[:keyword]).result.or(@issues.ransack(subject_or_description_cont: params[:keyword]).result) if params[:keyword].present?
|
||||||
|
@issues = @issues.reorder("#{issue_sort_by} #{issue_sort_direction}")
|
||||||
|
if params[:only_name].present?
|
||||||
|
@issues = kaminary_select_paginate(
|
||||||
|
@issues.select(:id, :subject, :project_issues_index, :updated_on, :created_on))
|
||||||
|
else
|
||||||
|
@issues = @issues.includes(:priority, :issue_status, :user, :show_assigners, :show_issue_tags, :version, :comment_journals)
|
||||||
|
@issues = kaminari_paginate(@issues)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
@issue.associate_attachment_container
|
||||||
|
render 'api/v1/issues/show'
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@object_result = Api::Pm::Issues::CreateService.call(@project, issue_params, current_user)
|
||||||
|
render 'api/v1/issues/create'
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@object_result = Api::Pm::Issues::UpdateService.call(@project, @issue, issue_params, current_user)
|
||||||
|
render 'api/v1/issues/update'
|
||||||
|
end
|
||||||
|
|
||||||
|
def batch_update
|
||||||
|
@object_result = Api::Pm::Issues::BatchUpdateService.call(@project, @issues, batch_issue_params, current_user)
|
||||||
|
if @object_result
|
||||||
|
render_ok
|
||||||
|
else
|
||||||
|
render_error('批量更新疑修失败!')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def batch_destroy
|
||||||
|
return render_ok if params[:ids].is_a?(Array) && params[:ids].blank?
|
||||||
|
@object_result = Api::Pm::Issues::BatchDeleteService.call(@project, @issues, current_user)
|
||||||
|
if @object_result
|
||||||
|
render_ok
|
||||||
|
else
|
||||||
|
render_error('批量删除疑修失败!')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def priorities
|
||||||
|
@priorities = IssuePriority.order(position: :asc)
|
||||||
|
@priorities = @priorities.ransack(name_cont: params[:keyword]).result if params[:keyword]
|
||||||
|
@priorities = kaminary_select_paginate(@priorities)
|
||||||
|
render "api/v1/issues/issue_priorities/index"
|
||||||
|
end
|
||||||
|
|
||||||
|
def tags
|
||||||
|
# IssueTag.pm_init_data(params[:pm_project_id]) unless $redis_cache.hget("pm_project_init_issue_tags", params[:pm_project_id])
|
||||||
|
@issue_tags = IssueTag.where(pm_project_id: params[:pm_project_id]).reorder("#{tag_sort_by} #{tag_sort_direction}")
|
||||||
|
@issue_tags = @issue_tags.ransack(name_cont: params[:keyword]).result if params[:keyword].present?
|
||||||
|
params[:only_name] = true #强制渲染 不走project
|
||||||
|
@issue_tags = kaminary_select_paginate(@issue_tags.select(:id, :name, :color))
|
||||||
|
render "api/v1/issues/issue_tags/index"
|
||||||
|
end
|
||||||
|
|
||||||
|
def statues
|
||||||
|
@statues = IssueStatus.order("position asc")
|
||||||
|
@statues = @statues.ransack(name_cont: params[:keyword]).result if params[:keyword].present?
|
||||||
|
@statues = kaminary_select_paginate(@statues)
|
||||||
|
render "api/v1/issues/statues/index"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@object_result = Api::Pm::Issues::DeleteService.call(@project, @issue, current_user)
|
||||||
|
if @object_result
|
||||||
|
render_ok
|
||||||
|
else
|
||||||
|
render_error('删除疑修失败!')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def export
|
||||||
|
return render_error('请输入正确的项目ID.') if params[:pm_project_id].blank?
|
||||||
|
Axlsx::Package.new do |p|
|
||||||
|
[['requirement', 1], ['task', 2], ['bug', 3]].each do |type|
|
||||||
|
p.workbook.add_worksheet(:name => type[0]) do |sheet|
|
||||||
|
@issues = Issue.where(pm_project_id: params[:pm_project_id], pm_issue_type: type[1])
|
||||||
|
sheet.add_row ["ID", "标题*", "正文", "创建者*", "创建时间", "修改者", "更新时间", "状态", "负责人", "优先级", "标记", "开始时间","结束时间", "预估工时"]
|
||||||
|
@issues.each do |issue|
|
||||||
|
sheet.add_row [issue.id, issue.subject, issue.description, issue.user.try(:login), issue.created_on.strftime("%Y-%m-%d %H:%M:%S"), issue.changer.try(:login), issue.updated_on.strftime("%Y-%m-%d %H:%M:%S"), issue.status_id, issue.assigners.pluck(:login).join(","), issue.priority_id, issue.issue_tags.pluck(:name, :color).join(","), issue.start_date.present? ? issue.start_date.strftime("%Y-%m-%d") : "", issue.due_date.present? ? issue.due_date.strftime("%Y-%m-%d") : "", issue.time_scale]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
p.workbook.add_worksheet(:name => 'leaf_relations') do |sheet|
|
||||||
|
leaf_issues = Issue.where(pm_project_id: params[:pm_project_id]).where.not(root_id: nil)
|
||||||
|
sheet.add_row ["ID", "父工作项ID"]
|
||||||
|
leaf_issues.each do |issue|
|
||||||
|
sheet.add_row [issue.id, issue.root_id]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
p.workbook.add_worksheet(:name => 'link_relations') do |sheet|
|
||||||
|
# links = PmLink.joins(:linkable_issue).where(issues: {pm_project_id: params[:pm_project_id]})
|
||||||
|
links = PmLink.find_by_sql("SELECT `pm_links`.* FROM `pm_links` INNER JOIN `issues` ON `issues`.`id` = `pm_links`.`linkable_id` AND `pm_links`.`linkable_type` = 'Issue' WHERE `issues`.`pm_project_id` = #{params[:pm_project_id]}")
|
||||||
|
sheet.add_row ["ID", "被关联工作项ID"]
|
||||||
|
links.each do |link|
|
||||||
|
sheet.add_row [link.linkable_id, link.be_linkable_id]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
p.serialize('public/导出工作项.xlsx')
|
||||||
|
end
|
||||||
|
|
||||||
|
send_file('public/导出工作项.xlsx', :type => 'application/octet-stream;charset=utf-8')
|
||||||
|
end
|
||||||
|
|
||||||
|
def import
|
||||||
|
begin
|
||||||
|
return render_error('请上传正确的文件') if params[:file].blank? || !params[:file].is_a?(ActionDispatch::Http::UploadedFile)
|
||||||
|
return render_error('请输入正确的项目ID.') if params[:pm_project_id].blank?
|
||||||
|
return render_error('请输入正确的组织ID.') if params[:organization_id].blank?
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
types = {requirement: 1, task: 2, bug: 3}
|
||||||
|
doc = SimpleXlsxReader.open(params[:file].tempfile)
|
||||||
|
doc.sheets.each do |sheet|
|
||||||
|
case sheet.name
|
||||||
|
when 'requirement', 'task', 'bug'
|
||||||
|
|
||||||
|
type = types["#{sheet.name}".to_sym]
|
||||||
|
|
||||||
|
sheet.rows.each.with_index do |row, index|
|
||||||
|
next if index == 0
|
||||||
|
issue = Issue.new(issue_classify: "issue", project_id: 0, pm_project_id: params[:pm_project_id], pm_issue_type: type, tracker_id: Tracker.first.id)
|
||||||
|
issue.fake_id = row[0]
|
||||||
|
issue.subject = row[1]
|
||||||
|
issue.description = row[2]
|
||||||
|
author = User.find_by(login: row[3])
|
||||||
|
issue.user = author
|
||||||
|
issue.created_on = row[4]
|
||||||
|
changer = User.find_by(login: row[5])
|
||||||
|
issue.changer = changer
|
||||||
|
issue.updated_on = row[6]
|
||||||
|
issue.status_id = row[7].to_i
|
||||||
|
if row[8].present?
|
||||||
|
row[8].split(',').each do |a|
|
||||||
|
u = User.find_by(login: a)
|
||||||
|
next unless u.present?
|
||||||
|
issue.assigners << u
|
||||||
|
end
|
||||||
|
end
|
||||||
|
issue.priority_id = row[9]
|
||||||
|
if row[10].present?
|
||||||
|
row[10].split(',').each_slice(2).to_a.each do |t|
|
||||||
|
tag = IssueTag.find_by(project_id: 0, organization_id: params[:organization_id], name: t[0])
|
||||||
|
if tag.present?
|
||||||
|
issue.issue_tags << tag
|
||||||
|
else
|
||||||
|
tag = IssueTag.create(project_id: 0,organization_id: params[:organization_id], name: t[0], color: t[1])
|
||||||
|
issue.issue_tags << tag
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
issue.start_date = row[11]
|
||||||
|
issue.due_date = row[12]
|
||||||
|
issue.time_scale = row[13]
|
||||||
|
issue.save!
|
||||||
|
end
|
||||||
|
when 'leaf_relations'
|
||||||
|
sheet.rows.each.with_index do |row, index|
|
||||||
|
next if index == 0
|
||||||
|
children_issue = Issue.where(fake_id: row[0]).last
|
||||||
|
parent_issue = Issue.where(fake_id: row[1]).last
|
||||||
|
next if children_issue.blank? || parent_issue.blank?
|
||||||
|
children_issue.root_id = parent_issue.id
|
||||||
|
children_issue.save(touch: false)
|
||||||
|
end
|
||||||
|
when 'link_relations'
|
||||||
|
sheet.rows.each.with_index do |row, index|
|
||||||
|
next if index == 0
|
||||||
|
link_issue = Issue.where(fake_id: row[0]).last
|
||||||
|
be_link_issue = Issue.where(fake_id: row[1]).last
|
||||||
|
next if link_issue.blank? || be_link_issue.blank?
|
||||||
|
PmLink.create!(linkable_type: 'Issue', linkable_id: link_issue.id, be_linkable_type: 'Issue', be_linkable_id: be_link_issue.id)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return render_error('导入失败,请上传正确格式的excel文件')
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
render_ok
|
||||||
|
rescue
|
||||||
|
return render_error('导入失败,请上传正确格式的excel文件')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_issues
|
||||||
|
children_issues = @issue.pm_issue_type == 1 ? @issue.child_count > 0 ? Issue.where(id: @issue.id) : Issue.none : Issue.where(root_id: @issue.id)
|
||||||
|
linkable_issues = Issue.where(id: PmLink.where(linkable_type: "Issue", linkable_id: @issue.id).pluck(:be_linkable_id))
|
||||||
|
belinkable_issues = Issue.where(id: PmLink.where(be_linkable_type: "Issue", be_linkable_id: @issue.id).pluck(:linkable_id))
|
||||||
|
|
||||||
|
full_link_issues_ids = children_issues.pluck(:id) | linkable_issues.pluck(:id) | belinkable_issues.pluck(:id)
|
||||||
|
compare_link_issues_ids = children_issues.pluck(:id) | linkable_issues.pluck(:id) | belinkable_issues.pluck(:id)
|
||||||
|
i = compare_link_issues_ids.count
|
||||||
|
while i > 0 do
|
||||||
|
children_issues = Issue.where(root_id: compare_link_issues_ids)
|
||||||
|
linkable_issues = Issue.where(id: PmLink.where(linkable_type: "Issue", linkable_id: compare_link_issues_ids).pluck(:be_linkable_id))
|
||||||
|
belinkable_issues = Issue.where(id: PmLink.where(be_linkable_type: "Issue", be_linkable_id: compare_link_issues_ids).pluck(:linkable_id))
|
||||||
|
|
||||||
|
compare_link_issues_ids = (children_issues.pluck(:id) | linkable_issues.pluck(:id) | belinkable_issues.pluck(:id)) - full_link_issues_ids
|
||||||
|
full_link_issues_ids = full_link_issues_ids | compare_link_issues_ids
|
||||||
|
i = compare_link_issues_ids.count
|
||||||
|
end
|
||||||
|
exclude_issues_ids = []
|
||||||
|
exclude_issues = Issue.where(id: full_link_issues_ids).where.not(root_id: nil)
|
||||||
|
exclude_issues.each do |i|
|
||||||
|
exclude_issues_ids << i.id if i.pm_issue_type == 1 && full_link_issues_ids.include?(i.root_id)
|
||||||
|
end
|
||||||
|
full_link_issues_ids = full_link_issues_ids - exclude_issues_ids
|
||||||
|
@requirement_issues = Issue.where(id:full_link_issues_ids, pm_issue_type:1, root_id: nil).pm_includes
|
||||||
|
@task_issues = Issue.where(id:full_link_issues_ids, pm_issue_type:2).pm_includes
|
||||||
|
@bug_issues = Issue.where(id:full_link_issues_ids, pm_issue_type:3).pm_includes
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def circle_link_issues(issue_ids)
|
||||||
|
if issue_ids.present?
|
||||||
|
children_issues = Issue.joins(:parent_issue).where(issues: {id: issue_ids})
|
||||||
|
linkable_issues = Issue.where(id: PmLink.where(linkable_type: "Issue", linkable_id: issue_ids))
|
||||||
|
belinkable_issues = Issue.where(id: PmLink.where(be_linkable_type: "Issue", be_linkable_id: issue_ids))
|
||||||
|
|
||||||
|
return circle_link_issues(children_issues.pluck(:id))
|
||||||
|
else
|
||||||
|
return []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_issue_operate_permission
|
||||||
|
return if params[:project_id].to_i.zero?
|
||||||
|
render_forbidden('您没有操作权限!') unless @project.member?(current_user) || current_user.admin? || @issue.user == current_user
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_issue
|
||||||
|
return render_parameter_missing if params[:pm_project_id].blank?
|
||||||
|
@issue = Issue.issue_issue.where(pm_project_id: params[:pm_project_id]).find_by_id(params[:id])
|
||||||
|
render_not_found('疑修不存在!') if @issue.blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_issues
|
||||||
|
return render_error('请输入正确的ID数组!') unless params[:ids].is_a?(Array)
|
||||||
|
params[:ids].each do |id|
|
||||||
|
@issue = Issue.find_by(id: id, pm_project_id: params[:pm_project_id])
|
||||||
|
return render_not_found("ID为#{id}的疑修不存在!") if @issue.blank?
|
||||||
|
end
|
||||||
|
if params[:ids].blank?
|
||||||
|
@issues = Issue.where(pm_project_id: params[:pm_project_id])
|
||||||
|
else
|
||||||
|
@issues = Issue.where(id: params[:ids], pm_project_id: params[:pm_project_id])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def query_params
|
||||||
|
params.permit(
|
||||||
|
:only_name,
|
||||||
|
:category,
|
||||||
|
:participant_category,
|
||||||
|
:keyword, :author_id,
|
||||||
|
:milestone_id, :assigner_id,
|
||||||
|
:status_id, :priority_id,
|
||||||
|
:begin_date, :end_date,
|
||||||
|
:update_begin_date, :update_end_date,
|
||||||
|
:sort_by, :sort_direction, :root_id,
|
||||||
|
:issue_tag_ids, :pm_project_id, :pm_sprint_id, :pm_issue_type, :pm_project_ids,
|
||||||
|
:status_ids, :ids, :exclude_ids, :pm_issue_types, :participator_id
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def issue_params
|
||||||
|
params.permit(
|
||||||
|
:status_id, :priority_id, :milestone_id,
|
||||||
|
:branch_name, :start_date, :due_date, :time_scale,
|
||||||
|
:subject, :description, :blockchain_token_num, :root_subject,
|
||||||
|
:pm_project_id, :pm_sprint_id, :pm_issue_type, :root_id, :link_able_id, :project_id,
|
||||||
|
issue_tag_ids: [],
|
||||||
|
assigner_ids: [],
|
||||||
|
attachment_ids: [],
|
||||||
|
receivers_login: []
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def batch_issue_params
|
||||||
|
params.permit(
|
||||||
|
:status_id, :priority_id, :milestone_id, :pm_sprint_id, :due_date, :pm_issue_type, :root_id, :target_pm_project_id, :project_id,
|
||||||
|
:issue_tag_ids => [],
|
||||||
|
:assigner_ids => [] )
|
||||||
|
end
|
||||||
|
|
||||||
|
def issue_sort_by
|
||||||
|
sort_by = params.fetch(:sort_by, "updated_on")
|
||||||
|
sort_by = Issue.column_names.include?(sort_by) ? sort_by : "updated_on"
|
||||||
|
sort_by
|
||||||
|
end
|
||||||
|
|
||||||
|
def issue_sort_direction
|
||||||
|
sort_direction = params.fetch(:sort_direction, "desc").downcase
|
||||||
|
sort_direction = %w(desc asc).include?(sort_direction) ? sort_direction : "desc"
|
||||||
|
sort_direction
|
||||||
|
end
|
||||||
|
|
||||||
|
def tag_sort_by
|
||||||
|
sort_by = params.fetch(:sort_by, "created_at")
|
||||||
|
sort_by = IssueTag.column_names.include?(sort_by) ? sort_by : "created_at"
|
||||||
|
sort_by
|
||||||
|
end
|
||||||
|
|
||||||
|
def tag_sort_direction
|
||||||
|
sort_direction = params.fetch(:sort_direction, "desc").downcase
|
||||||
|
sort_direction = %w(desc asc).include?(sort_direction) ? sort_direction : "desc"
|
||||||
|
sort_direction
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,61 @@
|
||||||
|
class Api::Pm::JournalsController < Api::Pm::BaseController
|
||||||
|
before_action :require_login, except: [:index, :children_journals]
|
||||||
|
before_action :load_project
|
||||||
|
before_action :load_issue
|
||||||
|
before_action :load_journal, only: [:children_journals, :update, :destroy]
|
||||||
|
|
||||||
|
def index
|
||||||
|
@object_result = Api::V1::Issues::Journals::ListService.call(@issue, query_params, current_user)
|
||||||
|
@total_journals_count = @object_result[:total_journals_count]
|
||||||
|
@total_operate_journals_count = @object_result[:total_operate_journals_count]
|
||||||
|
@total_comment_journals_count = @object_result[:total_comment_journals_count]
|
||||||
|
@journals = kaminary_select_paginate(@object_result[:data])
|
||||||
|
render 'api/pm/issues/journals/index'
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@object_result = Api::V1::Issues::Journals::CreateService.call(@issue, journal_params, current_user)
|
||||||
|
render 'api/v1/issues/journals/create'
|
||||||
|
end
|
||||||
|
|
||||||
|
def children_journals
|
||||||
|
@object_results = Api::V1::Issues::Journals::ChildrenListService.call(@issue, @journal, query_params, current_user)
|
||||||
|
@journals = kaminari_paginate(@object_results)
|
||||||
|
render 'api/v1/issues/journals/children_journals'
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@object_result = Api::V1::Issues::Journals::UpdateService.call(@issue, @journal, journal_params, current_user)
|
||||||
|
render 'api/v1/issues/journals/update'
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
TouchWebhookJob.set(wait: 5.seconds).perform_later('IssueComment', @issue&.id, current_user.id, @journal.id, 'deleted', JSON.parse(@journal.to_builder.target!))
|
||||||
|
if @journal.destroy!
|
||||||
|
render_ok
|
||||||
|
else
|
||||||
|
render_error('删除评论失败!')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def query_params
|
||||||
|
params.permit(:category, :keyword, :sort_by, :sort_direction)
|
||||||
|
end
|
||||||
|
|
||||||
|
def journal_params
|
||||||
|
params.permit(:notes, :parent_id, :reply_id, :attachment_ids => [], :receivers_login => [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_issue
|
||||||
|
@issue = Issue.issue_issue.where(pm_project_id: params[:pm_project_id]).find_by_id(params[:issue_id])
|
||||||
|
render_not_found('疑修不存在!') if @issue.blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_journal
|
||||||
|
@journal = Journal.find_by_id(params[:id])
|
||||||
|
render_not_found('评论不存在!') unless @journal.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,156 @@
|
||||||
|
class Api::Pm::ProjectsController < Api::Pm::BaseController
|
||||||
|
before_action :require_login, except: [:convert]
|
||||||
|
before_action :load_project, only: [:convert]
|
||||||
|
def convert
|
||||||
|
data = {
|
||||||
|
owner: @project.owner.try(:login),
|
||||||
|
identifier: @project.identifier,
|
||||||
|
name: @project.name
|
||||||
|
}
|
||||||
|
render_ok(data: data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def issues_count
|
||||||
|
return tip_exception '参数错误' unless params[:pm_project_id].present?
|
||||||
|
@issues = Issue.where(pm_project_id: params[:pm_project_id])
|
||||||
|
case params[:category].to_s
|
||||||
|
when 'closed'
|
||||||
|
@issues = @issues.closed
|
||||||
|
when 'opened'
|
||||||
|
@issues = @issues.opened
|
||||||
|
end
|
||||||
|
@participant_category_count = {}
|
||||||
|
if params[:participant_category].to_s == "authoredme" or params[:participant_category].to_s == "assignedme"
|
||||||
|
issues_category = @issues.joins(:issue_participants).where(pm_issue_type: [1, 2, 3]).where(issue_participants: {participant_type: %w[authored assigned atme], participant_id: current_user&.id})
|
||||||
|
@participant_category_count = issues_category.group(:pm_project_id, "issue_participants.participant_type").count
|
||||||
|
end
|
||||||
|
case params[:participant_category].to_s
|
||||||
|
when 'aboutme' # 关于我的
|
||||||
|
@issues = @issues.joins(:issue_participants).where(issue_participants: {participant_type: %w[authored assigned atme], participant_id: current_user&.id})
|
||||||
|
when 'authoredme' # 我创建的
|
||||||
|
@issues = @issues.joins(:issue_participants).where(issue_participants: {participant_type: 'authored', participant_id: current_user&.id})
|
||||||
|
when 'assignedme' # 我负责的
|
||||||
|
@issues = @issues.joins(:issue_participants).where(issue_participants: {participant_type: 'assigned', participant_id: current_user&.id})
|
||||||
|
when 'atme' # @我的
|
||||||
|
@issues = @issues.joins(:issue_participants).where(issue_participants: {participant_type: 'atme', participant_id: current_user&.id})
|
||||||
|
end
|
||||||
|
data = {}
|
||||||
|
@issues_count = @issues.group(:pm_project_id).count
|
||||||
|
# requirement 1 task 2 bug 3
|
||||||
|
@issues_type_count = @issues.group(:pm_project_id, :pm_issue_type).count
|
||||||
|
params[:pm_project_id].map(&:to_i).map do |project_id|
|
||||||
|
data[project_id] = {
|
||||||
|
total: @issues_count[project_id] || 0,
|
||||||
|
requirement: @issues_type_count[[project_id, 1]] || 0,
|
||||||
|
task: @issues_type_count[[project_id, 2]] || 0,
|
||||||
|
bug: @issues_type_count[[project_id, 3]] || 0,
|
||||||
|
authoredme: @participant_category_count[[project_id, 0]] || 0,
|
||||||
|
assignedme: @participant_category_count[[project_id, 1]] || 0,
|
||||||
|
atme: @participant_category_count[[project_id, 4]] || 0,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
render_ok(data: data)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def statistics
|
||||||
|
return tip_exception '参数错误' if params[:pm_project_id].blank?
|
||||||
|
@issues = Issue.where(pm_project_id: params[:pm_project_id], pm_issue_type:[1, 2, 3])
|
||||||
|
@last_week_close_issues = @issues.where(status_id: 5).where("updated_on > ? and updated_on < ?", Time.now - 7.days, Time.now)
|
||||||
|
last_week_close_type_count_data = @last_week_close_issues.group(:pm_issue_type).count
|
||||||
|
type_count_data = @issues.group(:pm_issue_type).count
|
||||||
|
type_status = @issues.group(:pm_issue_type,:status_id).count
|
||||||
|
type_status_data = {}
|
||||||
|
IssueStatus.all.map do |e|
|
||||||
|
# next if e.id == 5
|
||||||
|
[1,2,3].map{ |type|
|
||||||
|
next if type == 1 && [1, 6].include?(e.id)
|
||||||
|
type_status_data[type] = {} if type_status_data[type].nil?
|
||||||
|
if type_status[[type,e.id]].nil?
|
||||||
|
type_status_data[type][e.id] = 0
|
||||||
|
else
|
||||||
|
type_status_data[type][e.id] = type_status[[type,e.id]]
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
open_data = {
|
||||||
|
"1": type_status_data[1][1].to_i + type_status_data[1][2].to_i + type_status_data[1][3].to_i + type_status_data[1][6].to_i,
|
||||||
|
"2": type_status_data[2][1].to_i + type_status_data[2][2].to_i + type_status_data[2][3].to_i + type_status_data[2][6].to_i,
|
||||||
|
"3": type_status_data[3][1].to_i + type_status_data[3][2].to_i + type_status_data[3][3].to_i + type_status_data[3][6].to_i,
|
||||||
|
}
|
||||||
|
if type_count_data.keys.size < 3
|
||||||
|
nedd_add = [1,2,3] - type_count_data.keys
|
||||||
|
nedd_add.map{ |e|
|
||||||
|
type_count_data[e] = 0
|
||||||
|
}
|
||||||
|
end
|
||||||
|
if last_week_close_type_count_data.keys.size < 3
|
||||||
|
nedd_add = [1,2,3] - last_week_close_type_count_data.keys
|
||||||
|
nedd_add.map{ |e|
|
||||||
|
last_week_close_type_count_data[e] = 0
|
||||||
|
}
|
||||||
|
end
|
||||||
|
data = {
|
||||||
|
pie_chart: type_count_data,
|
||||||
|
bar_chart: type_status_data,
|
||||||
|
open_data: open_data,
|
||||||
|
last_week_close_data: last_week_close_type_count_data,
|
||||||
|
}
|
||||||
|
render_ok(data: data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def polyline
|
||||||
|
return tip_exception '参数错误' if params[:pm_project_id].blank?
|
||||||
|
time_line = (Time.current.beginning_of_day - 29.day) .. Time.current
|
||||||
|
@create_issues = Issue.where(pm_project_id: params[:pm_project_id],created_on: time_line)
|
||||||
|
@due_issues = Issue.where(pm_project_id: params[:pm_project_id],status_id:[3,5],due_date: time_line)
|
||||||
|
@create_issues_count = @create_issues.group(:pm_issue_type,"DATE(created_on)").count
|
||||||
|
@due_issues_count = @due_issues.group(:pm_issue_type,"DATE(due_date)").count
|
||||||
|
data = {
|
||||||
|
create_issues: {},
|
||||||
|
due_issues: {}
|
||||||
|
}
|
||||||
|
30.times do |time|
|
||||||
|
current_time = Date.current - time.day
|
||||||
|
if @create_issues_count.present?
|
||||||
|
data[:create_issues][current_time] = {
|
||||||
|
"1": @create_issues_count[[1,current_time]] || 0,
|
||||||
|
"2": @create_issues_count[[2,current_time]] || 0,
|
||||||
|
"3": @create_issues_count[[3,current_time]] || 0
|
||||||
|
}
|
||||||
|
else
|
||||||
|
data[:create_issues][current_time] = {
|
||||||
|
"1": 0,
|
||||||
|
"2": 0,
|
||||||
|
"3": 0
|
||||||
|
}
|
||||||
|
end
|
||||||
|
if @due_issues_count.present?
|
||||||
|
data[:due_issues][current_time] = {
|
||||||
|
"1": @due_issues_count[[1,current_time]] || 0,
|
||||||
|
"2": @due_issues_count[[2,current_time]] || 0,
|
||||||
|
"3": @due_issues_count[[3,current_time]] || 0
|
||||||
|
}
|
||||||
|
else
|
||||||
|
data[:due_issues][current_time] = {
|
||||||
|
"1": 0,
|
||||||
|
"2": 0,
|
||||||
|
"3": 0
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
render_ok(data: data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def bind_project
|
||||||
|
return render_forbidden('您没有操作权限!') unless @project.member?(current_user) || current_user.admin?
|
||||||
|
Issue.where(pm_project_id: params[:pm_project_id], user_id: current_user).update_all(project_id: params[:project_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def load_project
|
||||||
|
@project = Project.joins(:owner).find params[:project_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,109 @@
|
||||||
|
class Api::Pm::SprintIssuesController < Api::Pm::BaseController
|
||||||
|
|
||||||
|
before_action :require_login, except: [:index]
|
||||||
|
|
||||||
|
def index
|
||||||
|
@issues = Api::Pm::SprintIssues::ListService.call(query_params, current_user)
|
||||||
|
@issues = kaminari_paginate(@issues)
|
||||||
|
render 'api/v1/issues/index'
|
||||||
|
end
|
||||||
|
|
||||||
|
def burndown_charts
|
||||||
|
return tip_exception '参数错误' if params[:pm_sprint_id].blank? || params[:start_time].blank? || params[:end_time].blank?
|
||||||
|
@issues = Issue.where(pm_sprint_id: params[:pm_sprint_id])
|
||||||
|
start_time = Date.parse params[:start_time]
|
||||||
|
end_time = Date.parse params[:end_time]
|
||||||
|
time_count = (end_time - start_time).to_i + 1 # 计算间隔时间 加上最后一天
|
||||||
|
data = []
|
||||||
|
curren_issues = @issues.group(:status_id, :due_date).count
|
||||||
|
total_count = @issues.count
|
||||||
|
cardinality = (total_count.zero? || time_count.zero?) ? 0 : total_count.to_f / time_count.to_f
|
||||||
|
# cardinality = BigDecimal.new(total_count) / BigDecimal.new(time_count)
|
||||||
|
time_count.times do |x|
|
||||||
|
e_time = start_time + x
|
||||||
|
completed = curren_issues[[5, e_time]].to_i + curren_issues[[3, e_time]].to_i - @issues.where(pm_issue_type: 3, status_id: 3).size
|
||||||
|
total_count = total_count - completed
|
||||||
|
data << { time: e_time, undone: total_count, completed: completed, base_number: (cardinality * (time_count - x - 1)).to_f.round(2) }
|
||||||
|
end
|
||||||
|
render_ok(data: data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def statistics
|
||||||
|
pm_sprint_ids = params[:pm_sprint_ids].split(",") rescue []
|
||||||
|
return tip_exception '参数错误' if pm_sprint_ids.blank?
|
||||||
|
@issues = Issue.where(pm_sprint_id: pm_sprint_ids)
|
||||||
|
data = {}
|
||||||
|
# requirement 1 task 2 bug 3
|
||||||
|
@issues_count = @issues.group(:pm_sprint_id).count
|
||||||
|
@issues_type_count = @issues.group(:pm_sprint_id, :status_id).count
|
||||||
|
@issues_pm_type_count = @issues.group(:pm_sprint_id, :pm_issue_type).count
|
||||||
|
@issues_hour_count = @issues.group(:pm_sprint_id).sum(:time_scale)
|
||||||
|
@issues_hour_type_count = @issues.group(:pm_sprint_id, :status_id).sum(:time_scale)
|
||||||
|
@issues_hour_pm_type_count = @issues.group(:pm_sprint_id, :pm_issue_type).sum(:time_scale)
|
||||||
|
@issues_status_pm_type_count = @issues.group(:pm_sprint_id, :pm_issue_type, :status_id).count
|
||||||
|
pm_sprint_ids.map(&:to_i).map do |sprint_id|
|
||||||
|
# count_closed 工作项已完成/已关闭数量,需排除已修复的缺陷数量
|
||||||
|
count_closed = @issues_type_count[[sprint_id, 5]].to_i + @issues_type_count[[sprint_id, 3]].to_i - @issues.where(pm_sprint_id: sprint_id, pm_issue_type: 3, status_id: 3).size
|
||||||
|
# hour_closed 已完成/已关闭 预估工时之和,需排除已修复的缺陷预估工时
|
||||||
|
hour_closed = @issues_hour_type_count[[sprint_id, 5]].to_f + @issues_hour_type_count[[sprint_id, 3]].to_f - @issues.where(pm_sprint_id: sprint_id, pm_issue_type: 3, status_id: 3).sum(:time_scale).to_f
|
||||||
|
data[sprint_id] = {
|
||||||
|
count_total: @issues_count[sprint_id] || 0,
|
||||||
|
count_closed: count_closed || 0,
|
||||||
|
hour_total: @issues_hour_count[sprint_id].to_f || 0,
|
||||||
|
hour_closed: hour_closed || 0,
|
||||||
|
requirement: @issues_pm_type_count[[sprint_id, 1]] || 0,
|
||||||
|
task: @issues_pm_type_count[[sprint_id, 2]] || 0,
|
||||||
|
bug: @issues_pm_type_count[[sprint_id, 3]] || 0,
|
||||||
|
requirement_hour: @issues_hour_pm_type_count[[sprint_id, 1]].to_i || 0,
|
||||||
|
task_hour: @issues_hour_pm_type_count[[sprint_id, 2]].to_i || 0,
|
||||||
|
bug_hour: @issues_hour_pm_type_count[[sprint_id, 3]].to_i || 0,
|
||||||
|
requirement_open: (@issues_status_pm_type_count[[sprint_id, 1, 1]].to_i + @issues_status_pm_type_count[[sprint_id, 1, 2]].to_i) || 0,
|
||||||
|
task_open: @issues_status_pm_type_count[[sprint_id, 2, 1]].to_i + @issues_status_pm_type_count[[sprint_id, 2, 2]].to_i || 0,
|
||||||
|
bug_open: @issues_status_pm_type_count[[sprint_id, 3, 1]].to_i + @issues_status_pm_type_count[[sprint_id, 3, 2]].to_i || 0
|
||||||
|
}
|
||||||
|
end
|
||||||
|
render_ok(data: data)
|
||||||
|
end
|
||||||
|
|
||||||
|
before_action :load_uncomplete_issues, only: [:complete]
|
||||||
|
|
||||||
|
def complete
|
||||||
|
begin
|
||||||
|
case complete_params[:complete_type].to_i
|
||||||
|
when 1
|
||||||
|
@issues.update_all(status_id: 5)
|
||||||
|
when 2
|
||||||
|
@issues.update_all(pm_sprint_id: 0)
|
||||||
|
when 3
|
||||||
|
@issues.update_all(pm_sprint_id: complete_params[:target_pm_project_sprint_id])
|
||||||
|
end
|
||||||
|
render_ok
|
||||||
|
rescue => e
|
||||||
|
render_error(e.message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def load_uncomplete_issues
|
||||||
|
@issues = Issue.where(pm_sprint_id: complete_params[:pm_project_sprint_id]).where.not(status_id: 5)
|
||||||
|
end
|
||||||
|
|
||||||
|
def complete_params
|
||||||
|
params.permit(:pm_project_sprint_id, :complete_type, :target_pm_project_sprint_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def query_params
|
||||||
|
params.permit(
|
||||||
|
:category,
|
||||||
|
:pm_project_id,
|
||||||
|
:pm_issue_type, # 需求1 任务2 缺陷3
|
||||||
|
:assigner_id,
|
||||||
|
:priority_id,
|
||||||
|
:status_id,
|
||||||
|
:keyword, :status_ids, :pm_issue_types,
|
||||||
|
:sort_by, :sort_direction
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -62,7 +62,7 @@ class Api::V1::BaseController < ApplicationController
|
||||||
|
|
||||||
# 具有对仓库的访问权限
|
# 具有对仓库的访问权限
|
||||||
def require_public_and_member_above
|
def require_public_and_member_above
|
||||||
@project = load_project
|
@project = load_project
|
||||||
return render_forbidden if !@project.is_public && !current_user.admin? && !@project.member?(current_user)
|
return render_forbidden if !@project.is_public && !current_user.admin? && !@project.member?(current_user)
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -7,12 +7,4 @@ class Api::V1::Issues::IssuePrioritiesController < Api::V1::BaseController
|
||||||
@priorities = @priorities.ransack(name_cont: params[:keyword]).result if params[:keyword]
|
@priorities = @priorities.ransack(name_cont: params[:keyword]).result if params[:keyword]
|
||||||
@priorities = kaminary_select_paginate(@priorities)
|
@priorities = kaminary_select_paginate(@priorities)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pm_index
|
|
||||||
@priorities = IssuePriority.order(position: :asc)
|
|
||||||
@priorities = @priorities.ransack(name_cont: params[:keyword]).result if params[:keyword]
|
|
||||||
@priorities = kaminary_select_paginate(@priorities)
|
|
||||||
render "index"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
|
@ -13,12 +13,7 @@ class Api::V1::Issues::IssueTagsController < Api::V1::BaseController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def pm_index
|
def create
|
||||||
@issue_tags = IssueTag.init_mp_issues_tags
|
|
||||||
render_ok(@issue_tags)
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
@issue_tag = @project.issue_tags.new(issue_tag_params)
|
@issue_tag = @project.issue_tags.new(issue_tag_params)
|
||||||
if @issue_tag.save!
|
if @issue_tag.save!
|
||||||
render_ok
|
render_ok
|
||||||
|
|
|
@ -46,7 +46,7 @@ class Api::V1::Issues::JournalsController < Api::V1::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_issue
|
def load_issue
|
||||||
@issue = @project.issues.issue_issue.where(project_issues_index: params[:index]).where.not(id: params[:index]).take || Issue.find_by_id(params[:index])
|
@issue = @project.issues.issue_issue.where(project_issues_index: params[:index]).where.not(id: params[:index]).take || @project.issues.issue_issue.find_by_id(params[:index])
|
||||||
if @issue.blank?
|
if @issue.blank?
|
||||||
render_not_found("疑修不存在!")
|
render_not_found("疑修不存在!")
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,11 +8,4 @@ class Api::V1::Issues::StatuesController < Api::V1::BaseController
|
||||||
@statues = @statues.ransack(name_cont: params[:keyword]).result if params[:keyword].present?
|
@statues = @statues.ransack(name_cont: params[:keyword]).result if params[:keyword].present?
|
||||||
@statues = kaminary_select_paginate(@statues)
|
@statues = kaminary_select_paginate(@statues)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pm_index
|
|
||||||
@statues = IssueStatus.order("position asc")
|
|
||||||
@statues = @statues.ransack(name_cont: params[:keyword]).result if params[:keyword].present?
|
|
||||||
@statues = kaminary_select_paginate(@statues)
|
|
||||||
render "index"
|
|
||||||
end
|
|
||||||
end
|
end
|
|
@ -70,7 +70,7 @@ class Api::V1::IssuesController < Api::V1::BaseController
|
||||||
private
|
private
|
||||||
|
|
||||||
def load_issue
|
def load_issue
|
||||||
@issue = @project.issues.issue_issue.where(project_issues_index: params[:index]).where.not(id: params[:index]).take || Issue.find_by_id(params[:index])
|
@issue = @project.issues.issue_issue.where(project_issues_index: params[:index]).where.not(id: params[:index]).take || @project.issues.issue_issue.find_by_id(params[:index])
|
||||||
if @issue.blank?
|
if @issue.blank?
|
||||||
render_not_found("疑修不存在!")
|
render_not_found("疑修不存在!")
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
class Api::V1::PmIssuesController < ApplicationController
|
||||||
|
before_action :require_login, except: [:index, :show]
|
||||||
|
|
||||||
|
def index
|
||||||
|
project = Project.find_by_id(params[:project_id]) || Project.new( id: 0, user_id: 0, name:"pm_mm", identifier:"pm_mm" )
|
||||||
|
object_result = Api::V1::Issues::ListService.call(@project, query_params, current_user)
|
||||||
|
@total_issues_count = @object_result[:total_issues_count]
|
||||||
|
@opened_issues_count = @object_result[:opened_issues_count]
|
||||||
|
@closed_issues_count = @object_result[:closed_issues_count]
|
||||||
|
if params[:only_name].present?
|
||||||
|
@issues = kaminary_select_paginate(@object_result[:data].select(:id, :subject, :project_issues_index, :updated_on, :created_on))
|
||||||
|
else
|
||||||
|
@issues = kaminari_paginate(@object_result[:data])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
project = Project.find_by_id(params[:project_id]) || Project.new( id: 0, user_id: 0, name:"pm_mm", identifier:"pm_mm" )
|
||||||
|
@object_result = Api::V1::Issues::CreateService.call(project, issue_params, current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def issue_params
|
||||||
|
params.permit(
|
||||||
|
:status_id, :priority_id, :milestone_id,
|
||||||
|
:branch_name, :start_date, :due_date,
|
||||||
|
:subject, :description, :blockchain_token_num,
|
||||||
|
:pm_project_id, :pm_sprint_id,
|
||||||
|
:issue_tag_ids => [],
|
||||||
|
:assigner_ids => [],
|
||||||
|
:attachment_ids => [],
|
||||||
|
:receivers_login => []
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -1,5 +1,63 @@
|
||||||
class Api::V1::Projects::Actions::ActionsController < Api::V1::Projects::Actions::BaseController
|
class Api::V1::Projects::Actions::ActionsController < Api::V1::Projects::Actions::BaseController
|
||||||
|
|
||||||
|
def new_index
|
||||||
|
@files = $gitea_client.get_repos_contents_by_owner_repo_filepath(@project&.owner&.login, @project&.identifier, ".gitea/workflows") rescue []
|
||||||
|
@workflows = params[:workflows].split(",") if params[:workflows].present?
|
||||||
|
@action_runs = Gitea::ActionRun.where(repo_id: @project.gpid)
|
||||||
|
@action_runs = @action_runs.where(id: params[:ids].split(",")) if params[:ids].present?
|
||||||
|
@action_runs = @action_runs.where(workflow_id: @workflows) if params[:workflows].present?
|
||||||
|
group_data = @action_runs.where(status: [1,2]).group(:workflow_id, :status).count
|
||||||
|
@result = []
|
||||||
|
@files.map{|i|i['name']}.each do |file|
|
||||||
|
if @workflows.present?
|
||||||
|
next if !@workflows.include?(file)
|
||||||
|
end
|
||||||
|
last_action_run = @action_runs.where(workflow_id: file).order(updated: :desc).first
|
||||||
|
last_action_run_json = last_action_run.present? ? {
|
||||||
|
id: last_action_run.id,
|
||||||
|
schedule: last_action_run.schedule_id > 0,
|
||||||
|
title: last_action_run.title,
|
||||||
|
index: last_action_run.index,
|
||||||
|
status: last_action_run.status,
|
||||||
|
started: last_action_run.started,
|
||||||
|
stopped: last_action_run.stopped,
|
||||||
|
length: last_action_run.stopped-last_action_run.started,
|
||||||
|
created: last_action_run.created,
|
||||||
|
updated: last_action_run.updated,
|
||||||
|
} : {}
|
||||||
|
|
||||||
|
total = 0
|
||||||
|
success = 0
|
||||||
|
failure = 0
|
||||||
|
group_data.each do |k,v|
|
||||||
|
total += v if k[0] == file
|
||||||
|
success += v if k[0] == file && k[1] == 1
|
||||||
|
failure += v if k[0] == file && k[1] == 2
|
||||||
|
end
|
||||||
|
|
||||||
|
pipeline_type = 1
|
||||||
|
begin
|
||||||
|
content = Gitea::Repository::Entries::GetService.call(@project&.owner, @project&.identifier, URI.escape(file), ref: last_action_run.present? ? last_action_run.ref.gsub("refs/heads/","") : @project.default_branch)['content']
|
||||||
|
yaml_string = Base64.decode64(content).force_encoding("GBK").encode("UTF-8") unless Base64.decode64(content).force_encoding('UTF-8').valid_encoding?
|
||||||
|
yaml_string = Base64.decode64(content).force_encoding('UTF-8')
|
||||||
|
yml = YAML.safe_load(yaml_string)
|
||||||
|
pipeline_type = yml.name == file.to_s.gsub(".yml","").gsub(".yaml","") ? 2 : 1
|
||||||
|
rescue
|
||||||
|
Rails.logger.info("#{file}不能识别流水线类型")
|
||||||
|
end
|
||||||
|
@result << {
|
||||||
|
filename: file,
|
||||||
|
name: file.to_s.gsub(".yml","").gsub(".yaml","") ,
|
||||||
|
branch: last_action_run.present? ? last_action_run.ref.gsub("refs/heads/","") : @project.default_branch,
|
||||||
|
pipeline_type: pipeline_type,
|
||||||
|
total: total,
|
||||||
|
success: success,
|
||||||
|
failure: failure
|
||||||
|
}.merge(last_action_run_json)
|
||||||
|
end
|
||||||
|
render :json => {data: @result}
|
||||||
|
end
|
||||||
|
|
||||||
def index
|
def index
|
||||||
begin
|
begin
|
||||||
gitea_result = $gitea_hat_client.get_repos_actions_by_owner_repo(@project&.owner&.login, @project&.identifier)
|
gitea_result = $gitea_hat_client.get_repos_actions_by_owner_repo(@project&.owner&.login, @project&.identifier)
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
class Api::V1::Projects::Actions::RunsController < Api::V1::Projects::Actions::BaseController
|
class Api::V1::Projects::Actions::RunsController < Api::V1::Projects::Actions::BaseController
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@result_object = Api::V1::Projects::Actions::Runs::ListService.call(@project, {workflow: params[:workflow], page: page, limit: limit}, current_user&.gitea_token)
|
@result_object = Api::V1::Projects::Actions::Runs::ListService.call(@project, {workflow: params[:workflow], page: page, limit: limit}, current_user&.gitea_token)
|
||||||
puts @result_object
|
@begin_num = (page.to_i - 1) * limit.to_i
|
||||||
|
# puts @result_object
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
class Api::V1::Projects::CommitsController < Api::V1::BaseController
|
class Api::V1::Projects::CommitsController < Api::V1::BaseController
|
||||||
before_action :require_public_and_member_above, only: [:index, :diff, :recent]
|
before_action :require_public_and_member_above, only: [:index, :diff, :recent, :files]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@result_object = Api::V1::Projects::Commits::ListService.call(@project, {page: page, limit: limit, sha: params[:sha]}, current_user&.gitea_token)
|
@result_object = Api::V1::Projects::Commits::ListService.call(@project, {page: page, limit: limit, sha: params[:sha]}, current_user&.gitea_token)
|
||||||
|
@ -10,6 +10,14 @@ class Api::V1::Projects::CommitsController < Api::V1::BaseController
|
||||||
@result_object = Api::V1::Projects::Commits::DiffService.call(@project, params[:sha], current_user&.gitea_token)
|
@result_object = Api::V1::Projects::Commits::DiffService.call(@project, params[:sha], current_user&.gitea_token)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def files
|
||||||
|
if params[:filepath].present?
|
||||||
|
@result_object = $gitea_hat_client.get_repos_commits_files_by_owner_repo_sha_filepath(@project&.owner.login, @project&.identifier, params[:sha], CGI.escape(params[:filepath]), {query: {token: current_user&.gitea_token}})
|
||||||
|
else
|
||||||
|
@result_object = $gitea_hat_client.get_repos_commits_files_by_owner_repo_sha(@project&.owner.login, @project&.identifier, params[:sha], {query: {token: current_user&.gitea_token, page: page, limit: limit}})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def recent
|
def recent
|
||||||
hash = Api::V1::Projects::Commits::RecentService.call(@project, {keyword: params[:keyword], page: page, limit: limit}, current_user&.gitea_token)
|
hash = Api::V1::Projects::Commits::RecentService.call(@project, {keyword: params[:keyword], page: page, limit: limit}, current_user&.gitea_token)
|
||||||
@result_object = hash[:result]
|
@result_object = hash[:result]
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
class Api::V1::Projects::CompareController < Api::V1::BaseController
|
||||||
|
|
||||||
|
before_action :require_public_and_member_above, only: [:files]
|
||||||
|
|
||||||
|
def files
|
||||||
|
load_compare_params
|
||||||
|
if params[:type] == "sha"
|
||||||
|
@compare_result ||= gitea_compare_files(@base, @head)
|
||||||
|
else
|
||||||
|
@compare_result ||= @head.include?(":") ? gitea_compare_files(@base, @head) : gitea_compare_files(@head, @base)
|
||||||
|
@merge_status, @merge_message = get_merge_message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def load_compare_params
|
||||||
|
@base = params[:base].include?(":") ? Addressable::URI.unescape(params[:base].split(":")[0]) + ':' + Base64.decode64(params[:base].split(":")[1]) : Base64.decode64(params[:base])
|
||||||
|
@head = params[:head].include?(":") ? Addressable::URI.unescape(params[:head].split(":")[0]) + ':' + Base64.decode64(params[:head].split(":")[1]) : Base64.decode64(params[:head])
|
||||||
|
end
|
||||||
|
|
||||||
|
def gitea_compare_files(base, head)
|
||||||
|
if params[:filepath].present?
|
||||||
|
$gitea_hat_client.get_repos_compare_by_owner_repo_baseref_headref(@project&.owner&.login, @project.identifier, Addressable::URI.escape(base), Addressable::URI.escape(head), {query: {token: current_user&.gitea_token, isFiles: true, filepath: params[:filepath]}})
|
||||||
|
else
|
||||||
|
$gitea_hat_client.get_repos_compare_by_owner_repo_baseref_headref(@project&.owner&.login, @project.identifier, Addressable::URI.escape(base), Addressable::URI.escape(head), {query: {page:page,limit:limit,token: current_user&.gitea_token, isFiles: true}})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_merge_message
|
||||||
|
if @base.blank? || @head.blank?
|
||||||
|
return -2, "请选择分支"
|
||||||
|
else
|
||||||
|
return -2, "目标仓库未开启合并请求(PR)功能" unless @project.has_menu_permission("pulls")
|
||||||
|
if @head.include?(":")
|
||||||
|
fork_project = @project.forked_projects.joins(:owner).where(users: {login: @head.to_s.split("/")[0]}).take
|
||||||
|
return -2, "请选择正确的仓库" unless fork_project.present?
|
||||||
|
@exist_pullrequest = @project.pull_requests.where(is_original: true, head: @head.to_s.split(":")[1], base: @base, status: 0, fork_project_id: fork_project.id).take
|
||||||
|
else
|
||||||
|
@exist_pullrequest = @project.pull_requests.where(is_original: false, head: @base, base: @head, status: 0).take
|
||||||
|
end
|
||||||
|
if @exist_pullrequest.present?
|
||||||
|
return -2, "在这些分支之间的合并请求已存在:<a href='/#{@owner.login}/#{@project.identifier}/pulls/#{@exist_pullrequest.id}'>#{@exist_pullrequest.try(:title)}</a>"
|
||||||
|
else
|
||||||
|
Rails.logger.info @compare_result
|
||||||
|
if params[:filepath].present?
|
||||||
|
if @compare_result["Commits"].blank? && @compare_result["Diff"].blank?
|
||||||
|
return -2, "分支内容相同,无需创建合并请求"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if @compare_result[:total_data].to_i < 1
|
||||||
|
return -2, "分支内容相同,无需创建合并请求"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return 0, "可以合并"
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,31 @@
|
||||||
|
class Api::V1::Projects::OssHealthMeasuringController < Api::V1::BaseController
|
||||||
|
|
||||||
|
def index
|
||||||
|
|
||||||
|
url = URI("#{EduSetting.get("ohm_server_url")}/api/OSS_Health_Measuring/#{params[:owner]}/#{params[:repo]}")
|
||||||
|
|
||||||
|
http = Net::HTTP.new(url.host, url.port);
|
||||||
|
request = Net::HTTP::Get.new(url)
|
||||||
|
response = http.request(request)
|
||||||
|
render :json=> response.read_body
|
||||||
|
end
|
||||||
|
|
||||||
|
def keyid
|
||||||
|
url = URI("#{EduSetting.get("ohm_server_url")}/api/OSS_Health_Measuring/#{params[:owner]}/#{params[:repo]}/#{params[:key_id]}")
|
||||||
|
|
||||||
|
http = Net::HTTP.new(url.host, url.port);
|
||||||
|
request = Net::HTTP::Get.new(url)
|
||||||
|
response = http.request(request)
|
||||||
|
render :json=> response.read_body
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_get
|
||||||
|
url = URI("#{EduSetting.get("ohm_server_url")}/api/OHM_can_get/#{params[:owner]}/#{params[:repo]}")
|
||||||
|
|
||||||
|
http = Net::HTTP.new(url.host, url.port);
|
||||||
|
request = Net::HTTP::Get.new(url)
|
||||||
|
response = http.request(request)
|
||||||
|
render :json=> response.read_body
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
File diff suppressed because it is too large
Load Diff
|
@ -6,13 +6,21 @@ class Api::V1::Projects::Pulls::PullsController < Api::V1::BaseController
|
||||||
@pulls = kaminari_paginate(@pulls)
|
@pulls = kaminari_paginate(@pulls)
|
||||||
end
|
end
|
||||||
|
|
||||||
before_action :load_pull_request, only: [:show]
|
before_action :load_pull_request, only: [:show, :files]
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@result_object = Api::V1::Projects::Pulls::GetService.call(@project, @pull_request, current_user&.gitea_token)
|
@result_object = Api::V1::Projects::Pulls::GetService.call(@project, @pull_request, current_user&.gitea_token)
|
||||||
@last_review = @pull_request.reviews.order(created_at: :desc).take
|
@last_review = @pull_request.reviews.order(created_at: :desc).take
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def files
|
||||||
|
if params[:filepath].present?
|
||||||
|
@result_object = $gitea_hat_client.get_repos_pulls_files_by_owner_repo_index_filepath(@project&.owner.login, @project&.identifier, @pull_request.gitea_number, CGI.escape(params[:filepath]), {query: {token: current_user&.gitea_token}})
|
||||||
|
else
|
||||||
|
@result_object = $gitea_hat_client.get_repos_pulls_files_by_owner_repo_index(@project&.owner.login, @project&.identifier, @pull_request.gitea_number, {query: {isNew: "true",token: current_user&.gitea_token, page: page, limit: limit}})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def query_params
|
def query_params
|
||||||
params.permit(:status, :keyword, :priority_id, :issue_tag_id, :version_id, :reviewer_id, :sort_by, :sort_direction)
|
params.permit(:status, :keyword, :priority_id, :issue_tag_id, :version_id, :reviewer_id, :sort_by, :sort_direction)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
class Api::V1::ProjectsController < Api::V1::BaseController
|
class Api::V1::ProjectsController < Api::V1::BaseController
|
||||||
before_action :require_public_and_member_above, only: [:show, :compare, :blame]
|
before_action :require_public_and_member_above, only: [:show, :compare, :blame, :sonar_search]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render_ok
|
render_ok
|
||||||
|
@ -9,6 +9,7 @@ class Api::V1::ProjectsController < Api::V1::BaseController
|
||||||
@result_object = Api::V1::Projects::GetService.call(@project, current_user.gitea_token)
|
@result_object = Api::V1::Projects::GetService.call(@project, current_user.gitea_token)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def compare
|
def compare
|
||||||
@result_object = Api::V1::Projects::CompareService.call(@project, params[:from], params[:to], current_user&.gitea_token)
|
@result_object = Api::V1::Projects::CompareService.call(@project, params[:from], params[:to], current_user&.gitea_token)
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
class Api::V1::SonarqubesController < Api::V1::BaseController
|
||||||
|
before_action :load_repository
|
||||||
|
def sonar_initialize
|
||||||
|
gitea_params = { has_actions: params[:has_actions] == 'true' ? true :false }
|
||||||
|
gitea_setting = Gitea::Repository::UpdateService.call(@owner, @project.identifier, gitea_params)
|
||||||
|
if gitea_setting['has_actions'] == true
|
||||||
|
Gitea::Repository::ActionSecretsService.new(@owner, @project.identifier, 'SONAR_HOST_URL', Rails.application.config_for(:configuration)['sonarqube']['url'] ).call
|
||||||
|
Gitea::Repository::ActionSecretsService.new(@owner, @project.identifier, 'SONAR_TOKEN', Rails.application.config_for(:configuration)['sonarqube']['secret'] ).call
|
||||||
|
else
|
||||||
|
Gitea::Repository::ActionSecretsService.new(@owner, @project.identifier, 'SONAR_HOST_URL', Rails.application.config_for(:configuration)['sonarqube']['url'] ).destroy
|
||||||
|
Gitea::Repository::ActionSecretsService.new(@owner, @project.identifier, 'SONAR_TOKEN', Rails.application.config_for(:configuration)['sonarqube']['secret'] ).destroy
|
||||||
|
end
|
||||||
|
@project.update(gitea_params)
|
||||||
|
render_ok
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_file
|
||||||
|
checkout_url = 'https://gitlink.org.cn/KingChan/checkout@v4'
|
||||||
|
scanner_url = 'https://gitlink.org.cn/KingChan/sonarqube-scan-action@master'
|
||||||
|
begin
|
||||||
|
config = Rails.application.config_for(:configuration)
|
||||||
|
sonarqube_config = config.dig('sonarqube')
|
||||||
|
|
||||||
|
if sonarqube_config.present? && sonarqube_config['checkout'].present?
|
||||||
|
checkout_url = sonarqube_config['checkout']
|
||||||
|
end
|
||||||
|
if sonarqube_config.present? && sonarqube_config['scanner'].present?
|
||||||
|
scanner_url = sonarqube_config['scanner']
|
||||||
|
end
|
||||||
|
|
||||||
|
raise 'sonar config missing' if sonarqube_config.blank?
|
||||||
|
rescue => ex
|
||||||
|
raise ex if Rails.env.production?
|
||||||
|
|
||||||
|
puts %Q{\033[33m [warning] soanrqube config or configuration.yml missing,
|
||||||
|
please add it or execute 'cp config/configuration.yml.example config/configuration.yml' \033[0m}
|
||||||
|
end
|
||||||
|
|
||||||
|
sonar_scanner_content = {
|
||||||
|
filepath: '.gitea/workflows/SonarScanner.yaml',
|
||||||
|
branch: params[:branch],
|
||||||
|
new_branch: nil,
|
||||||
|
content: "
|
||||||
|
on:
|
||||||
|
# Trigger analysis when pushing to your main branches, and when creating a pull request.
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
- 'releases/**'
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
name: Main Workflow
|
||||||
|
jobs:
|
||||||
|
sonarqube:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: #{checkout_url}
|
||||||
|
with:
|
||||||
|
# Disabling shallow clones is recommended for improving the relevancy of reporting
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: SonarQube Scan
|
||||||
|
uses: #{scanner_url}
|
||||||
|
env:
|
||||||
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
|
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
|
||||||
|
",
|
||||||
|
message: 'Add .gitea/workflows/SonarScanner.yaml',
|
||||||
|
committer: {
|
||||||
|
email: @owner.mail,
|
||||||
|
name: @owner.login
|
||||||
|
},
|
||||||
|
identifier: @project.identifier
|
||||||
|
}
|
||||||
|
@path = GiteaService.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{params[:branch]}/"
|
||||||
|
sonar_scanner_exit = Repositories::EntriesInteractor.call(@owner, @project.identifier, '.gitea/workflows/SonarScanner.yaml', ref: params[:branch])
|
||||||
|
if sonar_scanner_exit.success?
|
||||||
|
Gitea::UpdateFileInteractor.call(@owner.gitea_token, @owner.login, sonar_scanner_content.merge(sha:sonar_scanner_exit.result['sha']))
|
||||||
|
else
|
||||||
|
sonar_scanner_content[:content] = Base64.strict_encode64(sonar_scanner_content[:content])
|
||||||
|
Gitea::CreateFileInteractor.call(@owner.gitea_token, @owner.login, sonar_scanner_content)
|
||||||
|
end
|
||||||
|
|
||||||
|
sonar_project_content = {
|
||||||
|
filepath: 'sonar-project.properties',
|
||||||
|
branch: params[:branch],
|
||||||
|
new_branch: nil,
|
||||||
|
"content": "sonar.projectKey=#{params[:owner]}-#{@project.id}\nsonar.sources=.\nsonar.java.binaries=.",
|
||||||
|
"message": 'Add sonar-project.properties',
|
||||||
|
committer: {
|
||||||
|
email: @owner.mail,
|
||||||
|
name: @owner.login
|
||||||
|
},
|
||||||
|
identifier: @project.identifier
|
||||||
|
}
|
||||||
|
sonar_project_exit = Repositories::EntriesInteractor.call(@owner, @project.identifier, 'sonar-project.properties', ref: params[:branch])
|
||||||
|
if sonar_project_exit.success?
|
||||||
|
Gitea::UpdateFileInteractor.call(@owner.gitea_token, @owner.login, sonar_project_content.merge(sha:sonar_project_exit.result['sha']))
|
||||||
|
else
|
||||||
|
sonar_project_content[:content] = Base64.strict_encode64(sonar_project_content[:content])
|
||||||
|
Gitea::CreateFileInteractor.call(@owner.gitea_token, @owner.login, sonar_project_content)
|
||||||
|
end
|
||||||
|
render_ok
|
||||||
|
end
|
||||||
|
|
||||||
|
def issues_search
|
||||||
|
params_data = {
|
||||||
|
components: "#{params[:owner]}-#{@project.id}",
|
||||||
|
s: params[:s],
|
||||||
|
impactSoftwareQualities: params[:impactSoftwareQualities],
|
||||||
|
issueStatuses: params[:issueStatuses],
|
||||||
|
ps: params[:ps],
|
||||||
|
p: params[:p],
|
||||||
|
facets: params[:facets],
|
||||||
|
additionalFields: params[:additionalFields],
|
||||||
|
timeZone: params[:timeZone],
|
||||||
|
types: params[:types],
|
||||||
|
impactSeverities: params[:impactSeverities],
|
||||||
|
tags: params[:tags]
|
||||||
|
}
|
||||||
|
data = Sonarqube.client.get('/api/issues/search', query: params_data)
|
||||||
|
render_ok data
|
||||||
|
end
|
||||||
|
|
||||||
|
def ce_component
|
||||||
|
params_data = {
|
||||||
|
components: "#{params[:owner]}-#{@project.id}",
|
||||||
|
}
|
||||||
|
data = Sonarqube.client.get('/api/ce/component', query: params_data)
|
||||||
|
render_ok data
|
||||||
|
end
|
||||||
|
|
||||||
|
def sources_issue_snippet
|
||||||
|
params_data = {
|
||||||
|
issueKey: params[:issueKey]
|
||||||
|
}
|
||||||
|
data = Sonarqube.client.get('/api/sources/issue_snippets', query: params_data)
|
||||||
|
render_ok data
|
||||||
|
end
|
||||||
|
|
||||||
|
def rules_show
|
||||||
|
params_data = {
|
||||||
|
key: params[:key]
|
||||||
|
}
|
||||||
|
data = Sonarqube.client.get('/api/rules/show', query: params_data)
|
||||||
|
render_ok data
|
||||||
|
end
|
||||||
|
|
||||||
|
def measures_search_history
|
||||||
|
params_data = {
|
||||||
|
from: params[:form],
|
||||||
|
component: "#{params[:owner]}-#{@project.id}",
|
||||||
|
metrics: params[:metrics],
|
||||||
|
ps: params[:ps]
|
||||||
|
}
|
||||||
|
data = Sonarqube.client.get('/api/measures/search_history', query: params_data)
|
||||||
|
render_ok data
|
||||||
|
end
|
||||||
|
|
||||||
|
def measures_component
|
||||||
|
params_data = {
|
||||||
|
component: "#{params[:owner]}-#{@project.id}",
|
||||||
|
additionalFields: params[:additionalFields],
|
||||||
|
metricKeys: params[:metricKeys]
|
||||||
|
}
|
||||||
|
data = Sonarqube.client.get('/api/measures/component', query: params_data)
|
||||||
|
render_ok data
|
||||||
|
end
|
||||||
|
end
|
|
@ -8,6 +8,6 @@ class Api::V1::Users::ProjectsController < Api::V1::BaseController
|
||||||
|
|
||||||
private
|
private
|
||||||
def query_params
|
def query_params
|
||||||
params.permit(:category, :is_public, :project_type, :sort_by, :sort_direction, :search)
|
params.permit(:category, :is_public, :project_type, :sort_by, :sort_direction, :search, :start_at, :end_at)
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,4 +1,5 @@
|
||||||
class Api::V1::UsersController < Api::V1::BaseController
|
class Api::V1::UsersController < Api::V1::BaseController
|
||||||
|
include AesCryptHelper
|
||||||
|
|
||||||
before_action :load_observe_user, except: [:check_user_id, :check_user_login]
|
before_action :load_observe_user, except: [:check_user_id, :check_user_login]
|
||||||
before_action :check_auth_for_observe_user, except: [:check_user_id, :check_user_login]
|
before_action :check_auth_for_observe_user, except: [:check_user_id, :check_user_login]
|
||||||
|
@ -53,7 +54,7 @@ class Api::V1::UsersController < Api::V1::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_password
|
def check_password
|
||||||
password = params[:password]
|
password = decrypt(params[:password]) rescue ""
|
||||||
return tip_exception(-5, "8~16位密码,支持字母数字和符号") unless password =~ CustomRegexp::PASSWORD
|
return tip_exception(-5, "8~16位密码,支持字母数字和符号") unless password =~ CustomRegexp::PASSWORD
|
||||||
return tip_exception(-5, "密码错误") unless @observe_user.check_password?(password)
|
return tip_exception(-5, "密码错误") unless @observe_user.check_password?(password)
|
||||||
render_ok
|
render_ok
|
||||||
|
@ -115,4 +116,29 @@ class Api::V1::UsersController < Api::V1::BaseController
|
||||||
return render_error('更改手机号失败!')
|
return render_error('更改手机号失败!')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def check_user_can_delete
|
||||||
|
org_ids = TeamUser.where(user_id: @observe_user.id).pluck(:organization_id) | OrganizationUser.where(user_id: @observe_user.id).pluck(:organization_id)
|
||||||
|
org_count = TeamUser.where(organization_id: org_ids).where(user_id: @observe_user.id).joins(:team).where(teams: {authorize: %w(owner)}).count
|
||||||
|
project_count = Project.where(user_id: @observe_user.id).count
|
||||||
|
render_ok({ can_delete: org_count == 0 && project_count == 0, org_count: org_count, project_count: project_count })
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
password = decrypt(params[:password]) rescue ""
|
||||||
|
return tip_exception(-1, "密码不正确.") unless @observe_user.check_password?(password)
|
||||||
|
org_ids = TeamUser.where(user_id: @observe_user.id).pluck(:organization_id) | OrganizationUser.where(user_id: @observe_user.id).pluck(:organization_id)
|
||||||
|
org_count = TeamUser.where(organization_id: org_ids).where(user_id: @observe_user.id).joins(:team).where(teams: {authorize: %w(owner)}).count
|
||||||
|
project_count = Project.where(user_id: @observe_user.id).count
|
||||||
|
return tip_exception(-1, "当前账号名下存在拥有的组织/代码库,请先删除或转让后再尝试注销操作.") if org_count > 0 || project_count > 0
|
||||||
|
UserAction.create(action_id: @observe_user.id, action_type: "DestroyUser", user_id: @observe_user.id, :ip => request.remote_ip, data_bank: @observe_user.attributes.to_json, memo: params[:memo])
|
||||||
|
@result_object = Api::V1::Users::DeleteUserService.call(@observe_user)
|
||||||
|
if @result_object
|
||||||
|
return render_ok
|
||||||
|
else
|
||||||
|
return render_error('删除失败!')
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
|
@ -210,6 +210,10 @@ class ApplicationController < ActionController::Base
|
||||||
tip_exception(401, "请登录后再操作") unless User.current.logged?
|
tip_exception(401, "请登录后再操作") unless User.current.logged?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def require_referer
|
||||||
|
tip_exception(403, "你没有权限访问") if request.host.present? && !request.referer.to_s.include?(request.host.to_s.gsub("www.",""))
|
||||||
|
end
|
||||||
|
|
||||||
def require_login_or_token
|
def require_login_or_token
|
||||||
if params[:token].present?
|
if params[:token].present?
|
||||||
user = User.try_to_autologin(params[:token])
|
user = User.try_to_autologin(params[:token])
|
||||||
|
@ -326,19 +330,19 @@ class ApplicationController < ActionController::Base
|
||||||
User.current = find_current_user
|
User.current = find_current_user
|
||||||
uid_logger("user_setup: " + (User.current.logged? ? "#{User.current.try(:login)} (id=#{User.current.try(:id)})" : "anonymous"))
|
uid_logger("user_setup: " + (User.current.logged? ? "#{User.current.try(:login)} (id=#{User.current.try(:id)})" : "anonymous"))
|
||||||
|
|
||||||
# 开放课程通过链接访问的用户
|
# # 开放课程通过链接访问的用户
|
||||||
if !User.current.logged? && !params[:chinaoocTimestamp].blank? && !params[:websiteName].blank? && !params[:chinaoocKey].blank?
|
# if !User.current.logged? && !params[:chinaoocTimestamp].blank? && !params[:websiteName].blank? && !params[:chinaoocKey].blank?
|
||||||
content = "#{OPENKEY}#{params[:websiteName]}#{params[:chinaoocTimestamp]}"
|
# content = "#{OPENKEY}#{params[:websiteName]}#{params[:chinaoocTimestamp]}"
|
||||||
|
#
|
||||||
if Digest::MD5.hexdigest(content) == params[:chinaoocKey]
|
# if Digest::MD5.hexdigest(content) == params[:chinaoocKey]
|
||||||
user = open_class_user
|
# user = open_class_user
|
||||||
if user
|
# if user
|
||||||
start_user_session(user)
|
# start_user_session(user)
|
||||||
set_autologin_cookie(user)
|
# set_autologin_cookie(user)
|
||||||
end
|
# end
|
||||||
User.current = user
|
# User.current = user
|
||||||
end
|
# end
|
||||||
end
|
# end
|
||||||
|
|
||||||
if !User.current.logged? && Rails.env.development?
|
if !User.current.logged? && Rails.env.development?
|
||||||
user = User.find 1
|
user = User.find 1
|
||||||
|
@ -371,15 +375,14 @@ class ApplicationController < ActionController::Base
|
||||||
uid_logger("user setup start: session[:user_id] is #{session[:user_id]}")
|
uid_logger("user setup start: session[:user_id] is #{session[:user_id]}")
|
||||||
uid_logger("0000000000000user setup start: default_yun_session is #{default_yun_session}, session[:current_user_id] is #{session[:"#{default_yun_session}"]}")
|
uid_logger("0000000000000user setup start: default_yun_session is #{default_yun_session}, session[:current_user_id] is #{session[:"#{default_yun_session}"]}")
|
||||||
current_domain_session = session[:"#{default_yun_session}"]
|
current_domain_session = session[:"#{default_yun_session}"]
|
||||||
if current_domain_session
|
autologin_user = try_to_autologin
|
||||||
# existing session
|
uid_logger("user setup start: autologin_user is #{autologin_user}")
|
||||||
User.current = (User.active.find(current_domain_session) rescue nil)
|
# 多浏览器退出账号时,token不存在处理
|
||||||
elsif autologin_user = try_to_autologin
|
if current_domain_session && autologin_user.nil?
|
||||||
autologin_user
|
autologin_user = (User.active.find(current_domain_session) rescue nil)
|
||||||
elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth?
|
set_autologin_cookie(autologin_user) if autologin_user.present?
|
||||||
# RSS key authentication does not start a session
|
|
||||||
User.find_by_rss_key(params[:key])
|
|
||||||
end
|
end
|
||||||
|
autologin_user
|
||||||
end
|
end
|
||||||
|
|
||||||
def try_to_autologin
|
def try_to_autologin
|
||||||
|
|
|
@ -30,25 +30,33 @@ class AttachmentsController < ApplicationController
|
||||||
|
|
||||||
|
|
||||||
def get_file
|
def get_file
|
||||||
|
Rails.logger.info("request.host===#{request.host},request.referer===#{request.referer}")
|
||||||
|
tip_exception(403, "你没有权限访问") if request.host.present? && !request.referer.to_s.include?(request.host.to_s.gsub("www.",""))
|
||||||
normal_status(-1, "参数缺失") if params[:download_url].blank?
|
normal_status(-1, "参数缺失") if params[:download_url].blank?
|
||||||
url = base_url.starts_with?("https:") ? params[:download_url].to_s.gsub("http:", "https:") : params[:download_url].to_s
|
url = base_url.starts_with?("https:") ? params[:download_url].to_s.gsub("http:", "https:") : params[:download_url].to_s
|
||||||
|
md5_file = Digest::MD5.hexdigest(params[:download_url])
|
||||||
|
FileUtils.mkdir_p("#{Rails.root}#{EduSetting.get("attachment_folder")}gitea/") unless Dir.exists?("#{Rails.root}#{EduSetting.get("attachment_folder")}gitea/")
|
||||||
|
tmp_path = "#{Rails.root}#{EduSetting.get("attachment_folder")}gitea/#{Time.now.strftime('%Y%m%d')}-#{md5_file}"
|
||||||
if url.starts_with?(base_url) && !url.starts_with?("#{base_url}/repo")
|
if url.starts_with?(base_url) && !url.starts_with?("#{base_url}/repo")
|
||||||
domain = GiteaService.gitea_config[:domain]
|
domain = GiteaService.gitea_config[:domain]
|
||||||
api_url = GiteaService.gitea_config[:base_url]
|
api_url = GiteaService.gitea_config[:base_url]
|
||||||
url = ("/repos"+url.split(base_url + "/api")[1])
|
url = ("/repos"+url.split(base_url + "/api")[1])
|
||||||
filepath, ref = url.split("/")[-1].split("?")
|
filepath, ref = url.split("/")[-1].split("?")
|
||||||
url.gsub!(url.split("/")[-1], '')
|
url.gsub!(url.split("/")[-1], '')
|
||||||
Rails.logger.info("url===#{url}")
|
# Rails.logger.info("url===#{url}")
|
||||||
Rails.logger.info(filepath)
|
Rails.logger.info(filepath)
|
||||||
request_url = [domain, api_url, URI.encode(url), URI.escape(filepath), "?ref=#{URI.escape(ref.split('ref=')[1])}&access_token=#{User.where(admin: true).take&.gitea_token}"].join
|
ref = ref.blank? ? "" : URI.escape(ref.split('ref=')[1])
|
||||||
|
request_url = [domain, api_url, URI.encode(url), URI.escape(filepath), "?ref=#{ref}&access_token=#{User.where(admin: true).take&.gitea_token}"].join
|
||||||
Rails.logger.info("request_url===#{request_url}")
|
Rails.logger.info("request_url===#{request_url}")
|
||||||
response = Faraday.get(request_url)
|
File.delete(tmp_path) if File.exist?(tmp_path) # 删除之前的文件
|
||||||
|
Util.download_file(request_url, tmp_path)
|
||||||
filename = filepath
|
filename = filepath
|
||||||
else
|
else
|
||||||
response = Faraday.get(URI.encode(url))
|
File.delete(tmp_path) if File.exist?(tmp_path) # 删除之前的文件
|
||||||
|
Util.download_file(URI.encode(url), tmp_path)
|
||||||
filename = params[:download_url].to_s.split("/").pop()
|
filename = params[:download_url].to_s.split("/").pop()
|
||||||
end
|
end
|
||||||
send_data(response.body.force_encoding("UTF-8"), filename: filename, type: "application/octet-stream", disposition: 'attachment')
|
send_file(tmp_path, filename: filename, type: "application/octet-stream", disposition: 'attachment')
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
|
|
@ -26,6 +26,16 @@ class CommitLogsController < ApplicationController
|
||||||
commit_log.project_trends.create(user_id: user.id, project_id: project&.id, action_type: "create") if user.id !=2
|
commit_log.project_trends.create(user_id: user.id, project_id: project&.id, action_type: "create") if user.id !=2
|
||||||
# 统计数据新增
|
# 统计数据新增
|
||||||
CacheAsyncSetJob.perform_later("project_common_service", {commits: 1}, project.id)
|
CacheAsyncSetJob.perform_later("project_common_service", {commits: 1}, project.id)
|
||||||
|
|
||||||
|
commit_user = User.find_by(mail: commit[:committer][:email]) rescue nil
|
||||||
|
commit_user = User.find_by(login: commit[:committer][:name]) if commit_user.blank? rescue nil
|
||||||
|
next if commit_user.blank?
|
||||||
|
|
||||||
|
# 触发变更issue状态的job
|
||||||
|
close_issue_content = message.to_s.scan(/\b(Close|Closes|Closed|Closing|close|closes|closed|closing)\s*(#\d+(,\s*#\d+)*)?\b/)
|
||||||
|
ChangeIssueStatusByMessageJob.perform_later(commit_id, project, commit_user, close_issue_content[0][1], 5) if close_issue_content[0].present? && close_issue_content[0][1].present?
|
||||||
|
solve_issue_content = message.to_s.scan(/\b(Fix|Fixes|Fixed|Fixing|fix|fixes|fixed|fixing|Resolve|Resolves|Resolved|Resolving|resolve|resolves|resolved|resolving|Implement|Implements|Implemented|Implementing|implement|implements|implemented|implementing)\s*(#\d+(,\s*#\d+)*)?\b/)
|
||||||
|
ChangeIssueStatusByMessageJob.perform_later(commit_id, project, commit_user, solve_issue_content[0][1], 3) if solve_issue_content[0].present? && solve_issue_content[0][1].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,6 +18,7 @@ class CompareController < ApplicationController
|
||||||
@page_limit = page_limit <=0 ? 15 : page_limit
|
@page_limit = page_limit <=0 ? 15 : page_limit
|
||||||
@page_offset = (@page_size -1) * @page_limit
|
@page_offset = (@page_size -1) * @page_limit
|
||||||
Rails.logger.info("+========#{@page_size}-#{@page_limit}-#{@page_offset}")
|
Rails.logger.info("+========#{@page_size}-#{@page_limit}-#{@page_offset}")
|
||||||
|
Rails.logger.info @compare_result
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -36,7 +37,7 @@ class CompareController < ApplicationController
|
||||||
if @exist_pullrequest.present?
|
if @exist_pullrequest.present?
|
||||||
return -2, "在这些分支之间的合并请求已存在:<a href='/#{@owner.login}/#{@project.identifier}/pulls/#{@exist_pullrequest.id}'>#{@exist_pullrequest.try(:title)}</a>"
|
return -2, "在这些分支之间的合并请求已存在:<a href='/#{@owner.login}/#{@project.identifier}/pulls/#{@exist_pullrequest.id}'>#{@exist_pullrequest.try(:title)}</a>"
|
||||||
else
|
else
|
||||||
if @compare_result["Commits"].blank? && @compare_result["Diff"].blank?
|
if @compare_result["FilesCount"].to_i == 0 && @compare_result["CommitsCount"].to_i == 0
|
||||||
return -2, "分支内容相同,无需创建合并请求"
|
return -2, "分支内容相同,无需创建合并请求"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@ module Api::PullHelper
|
||||||
|
|
||||||
def load_pull_request
|
def load_pull_request
|
||||||
pull_request_id = params[:pull_id] || params[:id]
|
pull_request_id = params[:pull_id] || params[:id]
|
||||||
@pull_request = @project.pull_requests.where(gitea_number: pull_request_id).where.not(id: pull_request_id).take || PullRequest.find_by_id(pull_request_id)
|
@pull_request = @project.pull_requests.where(gitea_number: pull_request_id).where.not(id: pull_request_id).take || @project.pull_requests.find_by_id(pull_request_id)
|
||||||
@issue = @pull_request&.issue
|
@issue = @pull_request&.issue
|
||||||
if @pull_request
|
if @pull_request
|
||||||
logger.info "###########pull_request founded"
|
logger.info "###########pull_request founded"
|
||||||
|
|
|
@ -77,4 +77,12 @@ module GitHelper
|
||||||
cha_path = path.present? ? path.split(";") : []
|
cha_path = path.present? ? path.split(";") : []
|
||||||
cha_path.reject(&:blank?)[0].try(:strip)
|
cha_path.reject(&:blank?)[0].try(:strip)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def expain_issue_commit(commit_message)
|
||||||
|
respace_arr= commit_message.to_s.scan(/#(\d+)/).map{|s|[s[0], "##{s[0]}"]}.uniq.sort_by{|s|-s[0].to_i}
|
||||||
|
respace_arr.each do |item|
|
||||||
|
issue = Issue.find_by_id(item[0].to_i)
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
|
@ -73,6 +73,17 @@ module LoginHelper
|
||||||
session[:"#{default_yun_session}"] = nil
|
session[:"#{default_yun_session}"] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def clear_user_cookie
|
||||||
|
if edu_setting('cookie_domain').present?
|
||||||
|
cookies.delete(autologin_cookie_name, domain: edu_setting('cookie_domain'))
|
||||||
|
else
|
||||||
|
cookies.delete(autologin_cookie_name)
|
||||||
|
end
|
||||||
|
# 清除前端写入的用户名
|
||||||
|
Rails.logger.info("########________cookies['login']___________###########{cookies['login']}")
|
||||||
|
cookies.delete("login")
|
||||||
|
end
|
||||||
|
|
||||||
# Sets the logged in user
|
# Sets the logged in user
|
||||||
def logged_user=(user)
|
def logged_user=(user)
|
||||||
reset_session
|
reset_session
|
||||||
|
|
|
@ -50,7 +50,7 @@ class MarkFilesController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_pull_request
|
def load_pull_request
|
||||||
@pull_request = @project.pull_requests.where(gitea_number: params[:id]).where.not(id: params[:id]).take || PullRequest.find_by_id(params[:id])
|
@pull_request = @project.pull_requests.where(gitea_number: params[:id]).where.not(id: params[:id]).take || @project.pull_requests.find_by_id(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
|
@ -0,0 +1,53 @@
|
||||||
|
class Oauth::Ci4sController < Oauth::BaseController
|
||||||
|
include RegisterHelper
|
||||||
|
|
||||||
|
|
||||||
|
def oauth_url
|
||||||
|
redirect_to Ci4s::Service.oauth_url
|
||||||
|
end
|
||||||
|
|
||||||
|
# 需要educoder那边设置回调地址
|
||||||
|
def create
|
||||||
|
begin
|
||||||
|
code = params['code'].to_s.strip
|
||||||
|
tip_exception("code不能为空") if code.blank?
|
||||||
|
|
||||||
|
new_user = false
|
||||||
|
token = Ci4s::Service.access_token(code)
|
||||||
|
Rails.logger.info("[OAuth2] result -> #{token}")
|
||||||
|
result = Ci4s::Service.user_info(token[:access_token])
|
||||||
|
tip_exception("请求用户信息错误") if result['code'].to_i != 200
|
||||||
|
user_info = result['data']
|
||||||
|
# 存在该用户
|
||||||
|
open_user = OpenUsers::Ci4s.find_by(uid: user_info['username'])
|
||||||
|
if open_user.present? && open_user.user.present?
|
||||||
|
successful_authentication(open_user.user)
|
||||||
|
redirect_to root_path(new_user: false)
|
||||||
|
return
|
||||||
|
else
|
||||||
|
if current_user.blank? || !current_user.logged?
|
||||||
|
new_user = true
|
||||||
|
session[:unionid] = user_info['username']
|
||||||
|
# login = User.generate_login('E')
|
||||||
|
login = user_info['username']
|
||||||
|
email = user_info['email']
|
||||||
|
email = "#{login}@forge.com" if email.blank?
|
||||||
|
reg_result = autologin_register(login, email, "Ec#{login}2021#", 'educoder', user_info['mobile'])
|
||||||
|
Rails.logger.info("[OAuth2] reg_result -> #{reg_result}")
|
||||||
|
if reg_result[:message].blank?
|
||||||
|
open_user = OpenUsers::Ci4s.create!(user_id: reg_result[:user][:id], uid: login, extra: user_info)
|
||||||
|
successful_authentication(open_user.user)
|
||||||
|
else
|
||||||
|
tip_exception(reg_result[:message])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
OpenUsers::Ci4s.create!(user: current_user, uid: user_info['username'], extra: user_info)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
Rails.logger.info("[OAuth2] session[:unionid] -> #{session[:unionid]}")
|
||||||
|
redirect_to root_path(new_user: new_user)
|
||||||
|
rescue Exception => ex
|
||||||
|
render_error(ex.message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -45,6 +45,9 @@ class Oauth::EducoderController < Oauth::BaseController
|
||||||
result = EducoderOauth::Service.access_token(code)
|
result = EducoderOauth::Service.access_token(code)
|
||||||
result = EducoderOauth::Service.user_info(result[:access_token])
|
result = EducoderOauth::Service.user_info(result[:access_token])
|
||||||
|
|
||||||
|
Rails.logger.info("OAuth2-session-unionid====111=======#{result['login']}")
|
||||||
|
tip_exception("调用头歌接口错误") if result['login'].blank?
|
||||||
|
|
||||||
# 存在该用户
|
# 存在该用户
|
||||||
open_user = OpenUsers::Educoder.find_by(uid: result['login'])
|
open_user = OpenUsers::Educoder.find_by(uid: result['login'])
|
||||||
if open_user.present? && open_user.user.present?
|
if open_user.present? && open_user.user.present?
|
||||||
|
@ -68,7 +71,7 @@ class Oauth::EducoderController < Oauth::BaseController
|
||||||
OpenUsers::Educoder.create!(user: current_user, uid: result['login'], extra: result)
|
OpenUsers::Educoder.create!(user: current_user, uid: result['login'], extra: result)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Rails.logger.info("[OAuth2] session[:unionid] -> #{session[:unionid]}")
|
Rails.logger.info("OAuth2-session-unionid=========== #{session[:unionid]}")
|
||||||
redirect_to "/bindlogin/educoder"
|
redirect_to "/bindlogin/educoder"
|
||||||
|
|
||||||
# redirect_to root_path(new_user: new_user)
|
# redirect_to root_path(new_user: new_user)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
class Oauth2Controller < ActionController::Base
|
class Oauth2Controller < ApplicationController
|
||||||
layout 'doorkeeper/application'
|
layout 'doorkeeper/application'
|
||||||
include LoginHelper
|
include LoginHelper
|
||||||
|
|
||||||
|
@ -6,6 +6,9 @@ class Oauth2Controller < ActionController::Base
|
||||||
client_id = params[:call_url].split("client_id=")[1].split("&redirect_uri")[0]
|
client_id = params[:call_url].split("client_id=")[1].split("&redirect_uri")[0]
|
||||||
@call_url = request.fullpath.split('call_url=').last
|
@call_url = request.fullpath.split('call_url=').last
|
||||||
@app = Doorkeeper::Application.find_by(uid: client_id)
|
@app = Doorkeeper::Application.find_by(uid: client_id)
|
||||||
|
if User.current.logged?
|
||||||
|
redirect_to @call_url + "&auth=" + User.current.login
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
class Organizations::OrganizationsController < Organizations::BaseController
|
class Organizations::OrganizationsController < Organizations::BaseController
|
||||||
|
include AesCryptHelper
|
||||||
before_action :require_login, except: [:index, :show, :recommend, :languages]
|
before_action :require_login, except: [:index, :show, :recommend, :languages]
|
||||||
# before_action :require_profile_completed, only: [:create]
|
# before_action :require_profile_completed, only: [:create]
|
||||||
before_action :convert_image!, only: [:create, :update]
|
before_action :convert_image!, only: [:create, :update]
|
||||||
|
@ -139,7 +140,7 @@ class Organizations::OrganizationsController < Organizations::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def password
|
def password
|
||||||
params.fetch(:password, "")
|
decrypt(params[:password]) rescue ""
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_organization
|
def load_organization
|
||||||
|
|
|
@ -10,6 +10,9 @@ class Organizations::ProjectsController < Organizations::BaseController
|
||||||
@projects = Project.from("( #{ public_projects_sql} UNION #{ private_projects_sql } ) AS projects")
|
@projects = Project.from("( #{ public_projects_sql} UNION #{ private_projects_sql } ) AS projects")
|
||||||
# 表情处理
|
# 表情处理
|
||||||
keywords = params[:search].to_s.each_char.select { |c| c.bytes.first < 240 }.join('')
|
keywords = params[:search].to_s.each_char.select { |c| c.bytes.first < 240 }.join('')
|
||||||
|
@projects = @projects.where(id: params[:pm_project_repository_ids].split(',')) if params[:pm_project_repository_ids].present?
|
||||||
|
@projects = @projects.where.not(id: params[:exclude_ids].to_s.split(",")) if params[:exclude_ids].present?
|
||||||
|
@projects = @projects.where(project_type: ['mirror', 'common']).where("gpid is not null") if params[:actived].present?
|
||||||
@projects = @projects.ransack(name_or_identifier_cont: keywords).result if params[:search].present?
|
@projects = @projects.ransack(name_or_identifier_cont: keywords).result if params[:search].present?
|
||||||
@projects = @projects.includes(:owner).order("projects.#{sort} #{sort_direction}")
|
@projects = @projects.includes(:owner).order("projects.#{sort} #{sort_direction}")
|
||||||
@projects = paginate(@projects)
|
@projects = paginate(@projects)
|
||||||
|
|
|
@ -11,7 +11,10 @@ class OwnersController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@owner = Owner.find_by(login: params[:id]) || Owner.find_by(id: params[:id])
|
# login = params[:id].to_s[0..-6]
|
||||||
|
login = params[:id].to_s
|
||||||
|
@owner = Owner.find_by(login: login) || Owner.find_by(id: login)
|
||||||
|
clear_user_cookie unless @owner.present?
|
||||||
return render_not_found unless @owner.present?
|
return render_not_found unless @owner.present?
|
||||||
# 组织
|
# 组织
|
||||||
if @owner.is_a?(Organization)
|
if @owner.is_a?(Organization)
|
||||||
|
|
|
@ -43,11 +43,11 @@ class ProjectsController < ApplicationController
|
||||||
|
|
||||||
category_id = params[:category_id]
|
category_id = params[:category_id]
|
||||||
@total_count =
|
@total_count =
|
||||||
if category_id.blank? && params[:search].blank? && params[:topic_id].blank?
|
if category_id.blank? && params[:search].blank? && params[:topic_id].blank? && params[:topic_name].blank?
|
||||||
# 默认查询时count性能问题处理
|
# 默认查询时count性能问题处理
|
||||||
not_category_count = Project.where(project_category_id: nil).count
|
not_category_count = Project.where(project_category_id: nil).count
|
||||||
ProjectCategory.sum("projects_count") - Project.visible.joins("left join organization_extensions on organization_extensions.organization_id = projects.user_id").where("organization_extensions.visibility =2").count + not_category_count
|
ProjectCategory.sum("projects_count") - Project.visible.joins("left join organization_extensions on organization_extensions.organization_id = projects.user_id").where("organization_extensions.visibility =2").count + not_category_count
|
||||||
elsif params[:search].present? || params[:topic_id].present?
|
elsif params[:search].present? || params[:topic_id].present? || params[:topic_name].present?
|
||||||
@projects.total_count
|
@projects.total_count
|
||||||
else
|
else
|
||||||
cate = ProjectCategory.find_by(id: category_id)
|
cate = ProjectCategory.find_by(id: category_id)
|
||||||
|
@ -250,18 +250,10 @@ class ProjectsController < ApplicationController
|
||||||
def show
|
def show
|
||||||
end
|
end
|
||||||
|
|
||||||
def mp_show
|
|
||||||
@project = Project.joins(:owner).find params[:project_id]
|
|
||||||
data={
|
|
||||||
owner:@project.owner.try(:login),
|
|
||||||
identifier:@project.identifier
|
|
||||||
}
|
|
||||||
render_ok(data:data)
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
if current_user.admin? || @project.manager?(current_user)
|
if current_user.admin? || @project.manager?(current_user)
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
|
UserAction.create(action_id: @project.id, action_type: "DestroyProject", user_id: current_user.id, :ip => request.remote_ip, data_bank: @project.attributes.to_json)
|
||||||
close_fork_pull_requests_by(@project)
|
close_fork_pull_requests_by(@project)
|
||||||
Gitea::Repository::DeleteService.new(@project.owner, @project.identifier,current_user.gitea_token).call
|
Gitea::Repository::DeleteService.new(@project.owner, @project.identifier,current_user.gitea_token).call
|
||||||
@project.destroy!
|
@project.destroy!
|
||||||
|
@ -295,13 +287,19 @@ class ProjectsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def watch_users
|
def watch_users
|
||||||
|
start_at = params[:start_at]
|
||||||
|
end_at = params[:end_at]
|
||||||
watchers = @project.watchers.includes(:user).order("watchers.created_at desc").distinct
|
watchers = @project.watchers.includes(:user).order("watchers.created_at desc").distinct
|
||||||
|
watchers = watchers.where("watchers.created_at > ? and watchers.created_at < ?", Time.at(start_at.to_i), Time.at(end_at.to_i)) if start_at.present? && end_at.present?
|
||||||
@watchers_count = watchers.size
|
@watchers_count = watchers.size
|
||||||
@watchers = paginate(watchers)
|
@watchers = paginate(watchers)
|
||||||
end
|
end
|
||||||
|
|
||||||
def praise_users
|
def praise_users
|
||||||
|
start_at = params[:start_at]
|
||||||
|
end_at = params[:end_at]
|
||||||
praises = @project.praise_treads.includes(:user).order("praise_treads.created_at desc").distinct
|
praises = @project.praise_treads.includes(:user).order("praise_treads.created_at desc").distinct
|
||||||
|
praises = praises.where("praise_treads.created_at > ? and praise_treads.created_at < ?", Time.at(start_at.to_i), Time.at(end_at.to_i)) if start_at.present? && end_at.present?
|
||||||
@praises_count = praises.size
|
@praises_count = praises.size
|
||||||
@praises = paginate(praises)
|
@praises = paginate(praises)
|
||||||
end
|
end
|
||||||
|
@ -314,8 +312,8 @@ class ProjectsController < ApplicationController
|
||||||
|
|
||||||
def simple
|
def simple
|
||||||
if !@project.common? && @project&.repository&.mirror&.waiting?
|
if !@project.common? && @project&.repository&.mirror&.waiting?
|
||||||
gitea_result = $gitea_client.get_repos_by_owner_repo(@project&.owner&.login, @project&.identifier)
|
gitea_result = $gitea_client.get_repos_by_owner_repo(@project&.owner&.login, @project&.identifier) rescue nil
|
||||||
if !gitea_result["empty"]
|
if gitea_result.present? && !gitea_result["empty"]
|
||||||
@project&.update_columns(gpid: gitea_result["id"])
|
@project&.update_columns(gpid: gitea_result["id"])
|
||||||
@project&.repository&.mirror&.succeeded!
|
@project&.repository&.mirror&.succeeded!
|
||||||
project_id = @project&.id
|
project_id = @project&.id
|
||||||
|
@ -374,7 +372,7 @@ class ProjectsController < ApplicationController
|
||||||
if @project_detail.save!
|
if @project_detail.save!
|
||||||
attachment_ids = Array(params[:attachment_ids])
|
attachment_ids = Array(params[:attachment_ids])
|
||||||
logger.info "=============> #{Array(params[:attachment_ids])}"
|
logger.info "=============> #{Array(params[:attachment_ids])}"
|
||||||
@attachments = Attachment.where(id: attachment_ids)
|
@attachments = Attachment.where(id: attachment_ids).or(Attachment.where(uuid: attachment_ids))
|
||||||
@attachments.update_all(
|
@attachments.update_all(
|
||||||
container_id: @project_detail.id,
|
container_id: @project_detail.id,
|
||||||
container_type: @project_detail.model_name.name,
|
container_type: @project_detail.model_name.name,
|
||||||
|
@ -395,7 +393,7 @@ class ProjectsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def mirror_params
|
def mirror_params
|
||||||
params.permit(:user_id, :name, :description, :repository_name, :is_mirror, :auth_username, :auth_token,
|
params.permit(:user_id, :name, :description, :repository_name, :is_mirror, :auth_username, :auth_token, :service,
|
||||||
:auth_password, :project_category_id, :project_language_id, :clone_addr, :private)
|
:auth_password, :project_category_id, :project_language_id, :clone_addr, :private)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -296,11 +296,11 @@ class PullRequestsController < ApplicationController
|
||||||
|
|
||||||
private
|
private
|
||||||
def load_pull_request
|
def load_pull_request
|
||||||
@pull_request = @project.pull_requests.where(gitea_number: params[:id]).where.not(id: params[:id]).take || PullRequest.find_by_id(params[:id])
|
@pull_request = @project.pull_requests.where(gitea_number: params[:id]).where.not(id: params[:id]).take || @project.pull_requests.find_by_id(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_pull_request
|
def find_pull_request
|
||||||
@pull_request = @project.pull_requests.where(gitea_number: params[:id]).where.not(id: params[:id]).take || PullRequest.find_by_id(params[:id])
|
@pull_request = @project.pull_requests.where(gitea_number: params[:id]).where.not(id: params[:id]).take || @project.pull_requests.find_by_id(params[:id])
|
||||||
@issue = @pull_request&.issue
|
@issue = @pull_request&.issue
|
||||||
if @pull_request.blank?
|
if @pull_request.blank?
|
||||||
normal_status(-1, "合并请求不存在")
|
normal_status(-1, "合并请求不存在")
|
||||||
|
|
|
@ -13,6 +13,7 @@ class RepositoriesController < ApplicationController
|
||||||
before_action :get_ref, only: %i[entries sub_entries top_counts files archive]
|
before_action :get_ref, only: %i[entries sub_entries top_counts files archive]
|
||||||
before_action :get_latest_commit, only: %i[entries sub_entries top_counts]
|
before_action :get_latest_commit, only: %i[entries sub_entries top_counts]
|
||||||
before_action :get_statistics, only: %i[top_counts]
|
before_action :get_statistics, only: %i[top_counts]
|
||||||
|
before_action :require_referer, only: [:raw]
|
||||||
|
|
||||||
def files
|
def files
|
||||||
result = @project.educoder? ? nil : Gitea::Repository::Files::GetService.call(@owner, @project.identifier, @ref, params[:search], @owner.gitea_token)
|
result = @project.educoder? ? nil : Gitea::Repository::Files::GetService.call(@owner, @project.identifier, @ref, params[:search], @owner.gitea_token)
|
||||||
|
@ -150,8 +151,8 @@ class RepositoriesController < ApplicationController
|
||||||
return render_error('暂未开放,敬请期待.')
|
return render_error('暂未开放,敬请期待.')
|
||||||
else
|
else
|
||||||
@commit = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, @owner&.gitea_token)
|
@commit = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, @owner&.gitea_token)
|
||||||
@commit_diff = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, @owner&.gitea_token, {diff: true})
|
# @commit_diff = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, @owner&.gitea_token, {diff: true})
|
||||||
render_error(@commit[:message], @commit[:status]) if @commit.has_key?(:status) || @commit_diff.has_key?(:status)
|
render_error(@commit[:message], @commit[:status]) if @commit.has_key?(:status)#|| @commit_diff.has_key?(:status)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ class ReviewsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_pull_request
|
def load_pull_request
|
||||||
@pull_request = @project.pull_requests.where(gitea_number: params[:id]).where.not(id: params[:id]).take || PullRequest.find_by_id(params[:id])
|
@pull_request = @project.pull_requests.where(gitea_number: params[:id]).where.not(id: params[:id]).take || @project.pull_requests.find_by_id(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
|
@ -54,9 +54,9 @@ class SitePagesController < ApplicationController
|
||||||
user = User.find_by_login params[:repository][:owner][:login]
|
user = User.find_by_login params[:repository][:owner][:login]
|
||||||
return normal_status(-1, '你还未开通Page服务,无法进行部署') unless user.website_permission
|
return normal_status(-1, '你还未开通Page服务,无法进行部署') unless user.website_permission
|
||||||
|
|
||||||
project = Project.where(identifier: params[:repository][:name],user_id: user.id)
|
project = Project.find_by(identifier: params[:repository][:name],user_id: user.id)
|
||||||
return normal_status(-1, '你没有权限操作') if project.owner?(user)
|
return normal_status(-1, '项目不存在') if project.nil?
|
||||||
return normal_status(-1, '该仓库还未开通Page服务,无法进行部署') if Page.exists?(user: user, project: project)
|
return normal_status(-1, '该仓库还未开通Page服务,无法进行部署') unless Page.exists?(user: user, project: project)
|
||||||
|
|
||||||
@page = Page.find_by(user: user, project: project)
|
@page = Page.find_by(user: user, project: project)
|
||||||
response_str = @page.deploy_page(branch)
|
response_str = @page.deploy_page(branch)
|
||||||
|
|
|
@ -20,6 +20,6 @@ class Users::ProjectsController < Users::BaseController
|
||||||
private
|
private
|
||||||
|
|
||||||
def query_params
|
def query_params
|
||||||
params.permit(:category, :status, :sort_direction)
|
params.permit(:category, :status, :sort_direction, :topic_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -714,6 +714,7 @@ class UsersController < ApplicationController
|
||||||
private
|
private
|
||||||
def load_user
|
def load_user
|
||||||
@user = User.find_by_login(params[:id]) || User.find_by(id: params[:id])
|
@user = User.find_by_login(params[:id]) || User.find_by(id: params[:id])
|
||||||
|
clear_user_cookie unless @user.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_params
|
def user_params
|
||||||
|
|
|
@ -5,7 +5,10 @@ class WatchersController < ApplicationController
|
||||||
before_action :get_target
|
before_action :get_target
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
start_at = params[:start_at]
|
||||||
|
end_at = params[:end_at]
|
||||||
scope = @target.watchers.includes(:user)
|
scope = @target.watchers.includes(:user)
|
||||||
|
scope = scope.where("watchers.created_at > ? and watchers.created_at < ?", Time.at(start_at.to_i), Time.at(end_at.to_i)) if start_at.present? && end_at.present?
|
||||||
@watchers = paginate(scope)
|
@watchers = paginate(scope)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
class BaseForm
|
class BaseForm
|
||||||
include ActiveModel::Model
|
include ActiveModel::Model
|
||||||
|
include AesCryptHelper
|
||||||
|
|
||||||
Error = Class.new(StandardError)
|
Error = Class.new(StandardError)
|
||||||
EmailError = Class.new(Error)
|
EmailError = Class.new(Error)
|
||||||
|
@ -53,11 +54,15 @@ class BaseForm
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_password(password)
|
def check_password(password)
|
||||||
|
password = decrypt(password) rescue ""
|
||||||
password = strip(password)
|
password = strip(password)
|
||||||
raise PasswordFormatError, "密码8~16位密码,支持字母数字和符号" unless password =~ CustomRegexp::PASSWORD
|
raise PasswordFormatError, "密码8~16位密码,支持字母数字和符号" unless password =~ CustomRegexp::PASSWORD
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_password_confirmation(password, password_confirmation)
|
def check_password_confirmation(password, password_confirmation)
|
||||||
|
password = decrypt(password) rescue ""
|
||||||
|
password_confirmation = decrypt(password_confirmation) rescue ""
|
||||||
|
|
||||||
password = strip(password)
|
password = strip(password)
|
||||||
password_confirmation = strip(password_confirmation)
|
password_confirmation = strip(password_confirmation)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
class Projects::MigrateForm < BaseForm
|
class Projects::MigrateForm < BaseForm
|
||||||
attr_accessor :user_id, :name, :repository_name, :project_category_id, :description, :auth_token,
|
attr_accessor :user_id, :name, :repository_name, :project_category_id, :description, :auth_token, :service,
|
||||||
:project_language_id, :clone_addr, :private, :is_mirror, :auth_username, :auth_password, :owner
|
:project_language_id, :clone_addr, :private, :is_mirror, :auth_username, :auth_password, :owner
|
||||||
|
|
||||||
validates :user_id, :name, :repository_name, :clone_addr, presence: true
|
validates :user_id, :name, :repository_name, :clone_addr, presence: true
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
module AesCryptHelper
|
||||||
|
|
||||||
|
AES_KEY = EduSetting.get("login_crypt_key") || '59c96c3572ab8cc1'
|
||||||
|
|
||||||
|
def encrypt(plain_text, output_encoding = 'base64')
|
||||||
|
|
||||||
|
# 将字符串密钥和IV转换为16字节的字节数组
|
||||||
|
key = AES_KEY.byteslice(0, 16)
|
||||||
|
iv = AES_KEY.byteslice(0, 16)
|
||||||
|
|
||||||
|
# 创建并设置AES-CBC加密器
|
||||||
|
cipher = OpenSSL::Cipher.new('AES-128-CBC')
|
||||||
|
cipher.encrypt
|
||||||
|
cipher.key = key
|
||||||
|
cipher.iv = iv
|
||||||
|
|
||||||
|
# 加密数据,并添加PKCS7填充
|
||||||
|
encrypted_data = cipher.update(plain_text) + cipher.final
|
||||||
|
# 将加密数据转换为Base64编码
|
||||||
|
Base64.strict_encode64(encrypted_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def decrypt(cipher_text, input_encoding = 'base64')
|
||||||
|
# 确保密钥是16字节长
|
||||||
|
key = AES_KEY.byteslice(0, 16) # 如果密钥不足16字节,填充空格;如果超过,截断
|
||||||
|
iv = AES_KEY.byteslice(0, 16)
|
||||||
|
|
||||||
|
decipher = OpenSSL::Cipher.new('AES-128-CBC')
|
||||||
|
decipher.decrypt
|
||||||
|
decipher.key = key
|
||||||
|
decipher.iv = iv
|
||||||
|
|
||||||
|
# 根据输入编码解码密文
|
||||||
|
decrypted_data = case input_encoding
|
||||||
|
when 'base64'
|
||||||
|
Base64.strict_decode64(cipher_text)
|
||||||
|
else
|
||||||
|
cipher_text
|
||||||
|
end
|
||||||
|
|
||||||
|
decrypted = decipher.update(decrypted_data) + decipher.final
|
||||||
|
decrypted
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::Pm::IssueLinksHelper
|
||||||
|
end
|
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::Pm::ProjectsHelper
|
||||||
|
end
|
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::V1::PmIssuesHelper
|
||||||
|
end
|
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::V1::Sonarqube::IssuesHelper
|
||||||
|
end
|
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::V1::SonarqubesHelper
|
||||||
|
end
|
|
@ -146,13 +146,13 @@ module ApplicationHelper
|
||||||
# 用户图像url,如果不存在的话,source为匿名用户,即默认使用匿名用户图像
|
# 用户图像url,如果不存在的话,source为匿名用户,即默认使用匿名用户图像
|
||||||
def url_to_avatar(source)
|
def url_to_avatar(source)
|
||||||
if File.exist?(disk_filename(source&.class, source&.id))
|
if File.exist?(disk_filename(source&.class, source&.id))
|
||||||
ctime = File.ctime(disk_filename(source.class, source.id)).to_i
|
ctime = File.ctime(disk_filename(source&.class, source&.id)).to_i
|
||||||
if %w(User Organization).include?(source.class.to_s)
|
if %w(User Organization).include?(source&.class.to_s)
|
||||||
File.join("images", relative_path, ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
|
File.join("images", relative_path, ["#{source&.class}", "#{source&.id}"]) + "?t=#{ctime}"
|
||||||
else
|
else
|
||||||
File.join("images/avatars", ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
|
File.join("images/avatars", ["#{source&.class}", "#{source&.id}"]) + "?t=#{ctime}"
|
||||||
end
|
end
|
||||||
elsif source.class.to_s == 'User'
|
elsif source&.class.to_s == 'User'
|
||||||
source.get_letter_avatar_url
|
source.get_letter_avatar_url
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,13 +13,13 @@ module AvatarHelper
|
||||||
|
|
||||||
def url_to_avatar(source)
|
def url_to_avatar(source)
|
||||||
if File.exist?(disk_filename(source&.class, source&.id))
|
if File.exist?(disk_filename(source&.class, source&.id))
|
||||||
ctime = File.ctime(disk_filename(source.class, source.id)).to_i
|
ctime = File.ctime(disk_filename(source&.class, source&.id)).to_i
|
||||||
if %w(User Organization).include?(source.class.to_s)
|
if %w(User Organization).include?(source&.class.to_s)
|
||||||
File.join("images", relative_path, ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
|
File.join("images", relative_path, ["#{source&.class}", "#{source&.id}"]) + "?t=#{ctime}"
|
||||||
else
|
else
|
||||||
File.join("images/avatars", ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
|
File.join("images/avatars", ["#{source&.class}", "#{source&.id}"]) + "?t=#{ctime}"
|
||||||
end
|
end
|
||||||
elsif source.class.to_s == 'User'
|
elsif source&.class.to_s == 'User'
|
||||||
source.get_letter_avatar_url
|
source.get_letter_avatar_url
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
module Pm::JournalsHelper
|
||||||
|
end
|
|
@ -129,7 +129,7 @@ module ProjectsHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def jianmu_devops_url
|
def jianmu_devops_url
|
||||||
EduSetting.get("jianmu_devops_url") || "https://ci-v3.test.jianmuhub.com"
|
EduSetting.get("jianmu_devops_url")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -145,15 +145,18 @@ module ProjectsHelper
|
||||||
url = EduSetting.get("ai_shang_url") || "https://shang.gitlink.org.cn"
|
url = EduSetting.get("ai_shang_url") || "https://shang.gitlink.org.cn"
|
||||||
case project.identifier.to_s.downcase
|
case project.identifier.to_s.downcase
|
||||||
when nil then ""
|
when nil then ""
|
||||||
when 'rails' then "#{url}/v1/rails/entropy"
|
# when 'rails' then "#{url}/v1/rails/entropy"
|
||||||
when 'jittor' then "#{url}/v1/jittor/entropy"
|
# when 'jittor' then "#{url}/v1/jittor/entropy"
|
||||||
when 'paddle' then "#{url}/v1/Paddle/entropy"
|
# when 'paddle' then "#{url}/v1/Paddle/entropy"
|
||||||
when 'vue' then "#{url}/v1/vue/entropy"
|
# when 'vue' then "#{url}/v1/vue/entropy"
|
||||||
when 'bootstrap' then "#{url}/v1/bootstrap/entropy"
|
# when 'bootstrap' then "#{url}/v1/bootstrap/entropy"
|
||||||
when 'tensorflow' then "#{url}/v1/tensorflow/entropy"
|
# when 'tensorflow' then "#{url}/v1/tensorflow/entropy"
|
||||||
when 'kernel' then "#{url}/v1/openeuler/entropy"
|
# when 'kernel' then "#{url}/v1/openeuler/entropy"
|
||||||
when 'opengauss-server' then "#{url}/v1/opengauss/entropy"
|
# when 'opengauss-server' then "#{url}/v1/opengauss/entropy"
|
||||||
when 'mindspore' then "#{url}/v1/mindspore/entropy"
|
# when 'mindspore' then "#{url}/v1/mindspore/entropy"
|
||||||
|
when 'openharmony-kernel' then "#{url}/api/openharmony/entropy"
|
||||||
|
when 'kernel' then "#{url}/api/openeuler/entropy"
|
||||||
|
when 'xiuos' then "#{url}/api/xiuos/entropy"
|
||||||
else ''
|
else ''
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
class ChangeIssueStatusByMessageJob < ApplicationJob
|
||||||
|
queue_as :notify
|
||||||
|
|
||||||
|
# Close, Closes, Closed, Closing, close, closes, closed, closing
|
||||||
|
# Fix, Fixes, Fixed, Fixing, fix, fixes, fixed, fixing
|
||||||
|
# Resolve, Resolves, Resolved, Resolving, resolve, resolves, resolved, resolving
|
||||||
|
# Implement, Implements, Implemented, Implementing, implement, implements, implemented, implementing
|
||||||
|
# 以上关键词后接 issue_id 例如:Closes #234 Closes #123, #245, #992
|
||||||
|
|
||||||
|
|
||||||
|
def get_pm_issue_data(user, org, pm_project_id, issue_id)
|
||||||
|
url = "#{EduSetting.get("pms_server_url")}/api/pms/#{org.login}/pmsProjectIssues/#{issue_id}?pmProjectId=#{pm_project_id}"
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'Cookie' => "autologin_trustie=#{Token.get_or_create_permanent_login_token(user, 'autologin')&.value}",
|
||||||
|
}
|
||||||
|
|
||||||
|
response = RestClient.get(url, headers)
|
||||||
|
|
||||||
|
puts response.body
|
||||||
|
return JSON.parse(response.body)["code"].to_i == 200
|
||||||
|
rescue
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def perform(commitsha, project, user, tag_issue_id_content, status_id=1)
|
||||||
|
Rails.logger.info "需要操作的issue_id内容为 #{tag_issue_id_content}"
|
||||||
|
tag_issue_id_content = tag_issue_id_content.gsub(/\s+/, '')
|
||||||
|
tag_issue_id_content.to_s.split(",").each do |tag_issue|
|
||||||
|
issue_id = tag_issue.gsub('#', '')
|
||||||
|
issue = project.issues.issue_issue.where(project_issues_index: issue_id).where.not(id: issue_id).take || Issue.issue_issue.find_by_id(issue_id)
|
||||||
|
next unless issue.present? # issue不存在 跳过
|
||||||
|
next if issue.project.present? && !issue.project.member?(user) # issue归属项目,用户没有修改issue的权限,跳过
|
||||||
|
next if issue.pm_project_id.nil? && project.id.to_i != issue.project&.id.to_i
|
||||||
|
next if issue.pm_project_id.present? && !get_pm_issue_data(user, project.owner, issue.pm_project_id, issue.id) # issue是组织下工作项,不具备组织的访问权限,跳过
|
||||||
|
|
||||||
|
issue_project = issue.project || Project.new(id: 0, user_id: 0, name: 'pm_mm', identifier: 'pm_mm', is_public:true)
|
||||||
|
if issue.pm_project_id.present?
|
||||||
|
Api::Pm::Issues::UpdateService.call(issue_project, issue, {status_id: status_id}, user, "Project##{project.id}@#{commitsha}")
|
||||||
|
else
|
||||||
|
Api::V1::Issues::UpdateService.call(issue_project, issue, {status_id: status_id}, user, "Project##{project.id}@#{commitsha}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,6 +6,7 @@ class OpenProjectDevOpsJob < ApplicationJob
|
||||||
def perform(project_id, user_id)
|
def perform(project_id, user_id)
|
||||||
project = Project.find_by(id: project_id)
|
project = Project.find_by(id: project_id)
|
||||||
return if project.blank?
|
return if project.blank?
|
||||||
|
return if EduSetting.get("jianmu_devops_url").blank?
|
||||||
user = User.find_by(id: user_id)
|
user = User.find_by(id: user_id)
|
||||||
code = jianmu_devops_code(project, user)
|
code = jianmu_devops_code(project, user)
|
||||||
uri = URI.parse("#{jianmu_devops_url}/activate?code=#{URI.encode_www_form_component(code)}")
|
uri = URI.parse("#{jianmu_devops_url}/activate?code=#{URI.encode_www_form_component(code)}")
|
||||||
|
|
|
@ -4,6 +4,7 @@ class UpdateProjectTopicJob < ApplicationJob
|
||||||
queue_as :message
|
queue_as :message
|
||||||
|
|
||||||
def perform(project_id)
|
def perform(project_id)
|
||||||
|
return unless $redis_cache.set("UpdateProjectTopicJob:#{project_id}", 1, nx: true, ex: 10.seconds)
|
||||||
project = Project.find_by(id: project_id)
|
project = Project.find_by(id: project_id)
|
||||||
return if project.blank?
|
return if project.blank?
|
||||||
begin
|
begin
|
||||||
|
@ -25,7 +26,9 @@ class UpdateProjectTopicJob < ApplicationJob
|
||||||
topic_count +=1
|
topic_count +=1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
$redis_cache.del("UpdateProjectTopicJob:#{project_id}")
|
||||||
rescue => e
|
rescue => e
|
||||||
|
$redis_cache.del("UpdateProjectTopicJob:#{project_id}")
|
||||||
puts "get_repos_languages: error:#{e.message}"
|
puts "get_repos_languages: error:#{e.message}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
require 'oauth2'
|
||||||
|
|
||||||
|
module Ci4s::Service
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def client_id
|
||||||
|
config = Rails.application.config_for(:configuration)
|
||||||
|
config.dig("oauth", "ci4s", "client_id")
|
||||||
|
end
|
||||||
|
|
||||||
|
def client_secret
|
||||||
|
config = Rails.application.config_for(:configuration)
|
||||||
|
config.dig("oauth", "ci4s", "client_secret")
|
||||||
|
end
|
||||||
|
|
||||||
|
def base_url
|
||||||
|
config = Rails.application.config_for(:configuration)
|
||||||
|
config.dig("oauth", "ci4s", "base_url")
|
||||||
|
end
|
||||||
|
|
||||||
|
def redirect_uri
|
||||||
|
config = Rails.application.config_for(:configuration)
|
||||||
|
config.dig("oauth", "ci4s", "redirect_uri")
|
||||||
|
end
|
||||||
|
|
||||||
|
def oauth_url
|
||||||
|
"#{base_url}/oauth/authorize?client_id=#{client_id}&redirect_uri=#{URI.encode_www_form_component(redirect_uri)}&response_type=code&grant_type=authorization_code"
|
||||||
|
end
|
||||||
|
|
||||||
|
def request(method, url, params)
|
||||||
|
begin
|
||||||
|
Rails.logger.info("[Ci4sOauth] [#{method.to_s.upcase}] #{url} || #{params}")
|
||||||
|
|
||||||
|
client ||= begin
|
||||||
|
Faraday.new(url: base_url) do |req|
|
||||||
|
req.request :url_encoded
|
||||||
|
req.headers['Content-Type'] = 'application/json'
|
||||||
|
req.response :logger # 显示日志
|
||||||
|
req.adapter Faraday.default_adapter
|
||||||
|
req.authorization :Bearer, params[:access_token]
|
||||||
|
req.headers['Authorization']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
response = client.public_send(method, url, params)
|
||||||
|
result = JSON.parse(response.body)
|
||||||
|
|
||||||
|
Rails.logger.info("[Ci4sOauth] [#{response.status}] #{result}")
|
||||||
|
|
||||||
|
result
|
||||||
|
rescue Exception => e
|
||||||
|
raise Gitlink::TipException.new(e.message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def access_token(code)
|
||||||
|
begin
|
||||||
|
Rails.logger.info("[Ci4sOauth] [code] #{code} ")
|
||||||
|
Rails.logger.info("[Ci4sOauth] [redirect_uri] #{redirect_uri} ")
|
||||||
|
client = OAuth2::Client.new(client_id, client_secret, site: base_url)
|
||||||
|
result = client.auth_code.get_token(code, redirect_uri: redirect_uri).to_hash
|
||||||
|
return result
|
||||||
|
rescue Exception => e
|
||||||
|
raise Gitlink::TipException.new(e.message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_info(access_token)
|
||||||
|
request(:get, '/user/info', {access_token: access_token})
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,7 +7,7 @@ module EducoderOauth::Service
|
||||||
begin
|
begin
|
||||||
Rails.logger.info("[EducoderOauth] [#{method.to_s.upcase}] #{url} || #{params}")
|
Rails.logger.info("[EducoderOauth] [#{method.to_s.upcase}] #{url} || #{params}")
|
||||||
|
|
||||||
client = Faraday.new(url: EducoderOauth.base_url)
|
client = Faraday.new(url: EducoderOauth.base_url, headers: {'X-EDU-Timestamp' => "#{Time.now.to_i}"})
|
||||||
response = client.public_send(method, url, params)
|
response = client.public_send(method, url, params)
|
||||||
result = JSON.parse(response.body)
|
result = JSON.parse(response.body)
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,14 @@
|
||||||
# user_id :integer
|
# user_id :integer
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
|
# label :string(255)
|
||||||
|
# node_type :string(255)
|
||||||
|
# is_mutil_link :boolean
|
||||||
|
# link_type :string(255)
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
# by_name (name)
|
||||||
# index_action_nodes_on_action_types_id (action_node_types_id)
|
# index_action_nodes_on_action_types_id (action_node_types_id)
|
||||||
# index_action_nodes_on_user_id (user_id)
|
# index_action_nodes_on_user_id (user_id)
|
||||||
#
|
#
|
||||||
|
@ -33,39 +38,37 @@ class Action::Node < ApplicationRecord
|
||||||
|
|
||||||
belongs_to :user, optional: true
|
belongs_to :user, optional: true
|
||||||
|
|
||||||
|
attr_accessor :cust_name, :run_values, :input_values, :parent_node_id, :sub_nodes, :link_type_array, :node_id
|
||||||
|
|
||||||
# def content_yaml
|
validates :name, presence: { message: "不能为空" }
|
||||||
# "foo".to_yaml
|
validates :full_name, length: { maximum: 200, too_long: "不能超过200个字符" }
|
||||||
# <<~YAML
|
validates :label, length: { maximum: 200, too_long: "不能超过200个字符" }
|
||||||
# - name: Set up JDK ${{ matrix.java }}
|
validates :description, length: { maximum: 65535, too_long: "不能超过65535个字符"}
|
||||||
# uses: actions/setup-java@v3
|
|
||||||
# with:
|
|
||||||
# distribution: 'temurin'
|
|
||||||
# java-version: ${{ matrix.java }}
|
|
||||||
# YAML
|
|
||||||
# end
|
|
||||||
|
|
||||||
def yaml_hash
|
|
||||||
|
|
||||||
|
def content_yaml
|
||||||
|
"foo".to_yaml
|
||||||
<<~YAML
|
<<~YAML
|
||||||
name: Check dist
|
- name: Set up JDK ${{ matrix.java }}
|
||||||
|
uses: actions/setup-java@v3
|
||||||
on:
|
with:
|
||||||
push:
|
distribution: 'temurin'
|
||||||
branches:
|
java-version: ${{ matrix.java }}
|
||||||
- main
|
|
||||||
paths-ignore:
|
|
||||||
- '**.md'
|
|
||||||
pull_request:
|
|
||||||
paths-ignore:
|
|
||||||
- '**.md'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
call-check-dist:
|
|
||||||
name: Check dist/
|
|
||||||
uses: actions/reusable-workflows/.github/workflows/check-dist.yml@main
|
|
||||||
with:
|
|
||||||
node-version: '20.x'
|
|
||||||
YAML
|
YAML
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def node
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_yaml
|
||||||
|
yaml = ERB.new(File.read(File.join(Rails.root, "app/views/api/v1/projects/pipelines", "build_node.yaml.erb"))).result(binding)
|
||||||
|
# 删除空行内容
|
||||||
|
yaml = yaml.gsub(/^\s*\n/, "")
|
||||||
|
# Rails.logger.info "========================="
|
||||||
|
# Rails.logger.info yaml
|
||||||
|
yaml
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
# input_type :string(255)
|
# input_type :string(255)
|
||||||
# description :string(255)
|
# description :string(255)
|
||||||
# is_required :boolean default("0")
|
# is_required :boolean default("0")
|
||||||
# sort_no :string(255) default("0")
|
# sort_no :integer default("0")
|
||||||
# user_id :integer
|
# user_id :integer
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
|
@ -24,4 +24,7 @@ class Action::NodeInput < ApplicationRecord
|
||||||
default_scope { order(sort_no: :asc) }
|
default_scope { order(sort_no: :asc) }
|
||||||
|
|
||||||
belongs_to :action_node, :class_name => 'Action::Node', foreign_key: "action_nodes_id"
|
belongs_to :action_node, :class_name => 'Action::Node', foreign_key: "action_nodes_id"
|
||||||
|
|
||||||
|
validates :name, presence: { message: "不能为空" }
|
||||||
|
validates :description, length: { maximum: 65535, too_long: "不能超过65535个字符"}
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,6 +29,9 @@ class Action::NodeSelect < ApplicationRecord
|
||||||
belongs_to :action_node, :class_name => 'Action::Node', foreign_key: "action_nodes_id"
|
belongs_to :action_node, :class_name => 'Action::Node', foreign_key: "action_nodes_id"
|
||||||
belongs_to :user, optional: true
|
belongs_to :user, optional: true
|
||||||
|
|
||||||
|
validates :name, presence: { message: "不能为空" }
|
||||||
|
validates :description, length: { maximum: 65535, too_long: "不能超过65535个字符"}
|
||||||
|
|
||||||
def value
|
def value
|
||||||
if self.val_ext.blank?
|
if self.val_ext.blank?
|
||||||
self.val
|
self.val
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# name :string(255)
|
# name :string(255)
|
||||||
# description :string(255)
|
# description :string(255)
|
||||||
# sort_no :integer
|
# sort_no :integer default("0")
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
#
|
#
|
||||||
|
@ -15,4 +15,7 @@ class Action::NodeType < ApplicationRecord
|
||||||
default_scope { order(sort_no: :asc) }
|
default_scope { order(sort_no: :asc) }
|
||||||
|
|
||||||
has_many :action_nodes, :class_name => 'Action::Node', foreign_key: "action_node_types_id"
|
has_many :action_nodes, :class_name => 'Action::Node', foreign_key: "action_node_types_id"
|
||||||
|
|
||||||
|
validates :name, presence: { message: "不能为空" }
|
||||||
|
validates :description, length: { maximum: 65535, too_long: "不能超过65535个字符"}
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: action_pipelines
|
||||||
|
#
|
||||||
|
# id :integer not null, primary key
|
||||||
|
# project_id :integer
|
||||||
|
# user_id :integer
|
||||||
|
# pipeline_name :string(255)
|
||||||
|
# pipeline_status :string(255)
|
||||||
|
# description :string(255)
|
||||||
|
# file_name :string(255)
|
||||||
|
# is_graphic_design :boolean default("0")
|
||||||
|
# repo_name :string(255)
|
||||||
|
# repo_identifier :string(255)
|
||||||
|
# repo_owner :string(255)
|
||||||
|
# branch :string(255)
|
||||||
|
# event :string(255)
|
||||||
|
# sha :string(255)
|
||||||
|
# json :text(65535)
|
||||||
|
# yaml :text(65535)
|
||||||
|
# disable :boolean default("0")
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_action_pipelines_on_project_id (project_id)
|
||||||
|
# index_action_pipelines_on_user_id (user_id)
|
||||||
|
#
|
||||||
|
|
||||||
|
class Action::Pipeline < ApplicationRecord
|
||||||
|
self.table_name = 'action_pipelines'
|
||||||
|
belongs_to :user, optional: true
|
||||||
|
belongs_to :project
|
||||||
|
|
||||||
|
validates :name, presence: { message: "不能为空" }
|
||||||
|
validates :json, length: { maximum: 65535, too_long: "不能超过65535个字符"}
|
||||||
|
validates :yaml, length: { maximum: 65535, too_long: "不能超过65535个字符"}
|
||||||
|
end
|
|
@ -0,0 +1,25 @@
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: action_pipeline_results
|
||||||
|
#
|
||||||
|
# id :integer not null, primary key
|
||||||
|
# project_id :integer
|
||||||
|
# run_id :integer
|
||||||
|
# step_id :string(255)
|
||||||
|
# job_name :string(255)
|
||||||
|
# job_show_type :string(255)
|
||||||
|
# job_result :text(65535)
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_action_pipeline_results_on_project_id (project_id)
|
||||||
|
# index_action_pipeline_results_on_run_id (run_id)
|
||||||
|
#
|
||||||
|
|
||||||
|
class Action::PipelineResult < ApplicationRecord
|
||||||
|
self.table_name = 'action_pipeline_results'
|
||||||
|
belongs_to :project
|
||||||
|
end
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# name :string(255)
|
# name :string(255)
|
||||||
# description :string(255)
|
# description :string(255)
|
||||||
# img :string(255)
|
# img :string(255)
|
||||||
# sort_no :string(255) default("0")
|
# sort_no :integer default("0")
|
||||||
# json :text(65535)
|
# json :text(65535)
|
||||||
# yaml :text(65535)
|
# yaml :text(65535)
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
|
@ -17,4 +17,7 @@ class Action::Template < ApplicationRecord
|
||||||
self.table_name = 'action_templates'
|
self.table_name = 'action_templates'
|
||||||
default_scope { order(sort_no: :asc) }
|
default_scope { order(sort_no: :asc) }
|
||||||
|
|
||||||
|
validates :name, presence: { message: "不能为空" }
|
||||||
|
validates :description, length: { maximum: 65535, too_long: "不能超过65535个字符"}
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,8 +26,6 @@
|
||||||
# cloud_url :string(255) default("")
|
# cloud_url :string(255) default("")
|
||||||
# course_second_category_id :integer default("0")
|
# course_second_category_id :integer default("0")
|
||||||
# delay_publish :boolean default("0")
|
# delay_publish :boolean default("0")
|
||||||
# memo_image :boolean default("0")
|
|
||||||
# extra_type :integer default("0")
|
|
||||||
# uuid :string(255)
|
# uuid :string(255)
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
|
@ -38,12 +36,14 @@
|
||||||
# index_attachments_on_created_on (created_on)
|
# index_attachments_on_created_on (created_on)
|
||||||
# index_attachments_on_is_public (is_public)
|
# index_attachments_on_is_public (is_public)
|
||||||
# index_attachments_on_quotes (quotes)
|
# index_attachments_on_quotes (quotes)
|
||||||
|
# index_attachments_on_uuid (uuid)
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Attachment < ApplicationRecord
|
class Attachment < ApplicationRecord
|
||||||
include BaseModel
|
include BaseModel
|
||||||
include Publicable
|
include Publicable
|
||||||
|
@ -76,6 +76,42 @@ class Attachment < ApplicationRecord
|
||||||
|
|
||||||
DCODES = %W(2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z)
|
DCODES = %W(2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z)
|
||||||
|
|
||||||
|
def self.build_from_remote_url(user, name, url, container=nil)
|
||||||
|
ext = name.split('.')[-1]
|
||||||
|
tmp_path = "#{Rails.root}/#{name}"
|
||||||
|
uri = URI(url)
|
||||||
|
size = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
||||||
|
response = http.get(uri.path)
|
||||||
|
File.open(tmp_path, 'wb') do |file|
|
||||||
|
file.write(response.body)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
digest = "#{Digest::MD5.file(tmp_path).hexdigest}_#{(Time.now.to_f * 1000).to_i}.#{ext}"
|
||||||
|
month_folder = "#{Time.now.year}/#{Time.now.month.to_s.rjust(2, '0')}"
|
||||||
|
save_path = "#{Rails.root}#{EduSetting.get("attachment_folder")}#{month_folder}"
|
||||||
|
unless Dir.exists?(save_path)
|
||||||
|
FileUtils.mkdir_p(save_path) ##不成功这里会抛异常
|
||||||
|
end
|
||||||
|
path = File.join(save_path, digest)
|
||||||
|
FileUtils.mv(tmp_path, path)
|
||||||
|
attachment = Attachment.new
|
||||||
|
attachment.filename = name
|
||||||
|
attachment.disk_filename = path[save_path.size+1, path.size]
|
||||||
|
attachment.filesize = size
|
||||||
|
attachment.content_type = 'application/octet-stream'
|
||||||
|
attachment.digest = digest.split('.')[0]
|
||||||
|
attachment.author_id = user.id
|
||||||
|
attachment.disk_directory = month_folder
|
||||||
|
attachment.cloud_url = url
|
||||||
|
attachment.uuid = SecureRandom.uuid
|
||||||
|
attachment.container = container
|
||||||
|
attachment.save!
|
||||||
|
|
||||||
|
return attachment
|
||||||
|
rescue
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
def diskfile
|
def diskfile
|
||||||
File.join(File.join(Rails.root, "files"), disk_directory.to_s, disk_filename.to_s)
|
File.join(File.join(Rails.root, "files"), disk_directory.to_s, disk_filename.to_s)
|
||||||
end
|
end
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue