forked from Gitlink/forgeplus
Merge pull request '20220608版本' (#307) from develop into master
This commit is contained in:
commit
4a886b4bb1
|
@ -84,4 +84,5 @@ redis_data/
|
|||
Dockerfile
|
||||
dump.rdb
|
||||
.tags*
|
||||
ceshi_user.xlsx
|
||||
ceshi_user.xlsx
|
||||
public/trace_task_results
|
|
@ -1,6 +1,25 @@
|
|||
class AccountsController < ApplicationController
|
||||
before_action :require_login, only: [:login_check, :simple_update]
|
||||
include ApplicationHelper
|
||||
|
||||
#skip_before_action :check_account, :only => [:logout]
|
||||
|
||||
def simple_update
|
||||
simple_update_params.merge!(username: params[:username]&.gsub(/\s+/, ""))
|
||||
simple_update_params.merge!(email: params[:email]&.gsub(/\s+/, ""))
|
||||
simple_update_params.merge!(platform: (params[:platform] || 'forge')&.gsub(/\s+/, ""))
|
||||
Register::RemoteForm.new(simple_update_params).validate!
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
result = auto_update(current_user, simple_update_params)
|
||||
if result[:message].blank?
|
||||
render_ok
|
||||
else
|
||||
render_error(result[:message])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def index
|
||||
render json: session
|
||||
end
|
||||
|
@ -315,6 +334,11 @@ class AccountsController < ApplicationController
|
|||
Register::CheckColumnsForm.new(check_params).validate!
|
||||
render_ok
|
||||
end
|
||||
|
||||
def login_check
|
||||
Register::LoginCheckColumnsForm.new(check_params.merge(user: current_user)).validate!
|
||||
render_ok
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
|
@ -383,4 +407,7 @@ class AccountsController < ApplicationController
|
|||
params.permit(:username, :email, :password, :platform)
|
||||
end
|
||||
|
||||
def simple_update_params
|
||||
params.permit(:username, :email, :password, :platform)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@ class Admins::ImportUsersController < Admins::BaseController
|
|||
def create
|
||||
return render_error('请上传正确的文件') if params[:file].blank? || !params[:file].is_a?(ActionDispatch::Http::UploadedFile)
|
||||
|
||||
result = Admins::ImportUserService.call(params[:file].to_io)
|
||||
result = Admins::ImportUserFromExcelService.call(params[:file].to_io)
|
||||
render_ok(result)
|
||||
rescue Admins::ImportUserService::Error => ex
|
||||
render_error(ex)
|
||||
|
|
|
@ -2,8 +2,24 @@ class Admins::MessageTemplatesController < Admins::BaseController
|
|||
before_action :get_template, only: [:edit, :update, :destroy]
|
||||
|
||||
def index
|
||||
message_templates = MessageTemplate.group(:type).count.keys
|
||||
@message_templates = kaminari_array_paginate(message_templates)
|
||||
message_templates = MessageTemplate.ransack(sys_notice_or_email_or_email_title_cont: params[:search]).result
|
||||
@message_templates = kaminari_paginate(message_templates)
|
||||
end
|
||||
|
||||
def new
|
||||
@message_template = MessageTemplate::CustomTip.new
|
||||
end
|
||||
|
||||
def create
|
||||
@message_template = MessageTemplate::CustomTip.new(ignore_params)
|
||||
|
||||
if @message_template.save!
|
||||
redirect_to admins_message_templates_path
|
||||
flash[:success] = "创建消息模板成功"
|
||||
else
|
||||
render :new
|
||||
flash[:danger] = "创建消息模板失败"
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
|
|
|
@ -14,12 +14,12 @@ class Ci::CloudAccountsController < Ci::BaseController
|
|||
|
||||
def create
|
||||
flag, msg = check_bind_cloud_account!
|
||||
return render_error(msg) if flag === true
|
||||
return tip_exception(msg) if flag === true
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
@cloud_account = bind_account!
|
||||
if @cloud_account.blank?
|
||||
render_error('激活失败, 请检查你的云服务器信息是否正确.')
|
||||
tip_exception('激活失败, 请检查你的云服务器信息是否正确.')
|
||||
raise ActiveRecord::Rollback
|
||||
else
|
||||
current_user.set_drone_step!(User::DEVOPS_UNVERIFIED)
|
||||
|
@ -27,17 +27,17 @@ class Ci::CloudAccountsController < Ci::BaseController
|
|||
end
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def activate
|
||||
return render_error('请先认证') unless current_user.ci_certification?
|
||||
return tip_exception('请先认证') unless current_user.ci_certification?
|
||||
|
||||
begin
|
||||
@cloud_account = Ci::CloudAccount.find params[:id]
|
||||
ActiveRecord::Base.transaction do
|
||||
if @repo
|
||||
return render_error('该项目已经激活') if @repo.repo_active?
|
||||
return tip_exception('该项目已经激活') if @repo.repo_active?
|
||||
@repo.activate!(@project)
|
||||
else
|
||||
@repo = Ci::Repo.auto_create!(@ci_user, @project)
|
||||
|
@ -50,7 +50,7 @@ class Ci::CloudAccountsController < Ci::BaseController
|
|||
end
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -59,39 +59,39 @@ class Ci::CloudAccountsController < Ci::BaseController
|
|||
|
||||
def bind
|
||||
flag, msg = check_bind_cloud_account!
|
||||
return render_error(msg) if flag === true
|
||||
return tip_exception(msg) if flag === true
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
@cloud_account = bind_account!
|
||||
if @cloud_account.blank?
|
||||
render_error('激活失败, 请检查你的云服务器信息是否正确.')
|
||||
tip_exception('激活失败, 请检查你的云服务器信息是否正确.')
|
||||
raise ActiveRecord::Rollback
|
||||
else
|
||||
current_user.set_drone_step!(User::DEVOPS_UNVERIFIED)
|
||||
end
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def trustie_bind
|
||||
account = params[:account].to_s
|
||||
return render_error("account不能为空.") if account.blank?
|
||||
return tip_exception("account不能为空.") if account.blank?
|
||||
|
||||
flag, msg = check_trustie_bind_cloud_account!
|
||||
return render_error(msg) if flag === true
|
||||
return tip_exception(msg) if flag === true
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
@cloud_account = trustie_bind_account!
|
||||
if @cloud_account.blank?
|
||||
render_error('激活失败, 请检查你的云服务器信息是否正确.')
|
||||
tip_exception('激活失败, 请检查你的云服务器信息是否正确.')
|
||||
raise ActiveRecord::Rollback
|
||||
else
|
||||
current_user.set_drone_step!(User::DEVOPS_UNVERIFIED)
|
||||
end
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def unbind
|
||||
|
@ -107,18 +107,18 @@ class Ci::CloudAccountsController < Ci::BaseController
|
|||
render_ok
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def oauth_grant
|
||||
password = params[:password].to_s
|
||||
return render_error('你输入的密码不正确.') unless current_user.check_password?(password)
|
||||
return tip_exception('你输入的密码不正确.') unless current_user.check_password?(password)
|
||||
|
||||
oauth = current_user.oauths.last
|
||||
return render_error("服务器出小差了.") if oauth.blank?
|
||||
return tip_exception("服务器出小差了.") if oauth.blank?
|
||||
|
||||
result = gitea_oauth_grant!(password, oauth)
|
||||
return render_error('授权失败.') unless result === true
|
||||
return tip_exception('授权失败.') unless result === true
|
||||
current_user.set_drone_step!(User::DEVOPS_CERTIFICATION)
|
||||
end
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
ActiveRecord::Base.transaction do
|
||||
size = Ci::Pipeline.where('branch=? and identifier=? and owner=?', params[:branch], params[:repo], params[:owner]).size
|
||||
if size > 0
|
||||
render_error("#{params[:branch]}分支已经存在流水线!")
|
||||
tip_exception("#{params[:branch]}分支已经存在流水线!")
|
||||
return
|
||||
end
|
||||
pipeline = Ci::Pipeline.new(pipeline_name: params[:pipeline_name], file_name: params[:file_name],owner: params[:owner],
|
||||
|
@ -53,7 +53,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
render_ok({id: pipeline.id})
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
# 在代码库创建文件
|
||||
|
@ -81,6 +81,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
repo_branch: pipeline.branch,
|
||||
repo_config: pipeline.file_name
|
||||
}
|
||||
Rails.logger.info("########create_params===#{create_params.to_json}")
|
||||
repo = Ci::Repo.create_repo(create_params)
|
||||
repo
|
||||
end
|
||||
|
@ -118,7 +119,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
end
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
@ -132,7 +133,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
end
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def content
|
||||
|
@ -182,7 +183,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
render_ok
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def update_stage
|
||||
|
@ -192,7 +193,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
end
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def delete_stage
|
||||
|
@ -205,7 +206,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
render_ok
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def update_stage_index(pipeline_id, show_index, diff)
|
||||
|
@ -229,7 +230,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
unless steps.empty?
|
||||
steps.each do |step|
|
||||
unless step[:template_id]
|
||||
render_error('请选择模板!')
|
||||
tip_exception('请选择模板!')
|
||||
return
|
||||
end
|
||||
if !step[:id]
|
||||
|
@ -246,7 +247,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
render_ok
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def create_stage_step
|
||||
|
@ -262,7 +263,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
render_ok
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def update_stage_step
|
||||
|
@ -279,7 +280,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
render_ok
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def delete_stage_step
|
||||
|
@ -289,6 +290,6 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
end
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -30,19 +30,19 @@ class Ci::ProjectsController < Ci::BaseController
|
|||
@file = interactor.result
|
||||
render_result(1, "更新成功")
|
||||
else
|
||||
render_error(interactor.error)
|
||||
tip_exception(interactor.error)
|
||||
end
|
||||
end
|
||||
|
||||
def activate
|
||||
return render_error('你还未认证') unless current_user.ci_certification?
|
||||
return tip_exception('你还未认证') unless current_user.ci_certification?
|
||||
|
||||
begin
|
||||
ActiveRecord::Base.transaction do
|
||||
if @repo
|
||||
return render_error('该项目已经激活') if @repo.repo_active?
|
||||
@repo.destroy! if @repo&.repo_user_id == 0
|
||||
return tip_exception('该项目已经激活') if @repo.repo_active?
|
||||
@repo.activate!(@project)
|
||||
return render_ok
|
||||
else
|
||||
@repo = Ci::Repo.auto_create!(@ci_user, @project)
|
||||
@ci_user.update_column(:user_syncing, false)
|
||||
|
@ -55,12 +55,12 @@ class Ci::ProjectsController < Ci::BaseController
|
|||
end
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
end
|
||||
|
||||
def deactivate
|
||||
return render_error('该项目已经取消激活') if !@repo.repo_active?
|
||||
return tip_exception('该项目已经取消激活') if !@repo.repo_active?
|
||||
|
||||
@project.update_column(:open_devops, false)
|
||||
@repo.deactivate_repos!
|
||||
|
|
|
@ -20,14 +20,14 @@ class Ci::SecretsController < Ci::BaseController
|
|||
if result["id"]
|
||||
render_ok
|
||||
else
|
||||
render_error(result["message"])
|
||||
tip_exception(result["message"])
|
||||
end
|
||||
else
|
||||
result = Ci::Drone::API.new(@ci_user.user_hash, ci_drone_url, params[:owner], params[:repo], options).create_secret
|
||||
if result["id"]
|
||||
render_ok
|
||||
else
|
||||
render_error(result["message"])
|
||||
tip_exception(result["message"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -39,7 +39,7 @@ class Ci::SecretsController < Ci::BaseController
|
|||
Ci::Drone::API.new(@ci_user.user_hash, ci_drone_url, params[:owner], params[:repo], {name: name}).delete_secret
|
||||
render_ok
|
||||
else
|
||||
render_error("参数名不能为空")
|
||||
tip_exception("参数名不能为空")
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_ok
|
||||
|
|
|
@ -50,7 +50,7 @@ class Ci::TemplatesController < Ci::BaseController
|
|||
end
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def update
|
||||
|
@ -63,7 +63,7 @@ class Ci::TemplatesController < Ci::BaseController
|
|||
)
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
@ -73,7 +73,7 @@ class Ci::TemplatesController < Ci::BaseController
|
|||
end
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
#======流水线模板查询=====#
|
||||
|
|
|
@ -160,9 +160,9 @@ module Ci::CloudAccountManageable
|
|||
state = SecureRandom.hex(8)
|
||||
# redirect_uri eg:
|
||||
# https://localhost:3000/login/oauth/authorize?client_id=94976481-ad0e-4ed4-9247-7eef106007a2&redirect_uri=http%3A%2F%2F121.69.81.11%3A80%2Flogin&response_type=code&state=9cab990b9cfb1805
|
||||
redirect_uri = CGI.escape("#{@cloud_account.drone_url}/login")
|
||||
clientId = client_id(oauth)
|
||||
grant_url = "#{Gitea.gitea_config[:domain]}/login/oauth/authorize?client_id=#{clientId}&redirect_uri=#{redirect_uri}&response_type=code&state=#{state}"
|
||||
# redirect_uri = CGI.escape("#{@cloud_account.drone_url}/login")
|
||||
# clientId = client_id(oauth)
|
||||
grant_url = "#{@cloud_account.drone_url}/login"
|
||||
logger.info "[gitea] grant_url: #{grant_url}"
|
||||
|
||||
conn = Faraday.new(url: grant_url) do |req|
|
||||
|
@ -179,7 +179,7 @@ module Ci::CloudAccountManageable
|
|||
|
||||
def drone_oauth_user!(url, state)
|
||||
logger.info "[drone] drone_oauth_user url: #{url}"
|
||||
conn = Faraday.new(url: "#{Gitea.gitea_config[:domain]}#{url}") do |req|
|
||||
conn = Faraday.new(url: url) do |req|
|
||||
req.request :url_encoded
|
||||
req.adapter Faraday.default_adapter
|
||||
req.headers["cookie"] = "_session_=#{SecureRandom.hex(28)}; _oauth_state_=#{state}"
|
||||
|
@ -188,8 +188,8 @@ module Ci::CloudAccountManageable
|
|||
response = conn.get
|
||||
logger.info "[drone] response headers: #{response.headers}"
|
||||
|
||||
true
|
||||
# response.headers['location'].include?('error') ? false : true
|
||||
# true
|
||||
response.headers['location'].include?('error') ? false : true
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -58,4 +58,32 @@ module RegisterHelper
|
|||
end
|
||||
end
|
||||
|
||||
def auto_update(user, params={})
|
||||
return if params.blank?
|
||||
result = {message: nil, user: nil}
|
||||
before_login = user.login
|
||||
user.login = params[:username]
|
||||
user.password = params[:password]
|
||||
user.mail = params[:email]
|
||||
|
||||
if user.save!
|
||||
sync_params = {
|
||||
password: params[:password].to_s,
|
||||
email: params[:email],
|
||||
login_name: params[:username],
|
||||
new_name: params[:username],
|
||||
source_id: 0
|
||||
}
|
||||
|
||||
interactor = Gitea::User::UpdateInteractor.call(before_login, sync_params)
|
||||
if interactor.success?
|
||||
result[:user] = user
|
||||
else
|
||||
result[:message] = '用户同步Gitea失败!'
|
||||
end
|
||||
else
|
||||
result[:message] = user.errors.full_messages.join(",")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@ module RenderHelper
|
|||
end
|
||||
|
||||
def render_error(message = '')
|
||||
render json: { status: -1, message: message }
|
||||
render json: { status: status, message: message }
|
||||
end
|
||||
|
||||
def render_not_acceptable(message = '请求已拒绝')
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
class NoticesController < ApplicationController
|
||||
|
||||
def create
|
||||
tip_exception("参数有误") if params["source"].blank?
|
||||
return tip_exception("参数有误") if params["source"].blank?
|
||||
user_id = params[:user_id]
|
||||
|
||||
if params["source"] == "CompetitionBegin"
|
||||
|
@ -13,9 +13,21 @@ class NoticesController < ApplicationController
|
|||
elsif params["source"] == "CompetitionReview"
|
||||
competition_id = params[:competition_id]
|
||||
SendTemplateMessageJob.perform_later('CompetitionReview', user_id, competition_id)
|
||||
elsif params["source"] == "CustomTip"
|
||||
users_id = params[:users_id]
|
||||
props = params[:props].to_unsafe_hash
|
||||
return tip_exception("参数有误") unless props.is_a?(Hash) && users_id.is_a?(Array)
|
||||
template_id = params[:template_id]
|
||||
SendTemplateMessageJob.perform_later('CustomTip', users_id, template_id, props)
|
||||
else
|
||||
tip_exception("#{params["source"]}未配置")
|
||||
end
|
||||
render_ok
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def params_props
|
||||
params.require(:notice).permit(:props)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ class OwnersController < ApplicationController
|
|||
|
||||
def show
|
||||
@owner = Owner.find_by(login: params[:id]) || Owner.find_by(id: params[:id])
|
||||
return render_ok(type: 'User') unless @owner.present?
|
||||
return render_not_found unless @owner.present?
|
||||
# 组织
|
||||
if @owner.is_a?(Organization)
|
||||
return render_forbidden("没有查看组织的权限") if org_limited_condition || org_privacy_condition
|
||||
|
|
|
@ -22,6 +22,7 @@ class ProjectsController < ApplicationController
|
|||
menu.append(menu_hash_by_name("devops")) if @project.has_menu_permission("devops") && @project.forge?
|
||||
menu.append(menu_hash_by_name("versions")) if @project.has_menu_permission("versions")
|
||||
menu.append(menu_hash_by_name("wiki")) if @project.has_menu_permission("wiki") && @project.forge?
|
||||
menu.append(menu_hash_by_name("services")) if @project.has_menu_permission("services") && @project.forge? && (current_user.admin? || @project.member?(current_user.id))
|
||||
menu.append(menu_hash_by_name("resources")) if @project.has_menu_permission("resources") && @project.forge?
|
||||
menu.append(menu_hash_by_name("activity"))
|
||||
menu.append(menu_hash_by_name("settings")) if user_is_admin && @project.forge?
|
||||
|
|
|
@ -225,7 +225,8 @@ class RepositoriesController < ApplicationController
|
|||
@path = Gitea.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/"
|
||||
@readme = result[:status] === :success ? result[:body] : nil
|
||||
@readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path)
|
||||
render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha")
|
||||
@readme['replace_content'] = readme_decode64_content(@readme, @owner, @repository, params[:ref], @path)
|
||||
render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha", "replace_content")
|
||||
rescue
|
||||
render json: nil
|
||||
end
|
||||
|
@ -387,4 +388,4 @@ class RepositoriesController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
class Traces::BaseController < ApplicationController
|
||||
|
||||
helper_method :observed_logged_user?, :observed_user
|
||||
|
||||
|
||||
def observed_user
|
||||
@_observed_user ||= (User.find_by_login(params[:user_id]) || User.find_by_id(params[:user_id]))
|
||||
end
|
||||
|
||||
def observed_logged_user?
|
||||
observed_user.id == User.current&.id
|
||||
end
|
||||
|
||||
protected
|
||||
def check_auth
|
||||
return render_forbidden unless current_user.admin? || observed_logged_user?
|
||||
end
|
||||
end
|
|
@ -0,0 +1,83 @@
|
|||
class Traces::ProjectsController < Traces::BaseController
|
||||
include OperateProjectAbilityAble
|
||||
|
||||
before_action :require_login
|
||||
before_action :load_project
|
||||
before_action :authorizate_user_can_edit_project!, except: [:task_results]
|
||||
|
||||
def tasks
|
||||
branch_name = params[:branch_name]
|
||||
return render_error("无可用检测次数") if @project.user_trace_tasks.size >= 5
|
||||
return render_error("分支名不能为空!") if branch_name.blank?
|
||||
@all_branches = Gitea::Repository::Branches::ListNameService.call(@project&.owner, @project.identifier)
|
||||
return render_error("请输入正确的分支名!") unless @all_branches["branch_name"].include?(branch_name)
|
||||
code, data, error = Trace::CheckService.call(current_user.trace_token, @project, "1", branch_name)
|
||||
if code == 200
|
||||
UserTraceTask.create!(
|
||||
user_id: current_user.id,
|
||||
project_id: @project.id,
|
||||
branch_tag: branch_name,
|
||||
task_id: data["task_id"]
|
||||
)
|
||||
render_ok
|
||||
else
|
||||
render_error("检测失败 Error:#{error}")
|
||||
end
|
||||
rescue Exception => exception
|
||||
puts exception.message
|
||||
normal_status(-1, exception.message)
|
||||
end
|
||||
|
||||
def task_results
|
||||
limit = params[:limit] || params[:per_page]
|
||||
limit = (limit.to_i.zero? || limit.to_i > 15) ? 15 : limit.to_i
|
||||
page = params[:page].to_i.zero? ? 1 : params[:page].to_i
|
||||
return render :json => {left_tasks_count: 5, data: []} if current_user.trace_user.nil?
|
||||
code, data, error = Trace::CheckResultService.call(current_user.trace_token, @project, nil, page, limit)
|
||||
if code == 200
|
||||
render :json => {left_tasks_count: 5 - @project.user_trace_tasks.size, data: data}
|
||||
else
|
||||
render_error("获取检测记录失败 Error:#{error}")
|
||||
end
|
||||
rescue Exception => exception
|
||||
puts exception.message
|
||||
normal_status(-1, exception.message)
|
||||
end
|
||||
|
||||
def reload_task
|
||||
return render_error("project_id错误") if params[:project_id].blank?
|
||||
branch_name = params[:branch_name]
|
||||
return render_error("分支名不能为空!") if branch_name.blank?
|
||||
@all_branches = Gitea::Repository::Branches::ListNameService.call(@project&.owner, @project.identifier)
|
||||
return render_error("请输入正确的分支名!") unless @all_branches["branch_name"].include?(branch_name)
|
||||
code, data, error = Trace::ReloadCheckService.call(current_user.trace_token, params[:project_id])
|
||||
if code == 200
|
||||
UserTraceTask.create!(
|
||||
user_id: current_user.id,
|
||||
project_id: @project.id,
|
||||
branch_tag: branch_name,
|
||||
task_id: data["task_id"]
|
||||
)
|
||||
render_ok
|
||||
else
|
||||
render_error("重新检测失败 Error:#{error}")
|
||||
end
|
||||
rescue Exception => exception
|
||||
puts exception.message
|
||||
normal_status(-1, exception.message)
|
||||
end
|
||||
|
||||
|
||||
def task_pdf
|
||||
return render_error("task_id错误") if params[:task_id].blank?
|
||||
result = Trace::PdfReportService.call(current_user.trace_token, params[:task_id])
|
||||
if result.is_a?(Hash) && result[:code] == 200
|
||||
redirect_to result[:download_url]
|
||||
else
|
||||
render_error("下载报告失败!")
|
||||
end
|
||||
rescue Exception => exception
|
||||
puts exception.message
|
||||
normal_status(-1, exception.message)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,14 @@
|
|||
class Traces::TraceUsersController < Traces::BaseController
|
||||
before_action :require_login
|
||||
|
||||
def create
|
||||
if current_user.trace_token.present?
|
||||
render_ok
|
||||
else
|
||||
render_error(-1, "代码溯源用户初始化失败")
|
||||
end
|
||||
rescue Exception => exception
|
||||
puts exception.message
|
||||
normal_status(-1, exception.message)
|
||||
end
|
||||
end
|
|
@ -1,332 +1,399 @@
|
|||
class UsersController < ApplicationController
|
||||
include ApplicationHelper
|
||||
include Ci::DbConnectable
|
||||
|
||||
before_action :load_user, only: [:show, :homepage_info, :sync_token, :sync_gitea_pwd, :projects, :watch_users, :fan_users, :hovercard]
|
||||
before_action :check_user_exist, only: [:show, :homepage_info,:projects, :watch_users, :fan_users, :hovercard]
|
||||
before_action :require_login, only: %i[me sync_user_info]
|
||||
before_action :connect_to_ci_db, only: [:get_user_info]
|
||||
before_action :convert_image!, only: [:update, :update_image]
|
||||
skip_before_action :check_sign, only: [:attachment_show]
|
||||
|
||||
def connect_to_ci_db(options={})
|
||||
if !(current_user && !current_user.is_a?(AnonymousUser) && current_user.devops_certification?)
|
||||
return
|
||||
end
|
||||
if current_user.ci_cloud_account.server_type == Ci::CloudAccount::SERVER_TYPE_TRUSTIE
|
||||
connect_to_trustie_ci_database(options)
|
||||
else
|
||||
connect_to_ci_database(options)
|
||||
end
|
||||
end
|
||||
|
||||
def list
|
||||
scope = User.active.recent.like(params[:search]).includes(:user_extension)
|
||||
@total_count = scope.size
|
||||
@users = paginate(scope)
|
||||
end
|
||||
|
||||
def show
|
||||
#待办事项,现在未做
|
||||
if User.current.admin? || User.current.login == @user.login
|
||||
@waiting_applied_messages = @user.applied_messages.waiting
|
||||
@common_applied_transfer_projects = AppliedTransferProject.where(owner_id: @user.id).common + AppliedTransferProject.where(owner_id: Organization.joins(team_users: :team).where(team_users: {user_id: @user.id}, teams: {authorize: %w(admin owner)} )).common
|
||||
@common_applied_projects = AppliedProject.where(project_id: @user.full_admin_projects).common
|
||||
#@undo_events = @waiting_applied_messages.size + @common_applied_transfer_projects.size + @common_applied_projects.size
|
||||
@undo_events = @common_applied_transfer_projects.size + @common_applied_projects.size
|
||||
else
|
||||
@waiting_applied_messages = AppliedMessage.none
|
||||
@common_applied_transfer_projects = AppliedTransferProject.none
|
||||
@common_applied_projects = AppliedProject.none
|
||||
@undo_events = 0
|
||||
end
|
||||
#用户的组织数量
|
||||
# @user_composes_count = @user.composes.size
|
||||
@user_composes_count = 0
|
||||
user_organizations = User.current.logged? ? @user.organizations.with_visibility(%w(common limited)) + @user.organizations.with_visibility("privacy").joins(:team_users).where(team_users: {user_id: current_user.id}) : @user.organizations.with_visibility("common")
|
||||
@user_org_count = user_organizations.size
|
||||
normal_projects = Project.members_projects(@user.id).to_sql
|
||||
org_projects = Project.joins(team_projects: [team: :team_users]).where(team_users: {user_id: @user.id}).to_sql
|
||||
projects = Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct
|
||||
user_projects = User.current.logged? && (User.current.admin? || User.current.login == @user.login) ? projects : projects.visible
|
||||
@projects_common_count = user_projects.common.size
|
||||
@projects_mirrior_count = user_projects.mirror.size
|
||||
@projects_sync_mirrior_count = user_projects.sync_mirror.size
|
||||
# 为了缓存活跃用户的基本信息,后续删除
|
||||
Cache::V2::OwnerCommonService.new(@user.id).read
|
||||
end
|
||||
|
||||
def watch_users
|
||||
watchers = Watcher.watching_users(@user.id).includes(:user).order("watchers.created_at desc")
|
||||
if params[:search].present?
|
||||
search_user_ids = User.where(id: watchers.pluck(:watchable_id)).like(params[:search]).pluck(:id)
|
||||
watchers = watchers.where(watchable_id: search_user_ids)
|
||||
end
|
||||
@watchers_count = watchers.size
|
||||
@watchers = paginate(watchers)
|
||||
end
|
||||
|
||||
def fan_users
|
||||
watchers = @user.watchers.includes(:user).order("watchers.created_at desc")
|
||||
watchers = watchers.joins(:user).merge(User.like(params[:search]))
|
||||
@watchers_count = watchers.size
|
||||
@watchers = paginate(watchers)
|
||||
end
|
||||
|
||||
def hovercard
|
||||
end
|
||||
|
||||
def update
|
||||
return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id])
|
||||
return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id)
|
||||
Util.write_file(@image, avatar_path(@user)) if user_params[:image].present?
|
||||
@user.attributes = user_params.except(:image)
|
||||
unless @user.save
|
||||
render_error(-1, @user.errors.full_messages.join(", "))
|
||||
end
|
||||
end
|
||||
|
||||
def update_image
|
||||
return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id])
|
||||
return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id)
|
||||
|
||||
Util.write_file(@image, avatar_path(@user))
|
||||
return render_ok({message: '头像修改成功'})
|
||||
rescue Exception => e
|
||||
uid_logger_error(e.message)
|
||||
render_error(-1, '头像修改失败!')
|
||||
end
|
||||
|
||||
def get_image
|
||||
return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id])
|
||||
return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id)
|
||||
|
||||
redirect_to Rails.application.config_for(:configuration)['platform_url'] + "/" + url_to_avatar(@user).to_s
|
||||
end
|
||||
|
||||
def me
|
||||
@user = current_user
|
||||
end
|
||||
|
||||
# 贴吧获取用户信接口
|
||||
def get_user_info
|
||||
begin
|
||||
@user = current_user
|
||||
begin
|
||||
result = Notice::Read::CountService.call(current_user.id)
|
||||
@message_unread_total = result.nil? ? 0 : result[2]["unread_notification"]
|
||||
rescue
|
||||
@message_unread_total = 0
|
||||
end
|
||||
# TODO 等消息上线再打开注释
|
||||
#@tidding_count = unviewed_tiddings(current_user) if current_user.present?
|
||||
rescue Exception => e
|
||||
uid_logger_error(e.message)
|
||||
missing_template
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def attachment_show
|
||||
file_name = params[:file_name]
|
||||
path = params[:path] || file_storage_directory
|
||||
send_file "#{path}/#{file_name}", :filename => "#{file_name}",
|
||||
:type => 'game',
|
||||
:disposition => 'attachment' #inline can open in browser
|
||||
end
|
||||
|
||||
def html_show
|
||||
@contents = File.read("#{params[:path]}")
|
||||
respond_to do |format|
|
||||
format.html {render :layout => false}
|
||||
end
|
||||
end
|
||||
|
||||
# Redo: 消息总数缓存
|
||||
def get_navigation_info
|
||||
# @old_domain = edu_setting('old_edu_host')
|
||||
# @user = current_user
|
||||
# # 新消息数
|
||||
# @new_message = @user.tidings.where("created_at > '#{@user.click_time}'").count > 0 || @user.private_messages.where("created_at > '#{@user.click_time}'").count > 0
|
||||
#
|
||||
# @user_url = "/users/#{@user.login}"
|
||||
# @career = Career.where(status: true).order("created_at asc").pluck(:id, :name)
|
||||
# @auth = User.current.ec_school.present? ? "#{@old_domain}/ecs/department?school_id=#{User.current.ec_school}" : nil
|
||||
end
|
||||
|
||||
# 用户回复功能
|
||||
def reply_message
|
||||
message = JournalsForMessage.new(reply_message_params)
|
||||
message.user_id = current_user.id
|
||||
message.save!
|
||||
|
||||
render_ok(id: message.id)
|
||||
end
|
||||
|
||||
# 搜索用户具有管理员角色的项目
|
||||
def search_user_projects
|
||||
projects = Project.where.not(status: 9)
|
||||
|
||||
projects = projects.joins(members: :member_roles).where(member_roles: { role_id: 3 })
|
||||
projects = projects.where(members: { user_id: current_user.id })
|
||||
|
||||
search = params[:search].to_s.strip
|
||||
projects = projects.where('projects.name LIKE ?', "%#{search}%") if search.present?
|
||||
|
||||
@projects = projects.select(:id, :name)
|
||||
end
|
||||
|
||||
#TODO 个人主页信息,forge上弃用-hs, 0602
|
||||
def homepage_info
|
||||
#待办事项,现在未做
|
||||
@undo_events = 10
|
||||
#用户的组织数量
|
||||
# @user_composes_count = @user.composes.size
|
||||
@user_composes_count = 10
|
||||
end
|
||||
|
||||
def brief_introduction
|
||||
content = params[:content].to_s.strip
|
||||
|
||||
current_user.user_extension.update!(brief_introduction: content)
|
||||
|
||||
render_ok
|
||||
end
|
||||
|
||||
def attendance
|
||||
attendance = Users::AttendanceService.call(current_user)
|
||||
render_ok(grade: current_user.grade, next_gold: attendance.next_gold)
|
||||
rescue Users::AttendanceService::Error => ex
|
||||
render_error(ex.message)
|
||||
end
|
||||
|
||||
# 其他平台登录后,必须将token同步到forge平台,实现sso登录功能
|
||||
def sync_token
|
||||
return render_error('未找相关用户!') unless @user
|
||||
|
||||
token = Token.get_or_create_permanent_login_token(@user, 'autologin')
|
||||
token.update_column(:value, params[:token])
|
||||
render_ok
|
||||
end
|
||||
|
||||
def trustie_related_projects
|
||||
projects = Project.includes(:owner, :members, :project_score).where(id: params[:ids]).order("updated_on desc")
|
||||
projects_json = []
|
||||
domain_url = EduSetting.get('host_name')
|
||||
if projects.present?
|
||||
projects.each do |p|
|
||||
project_url = "/#{p.owner.login}/#{p.identifier}"
|
||||
pj = {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
is_public: p.is_public,
|
||||
updated_on: p.updated_on.strftime("%Y-%m-%d"),
|
||||
status: p.status,
|
||||
is_member: p.member?(current_user.try(:id)),
|
||||
owner: {
|
||||
name: p.owner.try(:show_real_name),
|
||||
login: p.owner.login
|
||||
},
|
||||
members_count: p&.members.size,
|
||||
issues_count: p.issues_count - p.pull_requests_count,
|
||||
commits_count: p&.project_score&.changeset_num.to_i,
|
||||
http_url: domain_url + project_url,
|
||||
http_collaborator_url: domain_url + project_url + "/setting/collaborator",
|
||||
http_issues_url: domain_url + project_url + "/issues",
|
||||
http_commits_url: domain_url + project_url + "/commits",
|
||||
project_score: p&.project_score.present? ? p&.project_score&.as_json(:except=>[:created_at, :updated_at]).merge!(commit_time: format_time(p&.project_score&.commit_time)) : {}
|
||||
}
|
||||
projects_json.push(pj)
|
||||
end
|
||||
end
|
||||
Rails.logger.info("==========projects_json========+########{projects_json}")
|
||||
render json: { projects: projects_json.present? ? projects_json : {} }
|
||||
end
|
||||
|
||||
def trustie_projects
|
||||
user_id = User.select(:id, :login).where(login: params[:login])&.first&.id
|
||||
projects = Project.visible
|
||||
|
||||
projects = projects.joins(:members).where(members: { user_id: user_id })
|
||||
|
||||
search = params[:search].to_s.strip
|
||||
projects = projects.where('projects.name LIKE ?', "%#{search}%") if search.present?
|
||||
|
||||
projects = projects.select(:id, :name).limit(10).as_json
|
||||
render json: { projects: projects }
|
||||
end
|
||||
|
||||
def projects
|
||||
is_current_admin_user = User.current.logged? && (current_user&.admin? || current_user.id == @user.id)
|
||||
scope = Projects::ListMyQuery.call(params, @user,is_current_admin_user)
|
||||
@total_count = scope.size
|
||||
@projects = paginate(scope)
|
||||
end
|
||||
|
||||
# TODO 其他平台登录时同步修改gitea平台对应用户的密码
|
||||
# 该方法主要用于:别的平台初次部署对接forge平台,同步用户后,gitea平台对应的用户密码与forge平台用户密码不一致是问题
|
||||
def sync_gitea_pwd
|
||||
return render_error("未找到相关的用户") if @user.blank?
|
||||
|
||||
flag = sync_pwd_to_gitea!(@user, {password: params[:password].to_s})
|
||||
flag ? render_ok : render_error('同步失败!')
|
||||
end
|
||||
|
||||
# TODO
|
||||
# 同步trusite平台用户的salt信息,只需同步一次,同步完成后,该方法可以删除
|
||||
def sync_salt
|
||||
user = User.find_by_login params[:login]
|
||||
return if user.blank?
|
||||
user.update_column(:salt, params[:salt])
|
||||
render_ok
|
||||
end
|
||||
|
||||
def sync_user_info
|
||||
user = User.find_by_login params[:login]
|
||||
return render_forbidden unless user === current_user
|
||||
|
||||
sync_params = {
|
||||
email: params[:email],
|
||||
password: params[:password]
|
||||
}
|
||||
|
||||
Users::UpdateInfoForm.new(sync_params.merge(login: params[:login])).validate!
|
||||
|
||||
interactor = Gitea::User::UpdateInteractor.call(user.login, sync_params)
|
||||
if interactor.success?
|
||||
user.update!(password: params[:password], mail: params[:email], status: User::STATUS_ACTIVE)
|
||||
render_ok
|
||||
else
|
||||
render_error(interactor.error)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def load_user
|
||||
@user = User.find_by_login(params[:id]) || User.find_by(id: params[:id])
|
||||
end
|
||||
|
||||
def user_params
|
||||
params.require(:user).permit(:nickname, :image,
|
||||
user_extension_attributes: [
|
||||
:gender, :location, :location_city,
|
||||
:occupation, :technical_title,
|
||||
:school_id, :department_id, :province, :city,
|
||||
:custom_department, :identity, :student_id, :description,
|
||||
:show_super_description, :super_description,
|
||||
:show_email, :show_location, :show_department]
|
||||
)
|
||||
end
|
||||
|
||||
def reply_message_params
|
||||
normal_status(-1, "参数不对") if params[:journals_for_message][:jour_type].nil? || params[:journals_for_message][:jour_id].nil? ||
|
||||
params[:journals_for_message][:notes].nil? || params[:journals_for_message][:reply_id].nil?
|
||||
params.require(:journals_for_message).permit(:jour_type, :jour_id, :notes, :m_parent_id, :reply_id)
|
||||
end
|
||||
|
||||
def check_user_exist
|
||||
return if @user.present?
|
||||
render_not_found
|
||||
end
|
||||
|
||||
end
|
||||
class UsersController < ApplicationController
|
||||
include ApplicationHelper
|
||||
include Ci::DbConnectable
|
||||
|
||||
before_action :load_user, only: [:show, :homepage_info, :sync_token, :sync_gitea_pwd, :projects, :watch_users, :fan_users, :hovercard]
|
||||
before_action :check_user_exist, only: [:show, :homepage_info,:projects, :watch_users, :fan_users, :hovercard]
|
||||
before_action :require_login, only: %i[me sync_user_info]
|
||||
before_action :connect_to_ci_db, only: [:get_user_info]
|
||||
before_action :convert_image!, only: [:update, :update_image]
|
||||
skip_before_action :check_sign, only: [:attachment_show]
|
||||
before_action :sso_login, only: [:get_user_info]
|
||||
|
||||
def connect_to_ci_db(options={})
|
||||
if !(current_user && !current_user.is_a?(AnonymousUser) && current_user.devops_certification?)
|
||||
return
|
||||
end
|
||||
if current_user.ci_cloud_account.server_type == Ci::CloudAccount::SERVER_TYPE_TRUSTIE
|
||||
connect_to_trustie_ci_database(options)
|
||||
else
|
||||
connect_to_ci_database(options)
|
||||
end
|
||||
end
|
||||
|
||||
def list
|
||||
scope = User.active.recent.like(params[:search]).includes(:user_extension)
|
||||
@total_count = scope.size
|
||||
@users = paginate(scope)
|
||||
end
|
||||
|
||||
def show
|
||||
#待办事项,现在未做
|
||||
if User.current.admin? || User.current.login == @user.login
|
||||
@waiting_applied_messages = @user.applied_messages.waiting
|
||||
@common_applied_transfer_projects = AppliedTransferProject.where(owner_id: @user.id).common + AppliedTransferProject.where(owner_id: Organization.joins(team_users: :team).where(team_users: {user_id: @user.id}, teams: {authorize: %w(admin owner)} )).common
|
||||
@common_applied_projects = AppliedProject.where(project_id: @user.full_admin_projects).common
|
||||
#@undo_events = @waiting_applied_messages.size + @common_applied_transfer_projects.size + @common_applied_projects.size
|
||||
@undo_events = @common_applied_transfer_projects.size + @common_applied_projects.size
|
||||
else
|
||||
@waiting_applied_messages = AppliedMessage.none
|
||||
@common_applied_transfer_projects = AppliedTransferProject.none
|
||||
@common_applied_projects = AppliedProject.none
|
||||
@undo_events = 0
|
||||
end
|
||||
#用户的组织数量
|
||||
# @user_composes_count = @user.composes.size
|
||||
@user_composes_count = 0
|
||||
user_organizations = User.current.logged? ? @user.organizations.with_visibility(%w(common limited)) + @user.organizations.with_visibility("privacy").joins(:team_users).where(team_users: {user_id: current_user.id}) : @user.organizations.with_visibility("common")
|
||||
@user_org_count = user_organizations.size
|
||||
normal_projects = Project.members_projects(@user.id).to_sql
|
||||
org_projects = Project.joins(team_projects: [team: :team_users]).where(team_users: {user_id: @user.id}).to_sql
|
||||
projects = Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct
|
||||
user_projects = User.current.logged? && (User.current.admin? || User.current.login == @user.login) ? projects : projects.visible
|
||||
@projects_common_count = user_projects.common.size
|
||||
@projects_mirrior_count = user_projects.mirror.size
|
||||
@projects_sync_mirrior_count = user_projects.sync_mirror.size
|
||||
# 为了缓存活跃用户的基本信息,后续删除
|
||||
Cache::V2::OwnerCommonService.new(@user.id).read
|
||||
end
|
||||
|
||||
def watch_users
|
||||
watchers = Watcher.watching_users(@user.id).includes(:user).order("watchers.created_at desc")
|
||||
if params[:search].present?
|
||||
search_user_ids = User.where(id: watchers.pluck(:watchable_id)).like(params[:search]).pluck(:id)
|
||||
watchers = watchers.where(watchable_id: search_user_ids)
|
||||
end
|
||||
@watchers_count = watchers.size
|
||||
@watchers = paginate(watchers)
|
||||
end
|
||||
|
||||
def fan_users
|
||||
watchers = @user.watchers.includes(:user).order("watchers.created_at desc")
|
||||
watchers = watchers.joins(:user).merge(User.like(params[:search]))
|
||||
@watchers_count = watchers.size
|
||||
@watchers = paginate(watchers)
|
||||
end
|
||||
|
||||
def hovercard
|
||||
end
|
||||
|
||||
def update
|
||||
return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id])
|
||||
return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id)
|
||||
Util.write_file(@image, avatar_path(@user)) if user_params[:image].present?
|
||||
@user.attributes = user_params.except(:image)
|
||||
unless @user.save
|
||||
render_error(-1, @user.errors.full_messages.join(", "))
|
||||
end
|
||||
end
|
||||
|
||||
def update_image
|
||||
return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id])
|
||||
return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id)
|
||||
|
||||
Util.write_file(@image, avatar_path(@user))
|
||||
return render_ok({message: '头像修改成功'})
|
||||
rescue Exception => e
|
||||
uid_logger_error(e.message)
|
||||
render_error(-1, '头像修改失败!')
|
||||
end
|
||||
|
||||
def get_image
|
||||
return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id])
|
||||
return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id)
|
||||
|
||||
redirect_to Rails.application.config_for(:configuration)['platform_url'] + "/" + url_to_avatar(@user).to_s
|
||||
end
|
||||
|
||||
def me
|
||||
@user = current_user
|
||||
end
|
||||
|
||||
# 贴吧获取用户信接口
|
||||
def get_user_info
|
||||
begin
|
||||
@user = current_user
|
||||
begin
|
||||
result = Notice::Read::CountService.call(current_user.id)
|
||||
@message_unread_total = result.nil? ? 0 : result[2]["unread_notification"]
|
||||
rescue
|
||||
@message_unread_total = 0
|
||||
end
|
||||
# TODO 等消息上线再打开注释
|
||||
#@tidding_count = unviewed_tiddings(current_user) if current_user.present?
|
||||
rescue Exception => e
|
||||
uid_logger_error(e.message)
|
||||
missing_template
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def attachment_show
|
||||
file_name = params[:file_name]
|
||||
path = params[:path] || file_storage_directory
|
||||
send_file "#{path}/#{file_name}", :filename => "#{file_name}",
|
||||
:type => 'game',
|
||||
:disposition => 'attachment' #inline can open in browser
|
||||
end
|
||||
|
||||
def html_show
|
||||
@contents = File.read("#{params[:path]}")
|
||||
respond_to do |format|
|
||||
format.html {render :layout => false}
|
||||
end
|
||||
end
|
||||
|
||||
# Redo: 消息总数缓存
|
||||
def get_navigation_info
|
||||
# @old_domain = edu_setting('old_edu_host')
|
||||
# @user = current_user
|
||||
# # 新消息数
|
||||
# @new_message = @user.tidings.where("created_at > '#{@user.click_time}'").count > 0 || @user.private_messages.where("created_at > '#{@user.click_time}'").count > 0
|
||||
#
|
||||
# @user_url = "/users/#{@user.login}"
|
||||
# @career = Career.where(status: true).order("created_at asc").pluck(:id, :name)
|
||||
# @auth = User.current.ec_school.present? ? "#{@old_domain}/ecs/department?school_id=#{User.current.ec_school}" : nil
|
||||
end
|
||||
|
||||
# 用户回复功能
|
||||
def reply_message
|
||||
message = JournalsForMessage.new(reply_message_params)
|
||||
message.user_id = current_user.id
|
||||
message.save!
|
||||
|
||||
render_ok(id: message.id)
|
||||
end
|
||||
|
||||
# 搜索用户具有管理员角色的项目
|
||||
def search_user_projects
|
||||
projects = Project.where.not(status: 9)
|
||||
|
||||
projects = projects.joins(members: :member_roles).where(member_roles: { role_id: 3 })
|
||||
projects = projects.where(members: { user_id: current_user.id })
|
||||
|
||||
search = params[:search].to_s.strip
|
||||
projects = projects.where('projects.name LIKE ?', "%#{search}%") if search.present?
|
||||
|
||||
@projects = projects.select(:id, :name)
|
||||
end
|
||||
|
||||
#TODO 个人主页信息,forge上弃用-hs, 0602
|
||||
def homepage_info
|
||||
#待办事项,现在未做
|
||||
@undo_events = 10
|
||||
#用户的组织数量
|
||||
# @user_composes_count = @user.composes.size
|
||||
@user_composes_count = 10
|
||||
end
|
||||
|
||||
def brief_introduction
|
||||
content = params[:content].to_s.strip
|
||||
|
||||
current_user.user_extension.update!(brief_introduction: content)
|
||||
|
||||
render_ok
|
||||
end
|
||||
|
||||
def attendance
|
||||
attendance = Users::AttendanceService.call(current_user)
|
||||
render_ok(grade: current_user.grade, next_gold: attendance.next_gold)
|
||||
rescue Users::AttendanceService::Error => ex
|
||||
render_error(ex.message)
|
||||
end
|
||||
|
||||
# 其他平台登录后,必须将token同步到forge平台,实现sso登录功能
|
||||
def sync_token
|
||||
return render_error('未找相关用户!') unless @user
|
||||
|
||||
token = Token.get_or_create_permanent_login_token(@user, 'autologin')
|
||||
token.update_column(:value, params[:token])
|
||||
render_ok
|
||||
end
|
||||
|
||||
def trustie_related_projects
|
||||
projects = Project.includes(:owner, :members, :project_score).where(id: params[:ids]).order("updated_on desc")
|
||||
projects_json = []
|
||||
domain_url = EduSetting.get('host_name')
|
||||
if projects.present?
|
||||
projects.each do |p|
|
||||
project_url = "/#{p.owner.login}/#{p.identifier}"
|
||||
pj = {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
is_public: p.is_public,
|
||||
updated_on: p.updated_on.strftime("%Y-%m-%d"),
|
||||
status: p.status,
|
||||
is_member: p.member?(current_user.try(:id)),
|
||||
owner: {
|
||||
name: p.owner.try(:show_real_name),
|
||||
login: p.owner.login
|
||||
},
|
||||
members_count: p&.members.size,
|
||||
issues_count: p.issues_count - p.pull_requests_count,
|
||||
commits_count: p&.project_score&.changeset_num.to_i,
|
||||
http_url: domain_url + project_url,
|
||||
http_collaborator_url: domain_url + project_url + "/setting/collaborator",
|
||||
http_issues_url: domain_url + project_url + "/issues",
|
||||
http_commits_url: domain_url + project_url + "/commits",
|
||||
project_score: p&.project_score.present? ? p&.project_score&.as_json(:except=>[:created_at, :updated_at]).merge!(commit_time: format_time(p&.project_score&.commit_time)) : {}
|
||||
}
|
||||
projects_json.push(pj)
|
||||
end
|
||||
end
|
||||
Rails.logger.info("==========projects_json========+########{projects_json}")
|
||||
render json: { projects: projects_json.present? ? projects_json : {} }
|
||||
end
|
||||
|
||||
def trustie_projects
|
||||
user_id = User.select(:id, :login).where(login: params[:login])&.first&.id
|
||||
projects = Project.visible
|
||||
|
||||
projects = projects.joins(:members).where(members: { user_id: user_id })
|
||||
|
||||
search = params[:search].to_s.strip
|
||||
projects = projects.where('projects.name LIKE ?', "%#{search}%") if search.present?
|
||||
|
||||
projects = projects.select(:id, :name).limit(10).as_json
|
||||
render json: { projects: projects }
|
||||
end
|
||||
|
||||
def projects
|
||||
is_current_admin_user = User.current.logged? && (current_user&.admin? || current_user.id == @user.id)
|
||||
scope = Projects::ListMyQuery.call(params, @user,is_current_admin_user)
|
||||
@total_count = scope.size
|
||||
@projects = paginate(scope)
|
||||
end
|
||||
|
||||
# TODO 其他平台登录时同步修改gitea平台对应用户的密码
|
||||
# 该方法主要用于:别的平台初次部署对接forge平台,同步用户后,gitea平台对应的用户密码与forge平台用户密码不一致是问题
|
||||
def sync_gitea_pwd
|
||||
return render_error("未找到相关的用户") if @user.blank?
|
||||
|
||||
flag = sync_pwd_to_gitea!(@user, {password: params[:password].to_s})
|
||||
flag ? render_ok : render_error('同步失败!')
|
||||
end
|
||||
|
||||
# TODO
|
||||
# 同步trusite平台用户的salt信息,只需同步一次,同步完成后,该方法可以删除
|
||||
def sync_salt
|
||||
user = User.find_by_login params[:login]
|
||||
return if user.blank?
|
||||
user.update_column(:salt, params[:salt])
|
||||
render_ok
|
||||
end
|
||||
|
||||
def sync_user_info
|
||||
user = User.find_by_login params[:login]
|
||||
return render_forbidden unless user === current_user
|
||||
|
||||
sync_params = {
|
||||
email: params[:email],
|
||||
password: params[:password]
|
||||
}
|
||||
|
||||
Users::UpdateInfoForm.new(sync_params.merge(login: params[:login])).validate!
|
||||
|
||||
interactor = Gitea::User::UpdateInteractor.call(user.login, sync_params)
|
||||
if interactor.success?
|
||||
user.update!(password: params[:password], mail: params[:email], status: User::STATUS_ACTIVE)
|
||||
render_ok
|
||||
else
|
||||
render_error(interactor.error)
|
||||
end
|
||||
end
|
||||
|
||||
def email_search
|
||||
return render_error('请输入email') if params[:email].blank?
|
||||
@user = User.find_by(mail: params[:email])
|
||||
end
|
||||
|
||||
private
|
||||
def load_user
|
||||
@user = User.find_by_login(params[:id]) || User.find_by(id: params[:id])
|
||||
end
|
||||
|
||||
def user_params
|
||||
params.require(:user).permit(:nickname, :image,
|
||||
user_extension_attributes: [
|
||||
:gender, :location, :location_city,
|
||||
:occupation, :technical_title,
|
||||
:school_id, :department_id, :province, :city,
|
||||
:custom_department, :identity, :student_id, :description,
|
||||
:show_super_description, :super_description,
|
||||
:show_email, :show_location, :show_department]
|
||||
)
|
||||
end
|
||||
|
||||
def reply_message_params
|
||||
normal_status(-1, "参数不对") if params[:journals_for_message][:jour_type].nil? || params[:journals_for_message][:jour_id].nil? ||
|
||||
params[:journals_for_message][:notes].nil? || params[:journals_for_message][:reply_id].nil?
|
||||
params.require(:journals_for_message).permit(:jour_type, :jour_id, :notes, :m_parent_id, :reply_id)
|
||||
end
|
||||
|
||||
def check_user_exist
|
||||
return if @user.present?
|
||||
render_not_found
|
||||
end
|
||||
|
||||
def sso_login
|
||||
if params[:login].present? && !current_user.logged? && params[:websiteName].present?
|
||||
user = User.where("login = ?", "#{params[:login].presence}").first
|
||||
# 已同步注册,直接登录
|
||||
if user.present?
|
||||
successful_authentication(user)
|
||||
else
|
||||
autologin_register_by_educoder(params[:login].presence)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# 通过login参数查询头歌账号信息,注册并登录
|
||||
def autologin_register_by_educoder(edu_login)
|
||||
req_params = { "login" => "#{edu_login}", "private_token" => "hriEn3UwXfJs3PmyXnSH" }
|
||||
api_url= "https://data.educoder.net"
|
||||
client = Faraday.new(url: api_url)
|
||||
response = client.public_send("get", "/api/sources/get_user_info_by_login", req_params)
|
||||
result = JSON.parse(response.body)
|
||||
#查询
|
||||
return nil if result["status"].to_s != "0"
|
||||
|
||||
# login 邮箱 手机号 姓名 学校/单位
|
||||
user_info = result["data"]
|
||||
Rails.logger.info("user_info====== #{user_info}")
|
||||
login = user_info["login"]
|
||||
email = user_info["mail"]
|
||||
phone = user_info["phone"]
|
||||
real_name = user_info["username"]
|
||||
department_name = user_info["school"]
|
||||
password = "12345678"
|
||||
|
||||
# 没有用户时,新建用户并登录
|
||||
user = User.where("login = ? or phone = ? or mail = ? ", "#{login}", phone, email).first
|
||||
if user.present?
|
||||
# 手机号先记录,后续用
|
||||
user.update_column(:phone, "#{phone}") if phone.present?
|
||||
else
|
||||
ActiveRecord::Base.transaction do
|
||||
email = "#{login}@gitlink.org.cn" if email.blank?
|
||||
user_params = { status: 1, type: 'User', login: "#{login}", lastname: "#{real_name}", mail: "#{email}",
|
||||
nickname: "#{real_name}", professional_certification: 0, certification: 0, grade: 0,
|
||||
password: "#{password}", phone: "#{phone}", profile_completed: 1 }
|
||||
user = User.create!(user_params)
|
||||
UserExtension.create!(user_id: user.id, gender: 1, custom_department: "#{department_name}")
|
||||
interactor = Gitea::RegisterInteractor.call({username: login, email: email, password: password})
|
||||
if interactor.success?
|
||||
gitea_user = interactor.result
|
||||
Rails.logger.info("Gitea::RegisterInteractor.call result====== #{gitea_user}")
|
||||
result = Gitea::User::GenerateTokenService.call(login, password)
|
||||
user.gitea_token = result['sha1']
|
||||
user.gitea_uid = gitea_user[:body]['id']
|
||||
user.save!
|
||||
else
|
||||
Rails.logger.info("Gitea::RegisterInteractor.call error====== #{interactor.error}")
|
||||
end
|
||||
end
|
||||
end
|
||||
successful_authentication(user) if user.present?
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -16,6 +16,7 @@ includes:
|
|||
- users
|
||||
- projects
|
||||
- repositories
|
||||
- traces
|
||||
- pulls
|
||||
- issues
|
||||
- organizations
|
||||
|
|
|
@ -280,7 +280,7 @@ repo |是| |string |项目标识identifier
|
|||
### 返回字段说明
|
||||
参数 | 类型 | 字段说明
|
||||
--------- | ----------- | -----------
|
||||
menu_name |string|导航名称, home:主页,code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑,activity:动态,setting:仓库设置
|
||||
menu_name |string|导航名称, home:主页,code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑,wiki:维基,services:服务,activity:动态,setting:仓库设置
|
||||
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
@ -408,7 +408,7 @@ await octokit.request('POST /api/yystopf/ceshi/project_units')
|
|||
### 请求参数
|
||||
参数 | 必选 | 默认 | 类型 | 字段说明
|
||||
--------- | ------- | ------- | -------- | ----------
|
||||
|unit_types |是| |array | 项目模块内容, 支持以下参数:code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑 |
|
||||
|unit_types |是| |array | 项目模块内容, 支持以下参数:code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑,wiki:维基,resources:资源库,services:服务 |
|
||||
|
||||
|
||||
### 返回字段说明:
|
||||
|
@ -849,4 +849,36 @@ await octokit.request('POST /api/:owner/:repo/applied_transfer_projects/cancel.j
|
|||
"created_at": "2021-04-26 09:54",
|
||||
"time_ago": "1分钟前"
|
||||
}
|
||||
```
|
||||
|
||||
## 退出项目
|
||||
供项目成员(开发者、报告者)退出项目用
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/quit.json
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('POST /api/:owner/:repo/quit.json')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`POST /api/:owner/:repo/quit.json`
|
||||
|
||||
### 请求参数
|
||||
参数 | 必选 | 默认 | 类型 | 字段说明
|
||||
--------- | ------- | ------- | -------- | ----------
|
||||
|owner |是| |string |用户登录名 |
|
||||
|repo |是| |string |项目标识identifier |
|
||||
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 0,
|
||||
"message": "success"
|
||||
}
|
||||
```
|
|
@ -0,0 +1,217 @@
|
|||
# Traces
|
||||
|
||||
## 代码溯源初始化
|
||||
用户同意协议后请求的接口,创建代码溯源的账号
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X POST \
|
||||
http://localhost:3000/api/traces/trace_users.json
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('POST /api/traces/trace_users.json')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`POST api/traces/trace_users.json`
|
||||
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 0,
|
||||
"message": "success"
|
||||
}
|
||||
```
|
||||
<aside class="success">
|
||||
Success — a happy kitten is an authenticated kitten!
|
||||
</aside>
|
||||
|
||||
## 代码分析结果列表
|
||||
查询项目下代码分析的结果
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X GET \
|
||||
http://localhost:3000/api/traces/yystopf/many_branch/task_results.json
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('GET /api/traces/:owner/:repo/task_results.json')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`GET api/traces/:owner/:repo/task_results.json`
|
||||
|
||||
### 请求参数
|
||||
参数 | 必选 | 默认 | 类型 | 字段说明
|
||||
--------- | ------- | ------- | -------- | ----------
|
||||
owner|是|否|string | 项目所有者标识|
|
||||
repo|是 | 否|string | 项目标识 |
|
||||
page |否| 1 | int | 页码 |
|
||||
limit |否| 15 | int | 每页数量 |
|
||||
|
||||
### 返回字段说明(暂缺)
|
||||
<!-- 参数 | 类型 | 字段说明
|
||||
--------- | ----------- | -----------
|
||||
total_count |int |总数 |
|
||||
public_keys.id |int |ID|
|
||||
public_keys.name |string|密钥标题|
|
||||
public_keys.content |string|密钥内容|
|
||||
public_keys.fingerprint |string|密钥标识|
|
||||
public_keys.created_time |string|密钥创建时间| -->
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"accuracy": "20",
|
||||
"code_type": "C",
|
||||
"depth": "2",
|
||||
"detect_flag": "快速-组件级",
|
||||
"detect_rule": "快速-组件级,2,20,,开源软件,50,10",
|
||||
"detect_startdate": "2022-05-10 15:59:46 ",
|
||||
"detect_status": "fail",
|
||||
"detectflag": "快速-组件级",
|
||||
"fail_reason": "Invalid package type",
|
||||
"file_name": "many_branch.zip",
|
||||
"license_process": "100",
|
||||
"licenseparam": "开源软件",
|
||||
"package_type": "",
|
||||
"product_name": "84727546110",
|
||||
"project_id": "6dbc3e42-5857-4ca4-a54d-58fd9dbf6dc5",
|
||||
"sim_process": "100",
|
||||
"similarity_process": "2",
|
||||
"task_id": "15139171-091b-4316-98b1-6068970efa44",
|
||||
"totalsize": 5,
|
||||
"uid": "78",
|
||||
"vuln_process": "",
|
||||
"vulnlevel": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
<aside class="success">
|
||||
Success — a happy kitten is an authenticated kitten!
|
||||
</aside>
|
||||
|
||||
|
||||
|
||||
## 新建分析
|
||||
用户选择仓库分支进行代码分析的接口
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X POST \
|
||||
http://localhost:3000/api/traces/yystopf/many_branch/tasks.json
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('POST /api/traces/:owner/:repo/tasks.json')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`POST api/traces/:owner/:repo/tasks.json`
|
||||
|
||||
### 请求参数
|
||||
参数 | 必选 | 默认 | 类型 | 字段说明
|
||||
--------- | ------- | ------- | -------- | ----------
|
||||
owner |是 | 否 | string | 项目所有者标识 |
|
||||
repo |是 | 否 | string | 项目标识 |
|
||||
branch_name|是 | 否| string | 分支名称 |
|
||||
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 0,
|
||||
"message": "success"
|
||||
}
|
||||
```
|
||||
<aside class="success">
|
||||
Success — a happy kitten is an authenticated kitten!
|
||||
</aside>
|
||||
|
||||
## 重新扫描
|
||||
对代码分析结果进行再次分析
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X GET \
|
||||
http://localhost:3000/api/traces/yystopf/many_branch/reload_task.json
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('GET /api/traces/:owner/:repo/reload_task.json')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`GET api/traces/:owner/:repo/reload_task.json`
|
||||
|
||||
### 请求参数
|
||||
参数 | 必选 | 默认 | 类型 | 字段说明
|
||||
--------- | ------- | ------- | -------- | ----------
|
||||
owner |是 | 否 | string | 项目所有者标识 |
|
||||
repo |是 | 否 | string | 项目标识 |
|
||||
project_id|是 | 否| string | 代码分析结果里的project_id |
|
||||
branch_name|是 | 否| string | 分支名称 |
|
||||
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 0,
|
||||
"message": "success"
|
||||
}
|
||||
```
|
||||
<aside class="success">
|
||||
Success — a happy kitten is an authenticated kitten!
|
||||
</aside>
|
||||
|
||||
|
||||
## 下载报告
|
||||
把代码分析的结果下载到本地
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X GET \
|
||||
http://localhost:3000/api/traces/yystopf/many_branch/task_pdf.json
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('GET /api/traces/:owner/:repo/task_pdf.json')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`GET api/traces/:owner/:repo/task_pdf.json`
|
||||
|
||||
### 请求参数
|
||||
参数 | 必选 | 默认 | 类型 | 字段说明
|
||||
--------- | ------- | ------- | -------- | ----------
|
||||
owner |是 | 否 | string | 项目所有者标识 |
|
||||
repo |是 | 否 | string | 项目标识 |
|
||||
task_id|是 | 否| string | 代码分析结果里的task_id |
|
||||
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 0,
|
||||
"message": "success"
|
||||
}
|
||||
```
|
||||
<aside class="success">
|
||||
Success — a happy kitten is an authenticated kitten!
|
||||
</aside>
|
|
@ -4,7 +4,7 @@ class Gitea::User::UpdateForm
|
|||
|
||||
attr_accessor :username, :email, :admin, :allow_create_organization, :allow_git_hook, :allow_import_local,
|
||||
:full_name, :location, :login_name, :max_repo_creation, :must_change_password, :password, :prohibit_login,
|
||||
:source_id, :website
|
||||
:source_id, :website, :new_name
|
||||
|
||||
validates :username, presence: true
|
||||
validates :email, presence: true, format: { with: EMAIL_REGEX, multiline: true }
|
||||
|
|
|
@ -3,28 +3,40 @@ module Register
|
|||
include ActiveModel::Model
|
||||
|
||||
private
|
||||
def check_login(login)
|
||||
def check_login(login, user=nil)
|
||||
login = strip(login)
|
||||
raise LoginError, "登录名格式有误" unless login =~ CustomRegexp::LOGIN
|
||||
|
||||
login_exist = Owner.exists?(login: login) || ReversedKeyword.check_exists?(login)
|
||||
raise LoginError, '登录名已被使用' if login_exist
|
||||
if user.present?
|
||||
raise LoginError, '登录名已被使用' if login_exist && login != user&.login
|
||||
else
|
||||
raise LoginError, '登录名已被使用' if login_exist
|
||||
end
|
||||
end
|
||||
|
||||
def check_mail(mail)
|
||||
def check_mail(mail, user=nil)
|
||||
mail = strip(mail)
|
||||
raise EmailError, "邮件格式有误" unless mail =~ CustomRegexp::EMAIL
|
||||
|
||||
mail_exist = Owner.exists?(mail: mail)
|
||||
raise EmailError, '邮箱已被使用' if mail_exist
|
||||
if user.present?
|
||||
raise EmailError, '邮箱已被使用' if mail_exist && mail != user&.mail
|
||||
else
|
||||
raise EmailError, '邮箱已被使用' if mail_exist
|
||||
end
|
||||
end
|
||||
|
||||
def check_phone(phone)
|
||||
def check_phone(phone, user=nil)
|
||||
phone = strip(phone)
|
||||
raise PhoneError, "手机号格式有误" unless phone =~ CustomRegexp::PHONE
|
||||
|
||||
phone_exist = Owner.exists?(phone: phone)
|
||||
raise PhoneError, '手机号已被使用' if phone_exist
|
||||
if user.present?
|
||||
raise PhoneError, '手机号已被使用' if phone_exist && phone != user&.phone
|
||||
else
|
||||
raise PhoneError, '手机号已被使用' if phone_exist
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
module Register
|
||||
class LoginCheckColumnsForm < Register::BaseForm
|
||||
attr_accessor :type, :value, :user
|
||||
|
||||
validates :type, presence: true, numericality: true
|
||||
validates :value, presence: true
|
||||
validate :check!
|
||||
|
||||
def check!
|
||||
# params[:type] 为事件类型 1:登录名(login) 2:email(邮箱) 3:phone(手机号)
|
||||
case strip(type).to_i
|
||||
when 1 then check_login(strip(value), user)
|
||||
when 2 then check_mail(strip(value), user)
|
||||
when 3 then check_phone(strip(value), user)
|
||||
else raise("type值无效")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,486 +1,486 @@
|
|||
# 所有的方法请按首字母的顺序依次列出
|
||||
module ApplicationHelper
|
||||
include Gitlink::I18n
|
||||
include GitHelper
|
||||
|
||||
ONE_MINUTE = 60 * 1000
|
||||
ONE_HOUR = 60 * ONE_MINUTE
|
||||
ONE_DAY = 24 * ONE_HOUR
|
||||
ONE_MONTH = 30 * ONE_DAY
|
||||
|
||||
ONE_YEAR = 12 * ONE_MONTH
|
||||
|
||||
# 全局参数配置
|
||||
def edu_setting name
|
||||
EduSetting.get(name)
|
||||
end
|
||||
|
||||
# xss共计问题
|
||||
def content_safe content
|
||||
tags = %w(
|
||||
a abbr b bdo blockquote br caption cite code col colgroup dd del dfn dl
|
||||
dt em figcaption figure h1 h2 h3 h4 h5 h6 hgroup i img ins kbd li mark
|
||||
ol p pre q rp rt ruby s samp small strike strong sub sup table tbody td
|
||||
tfoot th thead time tr u ul var wbr div span
|
||||
)
|
||||
attributes = %w(href src width height alt cite datetime title class name xml:lang abbr style)
|
||||
sanitize content, tags: tags, attributes: attributes
|
||||
end
|
||||
|
||||
def graduation_navigation graduation
|
||||
graduation.class.to_s == "GraduationTopic" ? "毕设选题" : "毕设任务"
|
||||
end
|
||||
|
||||
def graduation_navigation_id course
|
||||
course.course_modules.find_by(module_type: "graduation").try(:id)
|
||||
end
|
||||
|
||||
# git用户
|
||||
# git用户命名规则:login+"@educoder.net"
|
||||
def git_username(email)
|
||||
User.find_by_mail(email) || User.find_by_login(email.split("@").first)
|
||||
end
|
||||
|
||||
# 不同的类型扩展不同的目录
|
||||
def relative_path
|
||||
"avatars"
|
||||
end
|
||||
|
||||
def replace_bytes_to_b(size_string)
|
||||
return size_string.gsub("Bytes", "B").gsub("bytes", "B").gsub("字节", "B")
|
||||
end
|
||||
|
||||
def storage_path
|
||||
File.join(Rails.root, "public", "images", relative_path)
|
||||
end
|
||||
|
||||
# 推荐实训
|
||||
def recommend_shixun(shixun)
|
||||
tag_repertoire_id = shixun.tag_repertoires.first.present? ? shixun.tag_repertoires.first.try(:id) : 0
|
||||
shixun_id = ShixunTagRepertoire.where("tag_repertoire_id = #{tag_repertoire_id} and
|
||||
shixun_id != #{shixun.id}").pluck(:shixun_id)
|
||||
|
||||
shixun_id = shixun_id.blank? ? -1 : shixun_id.join(",")
|
||||
Shixun.select([:id, :name, :user_id, :challenges_count, :myshixuns_count, :trainee, :identifier]).where("id
|
||||
in(#{shixun_id})").unhidden.publiced.order("homepage_show asc, myshixuns_count desc").limit(3)
|
||||
|
||||
end
|
||||
|
||||
# shixun开启挑战对应的行为名及url
|
||||
def task_operation_url current_myshixun, shixun
|
||||
if current_myshixun.blank?
|
||||
name = shixun.status == 0 ? "模拟实战" : "开启挑战"
|
||||
url = "/shixuns/#{shixun.identifier}/shixun_exec"
|
||||
else
|
||||
identifier = current_myshixun.current_task(current_myshixun.games).try(:identifier)
|
||||
if current_myshixun.status == 1
|
||||
name = "查看实战"
|
||||
else
|
||||
name = "继续挑战"
|
||||
end
|
||||
url = identifier
|
||||
end
|
||||
[name, url]
|
||||
end
|
||||
|
||||
# 获取当前时间
|
||||
def time_from_now(time)
|
||||
if String === time
|
||||
time = Time.parse(time)
|
||||
end
|
||||
|
||||
lastUpdateTime = time.to_i*1000
|
||||
|
||||
currentTime = Time.now.to_i*1000
|
||||
timePassed = currentTime - lastUpdateTime
|
||||
timeIntoFormat = 0
|
||||
updateAtValue = ""
|
||||
if timePassed < 0
|
||||
updateAtValue = "刚刚"
|
||||
elsif timePassed < ONE_MINUTE
|
||||
updateAtValue = "1分钟前"
|
||||
elsif timePassed < ONE_HOUR
|
||||
timeIntoFormat = timePassed / ONE_MINUTE
|
||||
updateAtValue = timeIntoFormat.to_s + "分钟前"
|
||||
elsif (timePassed < ONE_DAY)
|
||||
timeIntoFormat = timePassed / ONE_HOUR
|
||||
updateAtValue = timeIntoFormat.to_s + "小时前"
|
||||
elsif (timePassed < ONE_MONTH)
|
||||
timeIntoFormat = timePassed / ONE_DAY
|
||||
updateAtValue = timeIntoFormat.to_s + "天前"
|
||||
elsif (timePassed < ONE_YEAR)
|
||||
timeIntoFormat = timePassed / ONE_MONTH
|
||||
updateAtValue = timeIntoFormat.to_s + "个月前"
|
||||
else
|
||||
timeIntoFormat = timePassed / ONE_YEAR
|
||||
updateAtValue = timeIntoFormat.to_s + "年前"
|
||||
end
|
||||
updateAtValue
|
||||
end
|
||||
|
||||
# 计算到结束还有多长时间 **天**小时**分
|
||||
def how_much_time(time)
|
||||
if time.nil? || time < Time.now #6.21 -hs 增加小于time.now
|
||||
''
|
||||
else
|
||||
result = ((time - Time.now.to_i).to_i / (24*60*60)).to_s + " 天 "
|
||||
result += (((time - Time.now.to_i).to_i % (24*60*60)) / (60*60)).to_s + " 小时 "
|
||||
result + ((((time - Time.now.to_i).to_i % (24*60*60)) % (60*60)) / 60).to_s + " 分 "
|
||||
end
|
||||
end
|
||||
|
||||
def format_time(time)
|
||||
time.present? ? time.strftime("%Y-%m-%d %H:%M") : ''
|
||||
end
|
||||
|
||||
# 用户图像url,如果不存在的话,source为匿名用户,即默认使用匿名用户图像
|
||||
def url_to_avatar(source)
|
||||
if File.exist?(disk_filename(source&.class, source&.id))
|
||||
ctime = File.ctime(disk_filename(source.class, source.id)).to_i
|
||||
if %w(User Organization).include?(source.class.to_s)
|
||||
File.join("images", relative_path, ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
|
||||
else
|
||||
File.join("images/avatars", ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
|
||||
end
|
||||
elsif source.class.to_s == 'User'
|
||||
source.get_letter_avatar_url
|
||||
end
|
||||
end
|
||||
|
||||
def url_to_avatar_with_platform_url(source)
|
||||
platform_url = Rails.application.config_for(:configuration)['platform_url']
|
||||
if platform_url
|
||||
return Rails.application.config_for(:configuration)['platform_url'] + "/" + url_to_avatar(source).to_s
|
||||
else
|
||||
return url_to_avatar(source).to_s
|
||||
end
|
||||
end
|
||||
|
||||
# 主页banner图
|
||||
def banner_img(source_type)
|
||||
if File.exist?(disk_filename(source_type, "banner"))
|
||||
ctime = File.ctime(disk_filename(source_type, "banner")).to_i
|
||||
File.join("images/avatars", ["#{source_type}", "banner"]) + "?t=#{ctime}"
|
||||
end
|
||||
end
|
||||
|
||||
def disk_filename(source_type,source_id,image_file=nil)
|
||||
File.join(storage_path, "#{source_type}", "#{source_id}")
|
||||
end
|
||||
|
||||
def disk_auth_filename(source_type, source_id, type)
|
||||
File.join(storage_path, "#{source_type}", "#{source_id}#{type}")
|
||||
end
|
||||
|
||||
def disk_real_name_auth_filename(source_id)
|
||||
disk_auth_filename('UserAuthentication', source_id, 'ID')
|
||||
end
|
||||
|
||||
def auth_file_url(source_type, source_id, type)
|
||||
File.join('/images', relative_path, source_type, "#{source_id}#{type}")
|
||||
end
|
||||
|
||||
def real_name_auth_file_url(source_id)
|
||||
auth_file_url('UserAuthentication', source_id, 'ID')
|
||||
end
|
||||
|
||||
def disk_professional_auth_filename(source_id)
|
||||
disk_auth_filename('UserAuthentication', source_id, 'PRO')
|
||||
end
|
||||
|
||||
def professional_auth_file_url(source_id)
|
||||
auth_file_url('UserAuthentication', source_id, 'PRO')
|
||||
end
|
||||
|
||||
def shixun_url_to_avatar(shixun)
|
||||
if File.exist?(disk_filename(shixun.class, shixun.id))
|
||||
File.join("images/#{relative_path}", "#{shixun.class}", "#{shixun.id}")
|
||||
else
|
||||
File.join("educoder", "index", "shixun", "shixun#{rand(23)}.jpg")
|
||||
end
|
||||
end
|
||||
|
||||
# 选用实训的学校情况
|
||||
def school_user_detail shixun
|
||||
user_ids = shixun.myshixuns.map(&:user_id).uniq # 走缓存取数据
|
||||
school_ids = UserExtension.where(user_id:user_ids).pluck(:school_id).uniq
|
||||
school_names = School.where(id: school_ids[0..1]).pluck(:name)
|
||||
school_size = school_ids.size
|
||||
str = school_size > 0 ? "#{school_names.join("、")}等 #{school_size}所" : "0所"
|
||||
end
|
||||
|
||||
# 普通/分组 作业作品状态数组
|
||||
def student_work_status homework, user_id, course, work
|
||||
status = []
|
||||
homework_setting = homework.homework_group_setting user_id, true
|
||||
work = work || StudentWork.create(homework_common_id: homework.id, user_id: user_id)
|
||||
late_time = homework.late_time || course.end_date
|
||||
|
||||
if course.is_end && work && work.work_status > 0
|
||||
status << "查看作品"
|
||||
elsif !course.is_end
|
||||
if homework_setting.publish_time && homework_setting.publish_time < Time.now
|
||||
# 作业未截止时
|
||||
if homework_setting.end_time > Time.now
|
||||
if homework.homework_type == "group" && homework.homework_detail_group.base_on_project
|
||||
if work.project_id.nil? || work.project_id == 0
|
||||
status << "创建项目"
|
||||
status << "关联项目"
|
||||
elsif work.work_status == 0
|
||||
status << "取消关联"
|
||||
status << "提交作品"
|
||||
else
|
||||
status << "修改作品"
|
||||
end
|
||||
else
|
||||
if work.work_status == 0
|
||||
status << "提交作品"
|
||||
else
|
||||
status << "修改作品"
|
||||
end
|
||||
end
|
||||
|
||||
# 补交阶段
|
||||
elsif homework.allow_late && (late_time.nil? || late_time > Time.now)
|
||||
if homework.homework_type == "group" && homework.homework_detail_group.base_on_project
|
||||
if work.project_id.nil? || work.project_id == 0
|
||||
status << "创建项目"
|
||||
status << "关联项目"
|
||||
elsif work.work_status == 0
|
||||
status << "取消关联"
|
||||
status << "补交作品"
|
||||
else
|
||||
status << "补交附件"
|
||||
status << "查看作品"
|
||||
end
|
||||
else
|
||||
if work.work_status == 0
|
||||
status << "补交作品"
|
||||
else
|
||||
status << "补交附件"
|
||||
status << "查看作品"
|
||||
end
|
||||
end
|
||||
|
||||
# 匿评阶段
|
||||
elsif work.work_status != 0
|
||||
if homework.homework_detail_manual.comment_status == 3
|
||||
work_ids = homework.student_works.has_committed.pluck(:id)
|
||||
if StudentWorksEvaluationDistribution.where(student_work_id: work_ids, user_id: user_id).size > 0
|
||||
status << "匿评作品"
|
||||
end
|
||||
end
|
||||
status << "查看作品"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def commit_des_status work, homework
|
||||
status = []
|
||||
homework_setting = homework.homework_group_setting work.user_id
|
||||
|
||||
# 作业已发布且作业未截止(补交未截止)且提交了作品才能提交或修改总结
|
||||
if homework_setting.publish_time && homework_setting.publish_time < Time.now && work.work_status > 0 &&
|
||||
((homework_setting.end_time && homework_setting.end_time > Time.now) ||
|
||||
(homework.allow_late && homework.late_time && homework.late_time > Time.now))
|
||||
work.description.present? ? status << "修改总结" : status << "提交总结"
|
||||
end
|
||||
end
|
||||
|
||||
def download_url attachment,options={}
|
||||
attachment_path(attachment,options)
|
||||
end
|
||||
|
||||
# 耗时:天、小时、分、秒
|
||||
# 小于1分钟则不显示
|
||||
def game_spend_time time
|
||||
day = time / 86400
|
||||
hour = time % (24*60*60) / (60*60)
|
||||
min = time % (24*60*60) % (60*60) / 60
|
||||
sec = time % (24*60*60) % (60*60) % 60
|
||||
if day < 1
|
||||
if hour < 1
|
||||
if min < 1
|
||||
if sec < 1
|
||||
time = "--"
|
||||
else
|
||||
time = "#{sec}秒"
|
||||
end
|
||||
else
|
||||
time = "#{min}分钟 #{sec}秒"
|
||||
end
|
||||
else
|
||||
time = "#{hour}小时 #{min}分钟 #{sec}秒"
|
||||
end
|
||||
else
|
||||
time = "#{day}天 #{hour}小时 #{min}分钟 #{sec}秒"
|
||||
end
|
||||
return time
|
||||
end
|
||||
|
||||
def absolute_path(file_path)
|
||||
file_root_directory + File.join(edu_setting('attachment_folder'), file_path)
|
||||
end
|
||||
|
||||
def file_root_directory
|
||||
Rails.root.to_s
|
||||
end
|
||||
|
||||
def file_storage_directory
|
||||
file_root_directory + edu_setting('attachment_folder')
|
||||
end
|
||||
|
||||
def local_path(file)
|
||||
File.join(file.disk_directory, file.disk_filename)
|
||||
end
|
||||
|
||||
def update_downloads(file)
|
||||
file.update_attributes(:downloads => file.downloads + 1)
|
||||
end
|
||||
|
||||
# 项目信息,
|
||||
def project_info work, current_user, course_identity
|
||||
project = work.project
|
||||
if project
|
||||
if project.status == 9
|
||||
{id: -1, name: "#{project.name}(已删除)", title: "该项目已删除", author: project.creator, member_count: project.project_members.count}
|
||||
else
|
||||
project_score = project.project_score
|
||||
if project.is_public || current_user.manager_of_project?(project) || course_identity < Course::STUDENT
|
||||
{id: project.id, name: project.name, author: project.creator, member_count: project.project_members.count,
|
||||
all_score: project_score.all_score, code_score: project_score.code_score, issue_score: project_score.issue_score,
|
||||
attachment_score: project_score.attachment_score, message_score: project_score.message_score}
|
||||
else
|
||||
{id: -1, name: "#{project.name}", title: "该项目是私有的", author: project.creator, member_count: project.project_members.count,
|
||||
all_score: project_score.all_score, code_score: project_score.code_score, issue_score: project_score.issue_score,
|
||||
attachment_score: project_score.attachment_score, message_score: project_score.message_score}
|
||||
end
|
||||
end
|
||||
else
|
||||
{id: -1, name: "--", title: "--"}
|
||||
end
|
||||
end
|
||||
|
||||
def message_content(content)
|
||||
content = (strip_html content).strip
|
||||
content = content.gsub(/\s+/, " ")
|
||||
if content.gsub(" ", "") == ""
|
||||
content = "[非文本消息]"
|
||||
end
|
||||
content
|
||||
end
|
||||
|
||||
def strip_html(text, len=0, endss="...")
|
||||
ss = ""
|
||||
if !text.nil? && text.length>0
|
||||
ss=text.gsub(/<\/?.*?>/, '').strip
|
||||
ss = ss.gsub(/ */, '')
|
||||
ss = ss.gsub(/\r\n/,'') #新增
|
||||
ss = ss.gsub(/\n/,'') #新增
|
||||
if len > 0 && ss.length > len
|
||||
ss = ss[0, len] + endss
|
||||
elsif len > 0 && ss.length <= len
|
||||
ss = ss
|
||||
#ss = truncate(ss, :length => len)
|
||||
end
|
||||
end
|
||||
ss
|
||||
end
|
||||
|
||||
def strip_export_title(content)
|
||||
con_ = ""
|
||||
if content.length > 0
|
||||
con_ = strip_tags(content)
|
||||
con_ = con_.gsub(/\r\n/,'').gsub(/ */, '').strip
|
||||
end
|
||||
con_
|
||||
end
|
||||
|
||||
def pdf_load_sources(*arg)
|
||||
arr = arg.map do |path|
|
||||
content_tag(:script) do
|
||||
File.open(Rails.root.join('public', path)).read.to_s.html_safe
|
||||
end
|
||||
end
|
||||
|
||||
raw arr.join('')
|
||||
end
|
||||
|
||||
|
||||
|
||||
# 导出pdf时,转化markdown为html
|
||||
def to_markdown(text,origin_url)
|
||||
return nil if text.blank?
|
||||
options = {
|
||||
:autolink => true,
|
||||
:no_intra_emphasis => true,
|
||||
:fenced_code_blocks => true,
|
||||
:lax_html_blocks => true,
|
||||
:strikethrough => true,
|
||||
:superscript => false,
|
||||
:tables => true
|
||||
}
|
||||
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, options)
|
||||
m_t = markdown.render(text)
|
||||
m_t&.include?("src=\"") ? m_t&.gsub("src=\"","src=\"#{origin_url}") : m_t
|
||||
end
|
||||
|
||||
def shixun_status_class(shixun)
|
||||
case shixun.status
|
||||
when 0 then 'text-info'
|
||||
when 1 then 'text-warning'
|
||||
when 2 then 'text-success'
|
||||
when 3 then 'text-secondary'
|
||||
end
|
||||
end
|
||||
|
||||
def render_unix_time(date)
|
||||
date.to_time.to_i
|
||||
end
|
||||
|
||||
def find_user_by_login(login)
|
||||
User.find_by_login login
|
||||
end
|
||||
|
||||
def find_user_by_login_and_mail(login, mail)
|
||||
User.find_by(login: login, mail: mail)
|
||||
end
|
||||
|
||||
def find_user_by_gitea_uid(gitea_uid)
|
||||
User.find_by(gitea_uid: gitea_uid)
|
||||
end
|
||||
|
||||
def find_user_in_redis_cache(login, email)
|
||||
$redis_cache.hgetall("v2-owner-common:#{login}-#{email}")
|
||||
end
|
||||
|
||||
def find_user_in_redis_cache_by_id(id)
|
||||
$redis_cache.hgetall("v2-owner-common:#{id}")
|
||||
end
|
||||
|
||||
def render_base64_decoded(str)
|
||||
return nil if str.blank?
|
||||
Base64.decode64 str
|
||||
end
|
||||
|
||||
def render_admin_statistics_item
|
||||
url = Rails.application.config_for(:configuration)["admin_statistics_url"]
|
||||
|
||||
return if url.blank?
|
||||
content_tag(:li) do
|
||||
sidebar_item(url, "数据统计", icon: 'bar-chart', controller: 'root')
|
||||
end
|
||||
end
|
||||
|
||||
# 1 手机类型;0 邮箱类型
|
||||
# 注意新版的login是自动名生成的
|
||||
def phone_mail_type value
|
||||
value =~ /^1\d{10}$/ ? 1 : 0
|
||||
end
|
||||
|
||||
def strip(str)
|
||||
str.to_s.strip.presence
|
||||
end
|
||||
|
||||
end
|
||||
# 所有的方法请按首字母的顺序依次列出
|
||||
module ApplicationHelper
|
||||
include Gitlink::I18n
|
||||
include GitHelper
|
||||
|
||||
ONE_MINUTE = 60 * 1000
|
||||
ONE_HOUR = 60 * ONE_MINUTE
|
||||
ONE_DAY = 24 * ONE_HOUR
|
||||
ONE_MONTH = 30 * ONE_DAY
|
||||
|
||||
ONE_YEAR = 12 * ONE_MONTH
|
||||
|
||||
# 全局参数配置
|
||||
def edu_setting name
|
||||
EduSetting.get(name)
|
||||
end
|
||||
|
||||
# xss共计问题
|
||||
def content_safe content
|
||||
tags = %w(
|
||||
a abbr b bdo blockquote br caption cite code col colgroup dd del dfn dl
|
||||
dt em figcaption figure h1 h2 h3 h4 h5 h6 hgroup i img ins kbd li mark
|
||||
ol p pre q rp rt ruby s samp small strike strong sub sup table tbody td
|
||||
tfoot th thead time tr u ul var wbr div span
|
||||
)
|
||||
attributes = %w(href src width height alt cite datetime title class name xml:lang abbr style)
|
||||
sanitize content, tags: tags, attributes: attributes
|
||||
end
|
||||
|
||||
def graduation_navigation graduation
|
||||
graduation.class.to_s == "GraduationTopic" ? "毕设选题" : "毕设任务"
|
||||
end
|
||||
|
||||
def graduation_navigation_id course
|
||||
course.course_modules.find_by(module_type: "graduation").try(:id)
|
||||
end
|
||||
|
||||
# git用户
|
||||
# git用户命名规则:login+"@educoder.net"
|
||||
def git_username(email)
|
||||
User.find_by_mail(email) || User.find_by_login(email.split("@").first)
|
||||
end
|
||||
|
||||
# 不同的类型扩展不同的目录
|
||||
def relative_path
|
||||
"avatars"
|
||||
end
|
||||
|
||||
def replace_bytes_to_b(size_string)
|
||||
return size_string.gsub("Bytes", "B").gsub("bytes", "B").gsub("字节", "B")
|
||||
end
|
||||
|
||||
def storage_path
|
||||
File.join(Rails.root, "public", "images", relative_path)
|
||||
end
|
||||
|
||||
# 推荐实训
|
||||
def recommend_shixun(shixun)
|
||||
tag_repertoire_id = shixun.tag_repertoires.first.present? ? shixun.tag_repertoires.first.try(:id) : 0
|
||||
shixun_id = ShixunTagRepertoire.where("tag_repertoire_id = #{tag_repertoire_id} and
|
||||
shixun_id != #{shixun.id}").pluck(:shixun_id)
|
||||
|
||||
shixun_id = shixun_id.blank? ? -1 : shixun_id.join(",")
|
||||
Shixun.select([:id, :name, :user_id, :challenges_count, :myshixuns_count, :trainee, :identifier]).where("id
|
||||
in(#{shixun_id})").unhidden.publiced.order("homepage_show asc, myshixuns_count desc").limit(3)
|
||||
|
||||
end
|
||||
|
||||
# shixun开启挑战对应的行为名及url
|
||||
def task_operation_url current_myshixun, shixun
|
||||
if current_myshixun.blank?
|
||||
name = shixun.status == 0 ? "模拟实战" : "开启挑战"
|
||||
url = "/shixuns/#{shixun.identifier}/shixun_exec"
|
||||
else
|
||||
identifier = current_myshixun.current_task(current_myshixun.games).try(:identifier)
|
||||
if current_myshixun.status == 1
|
||||
name = "查看实战"
|
||||
else
|
||||
name = "继续挑战"
|
||||
end
|
||||
url = identifier
|
||||
end
|
||||
[name, url]
|
||||
end
|
||||
|
||||
# 获取当前时间
|
||||
def time_from_now(time)
|
||||
if String === time
|
||||
time = Time.parse(time)
|
||||
end
|
||||
|
||||
lastUpdateTime = time.to_i*1000
|
||||
|
||||
currentTime = Time.now.to_i*1000
|
||||
timePassed = currentTime - lastUpdateTime
|
||||
timeIntoFormat = 0
|
||||
updateAtValue = ""
|
||||
if timePassed < 0
|
||||
updateAtValue = "刚刚"
|
||||
elsif timePassed < ONE_MINUTE
|
||||
updateAtValue = "1分钟前"
|
||||
elsif timePassed < ONE_HOUR
|
||||
timeIntoFormat = timePassed / ONE_MINUTE
|
||||
updateAtValue = timeIntoFormat.to_s + "分钟前"
|
||||
elsif (timePassed < ONE_DAY)
|
||||
timeIntoFormat = (timePassed.to_f / ONE_HOUR).ceil
|
||||
updateAtValue = timeIntoFormat.to_s + "小时前"
|
||||
elsif (timePassed < ONE_MONTH)
|
||||
timeIntoFormat = (timePassed.to_f / ONE_DAY).ceil
|
||||
updateAtValue = timeIntoFormat.to_s + "天前"
|
||||
elsif (timePassed < ONE_YEAR)
|
||||
timeIntoFormat = (timePassed.to_f / ONE_MONTH).ceil
|
||||
updateAtValue = timeIntoFormat.to_s + "个月前"
|
||||
else
|
||||
timeIntoFormat = timePassed / ONE_YEAR
|
||||
updateAtValue = timeIntoFormat.to_s + "年前"
|
||||
end
|
||||
updateAtValue
|
||||
end
|
||||
|
||||
# 计算到结束还有多长时间 **天**小时**分
|
||||
def how_much_time(time)
|
||||
if time.nil? || time < Time.now #6.21 -hs 增加小于time.now
|
||||
''
|
||||
else
|
||||
result = ((time - Time.now.to_i).to_i / (24*60*60)).to_s + " 天 "
|
||||
result += (((time - Time.now.to_i).to_i % (24*60*60)) / (60*60)).to_s + " 小时 "
|
||||
result + ((((time - Time.now.to_i).to_i % (24*60*60)) % (60*60)) / 60).to_s + " 分 "
|
||||
end
|
||||
end
|
||||
|
||||
def format_time(time)
|
||||
time.present? ? time.strftime("%Y-%m-%d %H:%M") : ''
|
||||
end
|
||||
|
||||
# 用户图像url,如果不存在的话,source为匿名用户,即默认使用匿名用户图像
|
||||
def url_to_avatar(source)
|
||||
if File.exist?(disk_filename(source&.class, source&.id))
|
||||
ctime = File.ctime(disk_filename(source.class, source.id)).to_i
|
||||
if %w(User Organization).include?(source.class.to_s)
|
||||
File.join("images", relative_path, ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
|
||||
else
|
||||
File.join("images/avatars", ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
|
||||
end
|
||||
elsif source.class.to_s == 'User'
|
||||
source.get_letter_avatar_url
|
||||
end
|
||||
end
|
||||
|
||||
def url_to_avatar_with_platform_url(source)
|
||||
platform_url = Rails.application.config_for(:configuration)['platform_url']
|
||||
if platform_url
|
||||
return Rails.application.config_for(:configuration)['platform_url'] + "/" + url_to_avatar(source).to_s
|
||||
else
|
||||
return url_to_avatar(source).to_s
|
||||
end
|
||||
end
|
||||
|
||||
# 主页banner图
|
||||
def banner_img(source_type)
|
||||
if File.exist?(disk_filename(source_type, "banner"))
|
||||
ctime = File.ctime(disk_filename(source_type, "banner")).to_i
|
||||
File.join("images/avatars", ["#{source_type}", "banner"]) + "?t=#{ctime}"
|
||||
end
|
||||
end
|
||||
|
||||
def disk_filename(source_type,source_id,image_file=nil)
|
||||
File.join(storage_path, "#{source_type}", "#{source_id}")
|
||||
end
|
||||
|
||||
def disk_auth_filename(source_type, source_id, type)
|
||||
File.join(storage_path, "#{source_type}", "#{source_id}#{type}")
|
||||
end
|
||||
|
||||
def disk_real_name_auth_filename(source_id)
|
||||
disk_auth_filename('UserAuthentication', source_id, 'ID')
|
||||
end
|
||||
|
||||
def auth_file_url(source_type, source_id, type)
|
||||
File.join('/images', relative_path, source_type, "#{source_id}#{type}")
|
||||
end
|
||||
|
||||
def real_name_auth_file_url(source_id)
|
||||
auth_file_url('UserAuthentication', source_id, 'ID')
|
||||
end
|
||||
|
||||
def disk_professional_auth_filename(source_id)
|
||||
disk_auth_filename('UserAuthentication', source_id, 'PRO')
|
||||
end
|
||||
|
||||
def professional_auth_file_url(source_id)
|
||||
auth_file_url('UserAuthentication', source_id, 'PRO')
|
||||
end
|
||||
|
||||
def shixun_url_to_avatar(shixun)
|
||||
if File.exist?(disk_filename(shixun.class, shixun.id))
|
||||
File.join("images/#{relative_path}", "#{shixun.class}", "#{shixun.id}")
|
||||
else
|
||||
File.join("educoder", "index", "shixun", "shixun#{rand(23)}.jpg")
|
||||
end
|
||||
end
|
||||
|
||||
# 选用实训的学校情况
|
||||
def school_user_detail shixun
|
||||
user_ids = shixun.myshixuns.map(&:user_id).uniq # 走缓存取数据
|
||||
school_ids = UserExtension.where(user_id:user_ids).pluck(:school_id).uniq
|
||||
school_names = School.where(id: school_ids[0..1]).pluck(:name)
|
||||
school_size = school_ids.size
|
||||
str = school_size > 0 ? "#{school_names.join("、")}等 #{school_size}所" : "0所"
|
||||
end
|
||||
|
||||
# 普通/分组 作业作品状态数组
|
||||
def student_work_status homework, user_id, course, work
|
||||
status = []
|
||||
homework_setting = homework.homework_group_setting user_id, true
|
||||
work = work || StudentWork.create(homework_common_id: homework.id, user_id: user_id)
|
||||
late_time = homework.late_time || course.end_date
|
||||
|
||||
if course.is_end && work && work.work_status > 0
|
||||
status << "查看作品"
|
||||
elsif !course.is_end
|
||||
if homework_setting.publish_time && homework_setting.publish_time < Time.now
|
||||
# 作业未截止时
|
||||
if homework_setting.end_time > Time.now
|
||||
if homework.homework_type == "group" && homework.homework_detail_group.base_on_project
|
||||
if work.project_id.nil? || work.project_id == 0
|
||||
status << "创建项目"
|
||||
status << "关联项目"
|
||||
elsif work.work_status == 0
|
||||
status << "取消关联"
|
||||
status << "提交作品"
|
||||
else
|
||||
status << "修改作品"
|
||||
end
|
||||
else
|
||||
if work.work_status == 0
|
||||
status << "提交作品"
|
||||
else
|
||||
status << "修改作品"
|
||||
end
|
||||
end
|
||||
|
||||
# 补交阶段
|
||||
elsif homework.allow_late && (late_time.nil? || late_time > Time.now)
|
||||
if homework.homework_type == "group" && homework.homework_detail_group.base_on_project
|
||||
if work.project_id.nil? || work.project_id == 0
|
||||
status << "创建项目"
|
||||
status << "关联项目"
|
||||
elsif work.work_status == 0
|
||||
status << "取消关联"
|
||||
status << "补交作品"
|
||||
else
|
||||
status << "补交附件"
|
||||
status << "查看作品"
|
||||
end
|
||||
else
|
||||
if work.work_status == 0
|
||||
status << "补交作品"
|
||||
else
|
||||
status << "补交附件"
|
||||
status << "查看作品"
|
||||
end
|
||||
end
|
||||
|
||||
# 匿评阶段
|
||||
elsif work.work_status != 0
|
||||
if homework.homework_detail_manual.comment_status == 3
|
||||
work_ids = homework.student_works.has_committed.pluck(:id)
|
||||
if StudentWorksEvaluationDistribution.where(student_work_id: work_ids, user_id: user_id).size > 0
|
||||
status << "匿评作品"
|
||||
end
|
||||
end
|
||||
status << "查看作品"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def commit_des_status work, homework
|
||||
status = []
|
||||
homework_setting = homework.homework_group_setting work.user_id
|
||||
|
||||
# 作业已发布且作业未截止(补交未截止)且提交了作品才能提交或修改总结
|
||||
if homework_setting.publish_time && homework_setting.publish_time < Time.now && work.work_status > 0 &&
|
||||
((homework_setting.end_time && homework_setting.end_time > Time.now) ||
|
||||
(homework.allow_late && homework.late_time && homework.late_time > Time.now))
|
||||
work.description.present? ? status << "修改总结" : status << "提交总结"
|
||||
end
|
||||
end
|
||||
|
||||
def download_url attachment,options={}
|
||||
attachment_path(attachment,options)
|
||||
end
|
||||
|
||||
# 耗时:天、小时、分、秒
|
||||
# 小于1分钟则不显示
|
||||
def game_spend_time time
|
||||
day = time / 86400
|
||||
hour = time % (24*60*60) / (60*60)
|
||||
min = time % (24*60*60) % (60*60) / 60
|
||||
sec = time % (24*60*60) % (60*60) % 60
|
||||
if day < 1
|
||||
if hour < 1
|
||||
if min < 1
|
||||
if sec < 1
|
||||
time = "--"
|
||||
else
|
||||
time = "#{sec}秒"
|
||||
end
|
||||
else
|
||||
time = "#{min}分钟 #{sec}秒"
|
||||
end
|
||||
else
|
||||
time = "#{hour}小时 #{min}分钟 #{sec}秒"
|
||||
end
|
||||
else
|
||||
time = "#{day}天 #{hour}小时 #{min}分钟 #{sec}秒"
|
||||
end
|
||||
return time
|
||||
end
|
||||
|
||||
def absolute_path(file_path)
|
||||
file_root_directory + File.join(edu_setting('attachment_folder'), file_path)
|
||||
end
|
||||
|
||||
def file_root_directory
|
||||
Rails.root.to_s
|
||||
end
|
||||
|
||||
def file_storage_directory
|
||||
file_root_directory + edu_setting('attachment_folder')
|
||||
end
|
||||
|
||||
def local_path(file)
|
||||
File.join(file.disk_directory, file.disk_filename)
|
||||
end
|
||||
|
||||
def update_downloads(file)
|
||||
file.update_attributes(:downloads => file.downloads + 1)
|
||||
end
|
||||
|
||||
# 项目信息,
|
||||
def project_info work, current_user, course_identity
|
||||
project = work.project
|
||||
if project
|
||||
if project.status == 9
|
||||
{id: -1, name: "#{project.name}(已删除)", title: "该项目已删除", author: project.creator, member_count: project.project_members.count}
|
||||
else
|
||||
project_score = project.project_score
|
||||
if project.is_public || current_user.manager_of_project?(project) || course_identity < Course::STUDENT
|
||||
{id: project.id, name: project.name, author: project.creator, member_count: project.project_members.count,
|
||||
all_score: project_score.all_score, code_score: project_score.code_score, issue_score: project_score.issue_score,
|
||||
attachment_score: project_score.attachment_score, message_score: project_score.message_score}
|
||||
else
|
||||
{id: -1, name: "#{project.name}", title: "该项目是私有的", author: project.creator, member_count: project.project_members.count,
|
||||
all_score: project_score.all_score, code_score: project_score.code_score, issue_score: project_score.issue_score,
|
||||
attachment_score: project_score.attachment_score, message_score: project_score.message_score}
|
||||
end
|
||||
end
|
||||
else
|
||||
{id: -1, name: "--", title: "--"}
|
||||
end
|
||||
end
|
||||
|
||||
def message_content(content)
|
||||
content = (strip_html content).strip
|
||||
content = content.gsub(/\s+/, " ")
|
||||
if content.gsub(" ", "") == ""
|
||||
content = "[非文本消息]"
|
||||
end
|
||||
content
|
||||
end
|
||||
|
||||
def strip_html(text, len=0, endss="...")
|
||||
ss = ""
|
||||
if !text.nil? && text.length>0
|
||||
ss=text.gsub(/<\/?.*?>/, '').strip
|
||||
ss = ss.gsub(/ */, '')
|
||||
ss = ss.gsub(/\r\n/,'') #新增
|
||||
ss = ss.gsub(/\n/,'') #新增
|
||||
if len > 0 && ss.length > len
|
||||
ss = ss[0, len] + endss
|
||||
elsif len > 0 && ss.length <= len
|
||||
ss = ss
|
||||
#ss = truncate(ss, :length => len)
|
||||
end
|
||||
end
|
||||
ss
|
||||
end
|
||||
|
||||
def strip_export_title(content)
|
||||
con_ = ""
|
||||
if content.length > 0
|
||||
con_ = strip_tags(content)
|
||||
con_ = con_.gsub(/\r\n/,'').gsub(/ */, '').strip
|
||||
end
|
||||
con_
|
||||
end
|
||||
|
||||
def pdf_load_sources(*arg)
|
||||
arr = arg.map do |path|
|
||||
content_tag(:script) do
|
||||
File.open(Rails.root.join('public', path)).read.to_s.html_safe
|
||||
end
|
||||
end
|
||||
|
||||
raw arr.join('')
|
||||
end
|
||||
|
||||
|
||||
|
||||
# 导出pdf时,转化markdown为html
|
||||
def to_markdown(text,origin_url)
|
||||
return nil if text.blank?
|
||||
options = {
|
||||
:autolink => true,
|
||||
:no_intra_emphasis => true,
|
||||
:fenced_code_blocks => true,
|
||||
:lax_html_blocks => true,
|
||||
:strikethrough => true,
|
||||
:superscript => false,
|
||||
:tables => true
|
||||
}
|
||||
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, options)
|
||||
m_t = markdown.render(text)
|
||||
m_t&.include?("src=\"") ? m_t&.gsub("src=\"","src=\"#{origin_url}") : m_t
|
||||
end
|
||||
|
||||
def shixun_status_class(shixun)
|
||||
case shixun.status
|
||||
when 0 then 'text-info'
|
||||
when 1 then 'text-warning'
|
||||
when 2 then 'text-success'
|
||||
when 3 then 'text-secondary'
|
||||
end
|
||||
end
|
||||
|
||||
def render_unix_time(date)
|
||||
date.to_time.to_i
|
||||
end
|
||||
|
||||
def find_user_by_login(login)
|
||||
User.find_by_login login
|
||||
end
|
||||
|
||||
def find_user_by_login_and_mail(login, mail)
|
||||
User.find_by(login: login, mail: mail)
|
||||
end
|
||||
|
||||
def find_user_by_gitea_uid(gitea_uid)
|
||||
User.find_by(gitea_uid: gitea_uid)
|
||||
end
|
||||
|
||||
def find_user_in_redis_cache(login, email)
|
||||
$redis_cache.hgetall("v2-owner-common:#{login}-#{email}")
|
||||
end
|
||||
|
||||
def find_user_in_redis_cache_by_id(id)
|
||||
$redis_cache.hgetall("v2-owner-common:#{id}")
|
||||
end
|
||||
|
||||
def render_base64_decoded(str)
|
||||
return nil if str.blank?
|
||||
Base64.decode64 str
|
||||
end
|
||||
|
||||
def render_admin_statistics_item
|
||||
url = Rails.application.config_for(:configuration)["admin_statistics_url"]
|
||||
|
||||
return if url.blank?
|
||||
content_tag(:li) do
|
||||
sidebar_item(url, "数据统计", icon: 'bar-chart', controller: 'root')
|
||||
end
|
||||
end
|
||||
|
||||
# 1 手机类型;0 邮箱类型
|
||||
# 注意新版的login是自动名生成的
|
||||
def phone_mail_type value
|
||||
value =~ /^1\d{10}$/ ? 1 : 0
|
||||
end
|
||||
|
||||
def strip(str)
|
||||
str.to_s.strip.presence
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,169 +1,218 @@
|
|||
module RepositoriesHelper
|
||||
def render_permission(user, project)
|
||||
return "Admin" if user&.admin?
|
||||
project.get_premission(user)
|
||||
end
|
||||
|
||||
def render_decode64_content(str)
|
||||
return nil if str.blank?
|
||||
Base64.decode64(str).force_encoding("UTF-8").encode("UTF-8", invalid: :replace)
|
||||
end
|
||||
|
||||
def download_type(str)
|
||||
default_type = %w(xlsx xls ppt pptx pdf zip 7z rar exe pdb obj idb RData rdata doc docx mpp vsdx dot otf eot ttf woff woff2 mp4 mov wmv flv mpeg avi avchd webm mkv)
|
||||
default_type.include?(str&.downcase) || str.blank?
|
||||
end
|
||||
|
||||
def image_type?(str)
|
||||
default_type = %w(png jpg gif tif psd svg bmp webp jpeg ico psd)
|
||||
default_type.include?(str&.downcase)
|
||||
end
|
||||
|
||||
def is_readme?(type, str)
|
||||
return false if type != 'file' || str.blank?
|
||||
readme_types = ["readme.md", "readme", "readme_en.md", "readme_zh.md", "readme_en", "readme_zh"]
|
||||
readme_types.include?(str.to_s.downcase)
|
||||
end
|
||||
|
||||
def render_commit_author(author_json)
|
||||
return nil if author_json.blank? || (author_json["id"].blank? && author_json['name'].blank?)
|
||||
if author_json["id"].present?
|
||||
return find_user_by_gitea_uid author_json['id']
|
||||
end
|
||||
if author_json["id"].nil? && (author_json["name"].present? && author_json["email"].present?)
|
||||
return find_user_by_login_and_mail(author_json['name'], author_json["email"])
|
||||
end
|
||||
end
|
||||
|
||||
def render_cache_commit_author(author_json)
|
||||
if author_json["name"].present? && author_json["email"].present?
|
||||
return find_user_in_redis_cache(author_json['name'], author_json['email'])
|
||||
end
|
||||
if author_json["Name"].present? && author_json["Email"].present?
|
||||
return find_user_in_redis_cache(author_json['Name'], author_json['Email'])
|
||||
end
|
||||
end
|
||||
|
||||
def readme_render_decode64_content(str, owner, repo, ref, path)
|
||||
return nil if str.blank?
|
||||
begin
|
||||
content = Base64.decode64(str).force_encoding('UTF-8')
|
||||
|
||||
c_regex = /\!\[.*?\]\((.*?)\)/
|
||||
src_regex = /src=\"(.*?)\"/
|
||||
src2_regex = /src='(.*?)'/
|
||||
ss = content.to_s.scan(c_regex)
|
||||
ss_src = content.scan(src_regex)
|
||||
ss_src2 = content.scan(src2_regex)
|
||||
total_images = ss + ss_src + ss_src2
|
||||
if total_images.length > 0
|
||||
total_images.each do |s|
|
||||
begin
|
||||
image_title = /\"(.*?)\"/
|
||||
r_content = s[0]
|
||||
remove_title = r_content.to_s.scan(image_title)
|
||||
# if remove_title.length > 0
|
||||
# r_content = r_content.gsub(/#{remove_title[0]}/, "").strip
|
||||
# end
|
||||
path_last = r_content
|
||||
path_current = ""
|
||||
# 相对路径处理
|
||||
if r_content.start_with?("../")
|
||||
relative_path_length = r_content.split("../").size - 1
|
||||
path_pre = path.split("/").size - 1 - relative_path_length
|
||||
path_pre = 0 if path_pre < 0
|
||||
path_current = path_pre == 0 ? "" : path.split("/")[0..path_pre].join("/")
|
||||
path_last = r_content.split("../").last
|
||||
elsif r_content.start_with?("/") # 根路径处理
|
||||
path_last = r_content[1..r_content.size]
|
||||
else
|
||||
path_current = path
|
||||
end
|
||||
# if r_content.include?("?")
|
||||
# new_r_content = r_content + "&raw=true"
|
||||
# else
|
||||
# new_r_content = r_content + "?raw=true"
|
||||
# end
|
||||
new_r_content = r_content
|
||||
|
||||
unless r_content.include?("http://") || r_content.include?("https://") || r_content.include?("mailto:")
|
||||
# new_r_content = "#{path}" + new_r_content
|
||||
new_r_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw?filepath=#{path_current}/#{path_last}&ref=#{ref}"].join
|
||||
end
|
||||
content = content.gsub(/src=\"#{r_content}\"/, "src=\"#{new_r_content}\"").gsub(/src='#{r_content}'/, "src=\"#{new_r_content}\"")
|
||||
rescue
|
||||
next
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return content
|
||||
rescue
|
||||
return str
|
||||
end
|
||||
end
|
||||
|
||||
# unix_time values for example: 1604382982
|
||||
def render_format_time_with_unix(unix_time)
|
||||
Time.at(unix_time).strftime("%Y-%m-%d %H:%M")
|
||||
end
|
||||
|
||||
# date for example: 2020-11-01T19:57:27+08:00
|
||||
def render_format_time_with_date(date)
|
||||
date.to_time.strftime("%Y-%m-%d %H:%M")
|
||||
end
|
||||
|
||||
def decode64_content(entry, owner, repo, ref, path=nil)
|
||||
if is_readme?(entry['type'], entry['name'])
|
||||
# content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content']
|
||||
content = entry['content']
|
||||
path = URI.escape(entry['path']).to_s.downcase.gsub("/readme.md","")
|
||||
readme_render_decode64_content(content, owner, repo, ref, path)
|
||||
else
|
||||
file_type = File.extname(entry['name'].to_s)[1..-1]
|
||||
if image_type?(file_type)
|
||||
return entry['content'].nil? ? Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content'] : entry['content']
|
||||
end
|
||||
if download_type(file_type)
|
||||
return entry['content']
|
||||
end
|
||||
render_decode64_content(entry['content'])
|
||||
end
|
||||
end
|
||||
|
||||
def base64_to_image(path, content)
|
||||
# generate to https://git.trusite.net/pawm36ozq/-/raw/branch/master/entrn.png"
|
||||
content = Base64.decode64(content)
|
||||
File.open(path, 'wb') { |f| f.write(content) }
|
||||
end
|
||||
|
||||
def render_download_image_url(dir_path, file_path, content)
|
||||
full_path = file_path.starts_with?("/") ? [dir_path, file_path].join("") : [dir_path, file_path].join("/")
|
||||
file_name = full_path.split("/")[-1]
|
||||
# 用户名/项目标识/文件路径
|
||||
dir_path = generate_dir_path(full_path.split("/"+file_name)[0])
|
||||
|
||||
file_path = [dir_path, file_name].join('/')
|
||||
|
||||
puts "##### render_download_image_url file_path: #{file_path}"
|
||||
base64_to_image(file_path, content)
|
||||
file_path = file_path[6..-1]
|
||||
File.join(base_url, file_path)
|
||||
end
|
||||
|
||||
def generate_dir_path(dir_path)
|
||||
# tmp_dir_path
|
||||
# eg: jasder/forgeplus/raw/branch/ref
|
||||
dir_path = ["public", tmp_dir, dir_path].join('/')
|
||||
puts "#### dir_path: #{dir_path}"
|
||||
unless Dir.exists?(dir_path)
|
||||
FileUtils.mkdir_p(dir_path) ##不成功这里会抛异常
|
||||
end
|
||||
dir_path
|
||||
end
|
||||
|
||||
def tmp_dir
|
||||
"repo"
|
||||
end
|
||||
|
||||
end
|
||||
module RepositoriesHelper
|
||||
def render_permission(user, project)
|
||||
return "Admin" if user&.admin?
|
||||
project.get_premission(user)
|
||||
end
|
||||
|
||||
def render_decode64_content(str)
|
||||
return nil if str.blank?
|
||||
Base64.decode64(str).force_encoding("UTF-8").encode("UTF-8", invalid: :replace)
|
||||
end
|
||||
|
||||
def download_type(str)
|
||||
default_type = %w(xlsx xls ppt pptx pdf zip 7z rar exe pdb obj idb RData rdata doc docx mpp vsdx dot otf eot ttf woff woff2 mp4 mov wmv flv mpeg avi avchd webm mkv)
|
||||
default_type.include?(str&.downcase) || str.blank?
|
||||
end
|
||||
|
||||
def image_type?(str)
|
||||
default_type = %w(png jpg gif tif psd svg bmp webp jpeg ico psd)
|
||||
default_type.include?(str&.downcase)
|
||||
end
|
||||
|
||||
def is_readme?(type, str)
|
||||
return false if type != 'file' || str.blank?
|
||||
readme_types = ["readme.md", "readme", "readme_en.md", "readme_zh.md", "readme_en", "readme_zh"]
|
||||
readme_types.include?(str.to_s.downcase) || str =~ CustomRegexp::MD_REGEX
|
||||
end
|
||||
|
||||
def render_commit_author(author_json)
|
||||
return nil if author_json.blank? || (author_json["id"].blank? && author_json['name'].blank?)
|
||||
if author_json["id"].present?
|
||||
return find_user_by_gitea_uid author_json['id']
|
||||
end
|
||||
if author_json["id"].nil? && (author_json["name"].present? && author_json["email"].present?)
|
||||
return find_user_by_login_and_mail(author_json['name'], author_json["email"])
|
||||
end
|
||||
end
|
||||
|
||||
def render_cache_commit_author(author_json)
|
||||
if author_json["name"].present? && author_json["email"].present?
|
||||
return find_user_in_redis_cache(author_json['name'], author_json['email'])
|
||||
end
|
||||
if author_json["Name"].present? && author_json["Email"].present?
|
||||
return find_user_in_redis_cache(author_json['Name'], author_json['Email'])
|
||||
end
|
||||
end
|
||||
|
||||
def readme_render_decode64_content(str, owner, repo, ref, path)
|
||||
return nil if str.blank?
|
||||
begin
|
||||
content = Base64.decode64(str).force_encoding('UTF-8')
|
||||
|
||||
c_regex = /\!\[.*?\]\((.*?)\)/
|
||||
src_regex = /src=\"(.*?)\"/
|
||||
src2_regex = /src='(.*?)'/
|
||||
ss = content.to_s.scan(c_regex)
|
||||
ss_src = content.scan(src_regex)
|
||||
ss_src2 = content.scan(src2_regex)
|
||||
total_images = ss + ss_src + ss_src2
|
||||
if total_images.length > 0
|
||||
total_images.each do |s|
|
||||
begin
|
||||
image_title = /\"(.*?)\"/
|
||||
r_content = s[0]
|
||||
remove_title = r_content.to_s.scan(image_title)
|
||||
# if remove_title.length > 0
|
||||
# r_content = r_content.gsub(/#{remove_title[0]}/, "").strip
|
||||
# end
|
||||
path_last = r_content
|
||||
path_current = ""
|
||||
# 相对路径处理
|
||||
if r_content.start_with?("../")
|
||||
relative_path_length = r_content.split("../").size - 1
|
||||
path_pre = path.split("/").size - 1 - relative_path_length
|
||||
path_pre = 0 if path_pre < 0
|
||||
path_current = path_pre == 0 ? "" : path.split("/")[0..path_pre].join("/")
|
||||
path_last = r_content.split("../").last
|
||||
elsif r_content.start_with?("/") # 根路径处理
|
||||
path_last = r_content[1..r_content.size]
|
||||
else
|
||||
path_current = path
|
||||
end
|
||||
# if r_content.include?("?")
|
||||
# new_r_content = r_content + "&raw=true"
|
||||
# else
|
||||
# new_r_content = r_content + "?raw=true"
|
||||
# end
|
||||
new_r_content = r_content
|
||||
|
||||
unless r_content.include?("http://") || r_content.include?("https://") || r_content.include?("mailto:")
|
||||
# new_r_content = "#{path}" + new_r_content
|
||||
new_r_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw?filepath=#{path_current}/#{path_last}&ref=#{ref}"].join
|
||||
end
|
||||
content = content.gsub(/src=\"#{r_content}\"/, "src=\"#{new_r_content}\"").gsub(/src='#{r_content}'/, "src=\"#{new_r_content}\"")
|
||||
rescue
|
||||
next
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return content
|
||||
rescue
|
||||
return str
|
||||
end
|
||||
end
|
||||
|
||||
# author hui.he
|
||||
def new_readme_render_decode64_content(str, owner, repo, ref, readme_path, readme_name)
|
||||
file_path = readme_path.include?('/') ? readme_path.gsub("/#{readme_name}", '') : readme_path.gsub("#{readme_name}", '')
|
||||
return nil if str.blank?
|
||||
content = Base64.decode64(str).force_encoding('UTF-8')
|
||||
s_regex = /\[.*?\]\((.*?)\)/
|
||||
src_regex = /src=\"(.*?)\"/
|
||||
ss = content.to_s.scan(s_regex)
|
||||
ss_src = content.to_s.scan(src_regex)
|
||||
total_sources = ss + ss_src
|
||||
total_sources.uniq!
|
||||
total_sources.each do |s|
|
||||
begin
|
||||
s_content = s[0]
|
||||
# 链接直接跳过不做替换
|
||||
next if s_content.starts_with?('http://') || s_content.starts_with?('https://') || s_content.starts_with?('mailto:') || s_content.blank?
|
||||
ext = File.extname(s_content)[1..-1]
|
||||
|
||||
if image_type?(ext) || download_type(ext)
|
||||
s_content = File.expand_path(s_content, file_path)
|
||||
s_content = s_content.split("#{Rails.root}/")[1]
|
||||
# content = content.gsub(s[0], "/#{s_content}")
|
||||
s_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw?filepath=#{s_content}&ref=#{ref}"].join
|
||||
content = content.gsub(s[0], s_content)
|
||||
else
|
||||
path = [owner&.login, repo&.identifier, 'tree', ref, file_path].join("/")
|
||||
s_content = File.expand_path(s_content, path)
|
||||
s_content = s_content.split("#{Rails.root}/")[1]
|
||||
content = content.gsub(s[0], "/#{s_content}")
|
||||
end
|
||||
rescue
|
||||
next
|
||||
end
|
||||
end
|
||||
|
||||
return content
|
||||
rescue
|
||||
return str
|
||||
end
|
||||
|
||||
# unix_time values for example: 1604382982
|
||||
def render_format_time_with_unix(unix_time)
|
||||
Time.at(unix_time).strftime("%Y-%m-%d %H:%M")
|
||||
end
|
||||
|
||||
# date for example: 2020-11-01T19:57:27+08:00
|
||||
def render_format_time_with_date(date)
|
||||
date.to_time.strftime("%Y-%m-%d %H:%M")
|
||||
end
|
||||
|
||||
def readme_decode64_content(entry, owner, repo, ref, path=nil)
|
||||
Rails.logger.info("entry===#{entry["type"]} #{entry["name"]}")
|
||||
content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content']
|
||||
Rails.logger.info("content===#{content}")
|
||||
# readme_render_decode64_content(content, owner, repo, ref)
|
||||
new_readme_render_decode64_content(content, owner, repo, ref, entry['path'], entry['name'])
|
||||
end
|
||||
|
||||
def decode64_content(entry, owner, repo, ref, path=nil)
|
||||
if is_readme?(entry['type'], entry['name'])
|
||||
Rails.logger.info("entry===#{entry["type"]} #{entry["name"]}")
|
||||
content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content']
|
||||
Rails.logger.info("content===#{content}")
|
||||
# readme_render_decode64_content(content, owner, repo, ref)
|
||||
return Base64.decode64(content).force_encoding('UTF-8')
|
||||
else
|
||||
file_type = File.extname(entry['name'].to_s)[1..-1]
|
||||
if image_type?(file_type)
|
||||
return entry['content'].nil? ? Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content'] : entry['content']
|
||||
end
|
||||
if download_type(file_type)
|
||||
return entry['content']
|
||||
end
|
||||
render_decode64_content(entry['content'])
|
||||
end
|
||||
end
|
||||
|
||||
def base64_to_image(path, content)
|
||||
# generate to https://git.trusite.net/pawm36ozq/-/raw/branch/master/entrn.png"
|
||||
content = Base64.decode64(content)
|
||||
File.open(path, 'wb') { |f| f.write(content) }
|
||||
end
|
||||
|
||||
def render_download_image_url(dir_path, file_path, content)
|
||||
full_path = file_path.starts_with?("/") ? [dir_path, file_path].join("") : [dir_path, file_path].join("/")
|
||||
file_name = full_path.split("/")[-1]
|
||||
# 用户名/项目标识/文件路径
|
||||
dir_path = generate_dir_path(full_path.split("/"+file_name)[0])
|
||||
|
||||
file_path = [dir_path, file_name].join('/')
|
||||
|
||||
puts "##### render_download_image_url file_path: #{file_path}"
|
||||
base64_to_image(file_path, content)
|
||||
file_path = file_path[6..-1]
|
||||
File.join(base_url, file_path)
|
||||
end
|
||||
|
||||
def generate_dir_path(dir_path)
|
||||
# tmp_dir_path
|
||||
# eg: jasder/forgeplus/raw/branch/ref
|
||||
dir_path = ["public", tmp_dir, dir_path].join('/')
|
||||
puts "#### dir_path: #{dir_path}"
|
||||
unless Dir.exists?(dir_path)
|
||||
FileUtils.mkdir_p(dir_path) ##不成功这里会抛异常
|
||||
end
|
||||
dir_path
|
||||
end
|
||||
|
||||
def tmp_dir
|
||||
"repo"
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
class Admins::NewImportUserFromExcel < BaseImportXlsx
|
||||
UserData = Struct.new(:login, :email, :password, :nickname)
|
||||
|
||||
def read_each(&block)
|
||||
sheet.each_row_streaming(pad_cells: true, offset: 1) do |row|
|
||||
data = row.map(&method(:cell_value))[0..3]
|
||||
block.call UserData.new(*data)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def cell_value(obj)
|
||||
obj&.cell_value
|
||||
end
|
||||
end
|
|
@ -13,7 +13,7 @@ module Gitea
|
|||
end
|
||||
|
||||
def success?
|
||||
@error.nil?
|
||||
@error.nil? && @result[:status].to_s == "success"
|
||||
end
|
||||
|
||||
def result
|
||||
|
|
|
@ -4,6 +4,24 @@ class SendTemplateMessageJob < ApplicationJob
|
|||
def perform(source, *args)
|
||||
Rails.logger.info "SendTemplateMessageJob [args] #{args}"
|
||||
case source
|
||||
when 'CustomTip'
|
||||
receivers_id, template_id, props = args[0], args[1], args[2]
|
||||
template = MessageTemplate.find_by_id(template_id)
|
||||
return unless template.present?
|
||||
receivers = User.where(id: receivers_id).or(User.where(mail: receivers_id))
|
||||
not_exists_receivers = receivers_id - receivers.pluck(:id) - receivers.pluck(:mail)
|
||||
receivers_string, content, notification_url = MessageTemplate::CustomTip.get_message_content(receivers, template, props)
|
||||
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {receivers_id: receivers_id, template_id: template_id, props: props})
|
||||
receivers.find_each do |receiver|
|
||||
receivers_email_string, email_title, email_content = MessageTemplate::CustomTip.get_email_message_content(receiver, template, props)
|
||||
Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
|
||||
end
|
||||
not_exists_receivers.each do |mail|
|
||||
valid_email_regex = /^[a-zA-Z0-9]+([.\-_\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/i
|
||||
next unless (mail =~ valid_email_regex)
|
||||
email_title, email_content = MessageTemplate::CustomTip.get_email_content(template, props)
|
||||
Notice::Write::EmailCreateService.call(mail, email_title, email_content)
|
||||
end
|
||||
when 'FollowTip'
|
||||
watcher_id = args[0]
|
||||
watcher = Watcher.find_by_id(watcher_id)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module CustomRegexp
|
||||
PHONE = /1\d{10}/
|
||||
EMAIL = /\A[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+\z/
|
||||
LOGIN = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾
|
||||
LOGIN = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]{4,15}\z/ #只含有数字、字母、下划线不能以下划线开头和结尾
|
||||
LASTNAME = /\A[a-zA-Z0-9\u4e00-\u9fa5]+\z/
|
||||
NICKNAME = /\A[\u4e00-\u9fa5_a-zA-Z0-9]+\z/
|
||||
PASSWORD = /\A[a-z_A-Z0-9\-\.!@#\$%\\\^&\*\)\(\+=\{\}\[\]\/",'_<>~\·`\?:;|]{8,16}\z/
|
||||
|
@ -11,5 +11,5 @@ module CustomRegexp
|
|||
|
||||
URL_REGEX = /\A(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?\z/i
|
||||
REPOSITORY_NAME_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾
|
||||
|
||||
MD_REGEX = /^.+(\.[m|M][d|D])$/
|
||||
end
|
||||
|
|
|
@ -16,8 +16,7 @@ class MessageTemplate < ApplicationRecord
|
|||
PLATFORM = 'GitLink'
|
||||
|
||||
def self.build_init_data
|
||||
MessageTemplate::IssueAssignerExpire.destroy_all
|
||||
MessageTemplate::IssueCreatorExpire.destroy_all
|
||||
MessageTemplate.where.not(type: 'MessageTemplate::CustomTip').destroy_all
|
||||
self.create(type: 'MessageTemplate::FollowedTip', sys_notice: '<b>{nickname}</b> 关注了你', notification_url: '{baseurl}/{login}')
|
||||
email_html = File.read("#{email_template_html_dir}/issue_assigned.html")
|
||||
self.create(type: 'MessageTemplate::IssueAssigned', sys_notice: '{nickname1}在 <b>{nickname2}/{repository}</b> 指派给你一个疑修:<b>{title}</b>', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}', email: email_html, email_title: "#{PLATFORM}: {nickname1} 在 {nickname2}/{repository} 指派给你一个疑修")
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: message_templates
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# sys_notice :text(65535)
|
||||
# email :text(65535)
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# notification_url :string(255)
|
||||
# email_title :string(255)
|
||||
#
|
||||
|
||||
# 统一模板(
|
||||
|
||||
class MessageTemplate::CustomTip < MessageTemplate
|
||||
|
||||
# MessageTemplate::CustomTip.get_message_content(User.where(login: 'yystopf'), "hahah")
|
||||
def self.get_message_content(receivers, template, props={})
|
||||
return '', '', '' if receivers.blank? || template.blank?
|
||||
content = template.sys_notice
|
||||
notification_url = template.notification_url
|
||||
props.each do |k, v|
|
||||
content.gsub!("{#{k}}", v)
|
||||
notification_url.gsub!("{#{k}}", v)
|
||||
end
|
||||
notification_url.gsub!('{baseurl}', base_url)
|
||||
return receivers_string(receivers), content, notification_url
|
||||
rescue => e
|
||||
Rails.logger.info("MessageTemplate::CustomTip.get_message_content [ERROR] #{e}")
|
||||
return '', '', ''
|
||||
end
|
||||
|
||||
def self.get_email_message_content(receiver, template, props={})
|
||||
return '', '', '' if receiver.blank? || template.blank?
|
||||
title = template.email_title
|
||||
content = template.email
|
||||
props.each do |k, v|
|
||||
title.gsub!("{#{k}}", v)
|
||||
content.gsub!("{#{k}}", v)
|
||||
end
|
||||
content.gsub!('{receiver}', receiver&.real_name)
|
||||
title.gsub!('{platform}', PLATFORM)
|
||||
content.gsub!('{platform}', PLATFORM)
|
||||
content.gsub!('{baseurl}', base_url)
|
||||
|
||||
return receiver&.mail, title, content
|
||||
rescue => e
|
||||
Rails.logger.info("MessageTemplate::CustomTip.get_email_message_content [ERROR] #{e}")
|
||||
return '', '', ''
|
||||
end
|
||||
|
||||
def self.get_email_content(template, props = {})
|
||||
return '', '', '' if template.blank?
|
||||
title = template.email_title
|
||||
content = template.email
|
||||
props.each do |k, v|
|
||||
title.gsub!("{#{k}}", v)
|
||||
content.gsub!("{#{k}}", v)
|
||||
end
|
||||
title.gsub!('{platform}', PLATFORM)
|
||||
content.gsub!('{platform}', PLATFORM)
|
||||
|
||||
return title, content
|
||||
rescue => e
|
||||
Rails.logger.info("MessageTemplate::CustomTip.get_email_content [ERROR] #{e}")
|
||||
return '', ''
|
||||
end
|
||||
end
|
|
@ -141,6 +141,7 @@ class MessageTemplate::ProjectSettingChanged < MessageTemplate
|
|||
navbar.gsub!('devops', '工作流')
|
||||
navbar.gsub!('versions', '里程碑')
|
||||
navbar.gsub!('resources', '资源库')
|
||||
navbar.gsub!('services', '服务')
|
||||
if change_count > 1
|
||||
content.sub!('{ifnavbar}', '<br/>')
|
||||
else
|
||||
|
@ -290,6 +291,7 @@ class MessageTemplate::ProjectSettingChanged < MessageTemplate
|
|||
navbar.gsub!('devops', '工作流')
|
||||
navbar.gsub!('versions', '里程碑')
|
||||
navbar.gsub!('resources', '资源库')
|
||||
navbar.gsub!('services', '服务')
|
||||
if change_count > 1
|
||||
content.sub!('{ifnavbar}', '<br/>')
|
||||
else
|
||||
|
|
|
@ -1,411 +1,420 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: projects
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# name :string(255) default(""), not null
|
||||
# description :text(4294967295)
|
||||
# homepage :string(255) default("")
|
||||
# is_public :boolean default("1"), not null
|
||||
# parent_id :integer
|
||||
# created_on :datetime
|
||||
# updated_on :datetime
|
||||
# identifier :string(255)
|
||||
# status :integer default("1"), not null
|
||||
# lft :integer
|
||||
# rgt :integer
|
||||
# inherit_members :boolean default("0"), not null
|
||||
# project_type :integer default("0")
|
||||
# hidden_repo :boolean default("0"), not null
|
||||
# attachmenttype :integer default("1")
|
||||
# user_id :integer
|
||||
# dts_test :integer default("0")
|
||||
# enterprise_name :string(255)
|
||||
# organization_id :integer
|
||||
# project_new_type :integer
|
||||
# gpid :integer
|
||||
# forked_from_project_id :integer
|
||||
# forked_count :integer default("0")
|
||||
# publish_resource :integer default("0")
|
||||
# visits :integer default("0")
|
||||
# hot :integer default("0")
|
||||
# invite_code :string(255)
|
||||
# qrcode :string(255)
|
||||
# qrcode_expiretime :integer default("0")
|
||||
# script :text(65535)
|
||||
# training_status :integer default("0")
|
||||
# rep_identifier :string(255)
|
||||
# project_category_id :integer
|
||||
# project_language_id :integer
|
||||
# praises_count :integer default("0")
|
||||
# watchers_count :integer default("0")
|
||||
# issues_count :integer default("0")
|
||||
# pull_requests_count :integer default("0")
|
||||
# language :string(255)
|
||||
# versions_count :integer default("0")
|
||||
# issue_tags_count :integer default("0")
|
||||
# closed_issues_count :integer default("0")
|
||||
# open_devops :boolean default("0")
|
||||
# gitea_webhook_id :integer
|
||||
# open_devops_count :integer default("0")
|
||||
# recommend :boolean default("0")
|
||||
# platform :integer default("0")
|
||||
# license_id :integer
|
||||
# ignore_id :integer
|
||||
# default_branch :string(255) default("master")
|
||||
# website :string(255)
|
||||
# lesson_url :string(255)
|
||||
# is_pinned :boolean default("0")
|
||||
# recommend_index :integer default("0")
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_projects_on_forked_from_project_id (forked_from_project_id)
|
||||
# index_projects_on_identifier (identifier)
|
||||
# index_projects_on_invite_code (invite_code)
|
||||
# index_projects_on_is_public (is_public)
|
||||
# index_projects_on_lft (lft)
|
||||
# index_projects_on_license_id (license_id)
|
||||
# index_projects_on_name (name)
|
||||
# index_projects_on_platform (platform)
|
||||
# index_projects_on_project_category_id (project_category_id)
|
||||
# index_projects_on_project_language_id (project_language_id)
|
||||
# index_projects_on_project_type (project_type)
|
||||
# index_projects_on_recommend (recommend)
|
||||
# index_projects_on_rgt (rgt)
|
||||
# index_projects_on_status (status)
|
||||
# index_projects_on_updated_on (updated_on)
|
||||
#
|
||||
|
||||
class Project < ApplicationRecord
|
||||
include Matchable
|
||||
include Publicable
|
||||
include Watchable
|
||||
include ProjectOperable
|
||||
include Dcodes
|
||||
|
||||
# common:开源托管项目
|
||||
# mirror:普通镜像项目,没有定时同步功能
|
||||
# sync_mirror:同步镜像项目,有系统定时同步功能,且用户可手动同步操作
|
||||
#
|
||||
enum project_type: { sync_mirror: 2, mirror: 1, common: 0 }
|
||||
|
||||
# forge: trustie平台项目, educoder: educoder平台项目, 默认为forge平台
|
||||
enum platform: { forge: 0, educoder: 1 }
|
||||
|
||||
belongs_to :ignore, optional: true
|
||||
belongs_to :license, optional: true
|
||||
belongs_to :owner, class_name: 'Owner', foreign_key: :user_id, optional: true
|
||||
belongs_to :organization_extension, foreign_key: :user_id, primary_key: :organization_id, optional: true, counter_cache: :num_projects
|
||||
belongs_to :project_category, optional: true , :counter_cache => true
|
||||
belongs_to :project_language, optional: true , :counter_cache => true
|
||||
belongs_to :forked_from_project, class_name: 'Project', optional: true, foreign_key: :forked_from_project_id
|
||||
has_many :project_trends, dependent: :destroy
|
||||
has_many :watchers, as: :watchable, dependent: :destroy
|
||||
has_many :fork_users, dependent: :destroy
|
||||
has_many :forked_users, class_name: 'ForkUser', foreign_key: :fork_project_id, dependent: :destroy
|
||||
has_many :forked_projects, class_name: 'Project', foreign_key: :forked_from_project_id
|
||||
has_one :project_educoder, dependent: :destroy
|
||||
|
||||
has_one :project_score, dependent: :destroy
|
||||
has_one :repository, dependent: :destroy
|
||||
has_many :pull_requests, dependent: :destroy
|
||||
has_many :issue_tags, -> { order("issue_tags.created_at DESC") }, dependent: :destroy
|
||||
has_many :issues, dependent: :destroy
|
||||
# has_many :user_grades, dependent: :destroy
|
||||
has_many :attachments, as: :container, dependent: :destroy
|
||||
has_one :project_score, dependent: :destroy
|
||||
has_many :versions, -> { order("versions.created_on DESC, versions.name DESC") }, dependent: :destroy
|
||||
has_many :praise_treads, as: :praise_tread_object, dependent: :destroy
|
||||
has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
|
||||
has_one :project_detail, dependent: :destroy
|
||||
has_many :project_units, dependent: :destroy
|
||||
has_one :applied_transfer_project,-> { order created_at: :desc }, dependent: :destroy
|
||||
has_many :pinned_projects, dependent: :destroy
|
||||
has_many :has_pinned_users, through: :pinned_projects, source: :user
|
||||
has_many :webhooks, class_name: "Gitea::Webhook", primary_key: :gpid, foreign_key: :repo_id
|
||||
after_create :incre_user_statistic, :incre_platform_statistic
|
||||
after_save :check_project_members
|
||||
before_save :set_invite_code, :reset_unmember_followed, :set_recommend_and_is_pinned, :reset_cache_data
|
||||
before_destroy :decre_project_common, :decre_forked_from_project_count
|
||||
after_destroy :decre_user_statistic, :decre_platform_statistic
|
||||
scope :project_statics_select, -> {select(:id,:name, :is_public, :identifier, :status, :project_type, :user_id, :forked_count, :description, :visits, :project_category_id, :project_language_id, :license_id, :ignore_id, :watchers_count, :created_on)}
|
||||
scope :no_anomory_projects, -> {where("projects.user_id is not null and projects.user_id != ?", 2)}
|
||||
scope :recommend, -> { visible.project_statics_select.where(recommend: true) }
|
||||
scope :pinned, -> {where(is_pinned: true)}
|
||||
|
||||
delegate :content, to: :project_detail, allow_nil: true
|
||||
delegate :name, to: :license, prefix: true, allow_nil: true
|
||||
|
||||
def self.all_visible(user_id=nil)
|
||||
user_projects_sql = Project.joins(:owner).where(users: {type: 'User'}).to_sql
|
||||
org_public_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension).where(organization_extensions: {visibility: 'common'})).to_sql
|
||||
if user_id.present?
|
||||
org_limit_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension).where(organization_extensions: {visibility: 'limited'})).to_sql
|
||||
org_privacy_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension, :organization_users).where(organization_extensions: {visibility: 'privacy'}, organization_users: {user_id: user_id})).to_sql
|
||||
return Project.from("( #{ user_projects_sql } UNION #{ org_public_projects_sql } UNION #{ org_limit_projects_sql } UNION #{org_privacy_projects_sql} ) AS projects").visible
|
||||
else
|
||||
return Project.from("( #{ user_projects_sql } UNION #{ org_public_projects_sql } ) AS projects").visible
|
||||
end
|
||||
end
|
||||
|
||||
def reset_cache_data
|
||||
CacheAsyncResetJob.set(wait: 5.seconds).perform_later("project_common_service", self.id)
|
||||
if changes[:user_id].present?
|
||||
CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: -1}, changes[:user_id].first)
|
||||
CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: 1}, changes[:user_id].last)
|
||||
end
|
||||
if changes[:project_language_id].present?
|
||||
first_language = ProjectLanguage.find_by_id(changes[:project_language_id].first)
|
||||
last_language = ProjectLanguage.find_by_id(changes[:project_language_id].last)
|
||||
CacheAsyncSetJob.perform_later("user_statistic_service", {project_language_count_key: first_language&.name, project_language_count: -1}, self.user_id)
|
||||
CacheAsyncSetJob.perform_later("user_statistic_service", {project_language_count_key: last_language&.name, project_language_count: 1}, self.user_id)
|
||||
CacheAsyncSetJob.perform_later("platform_statistic_service", {project_language_count_key: first_language&.name, project_language_count: -1})
|
||||
CacheAsyncSetJob.perform_later("platform_statistic_service", {project_language_count_key: last_language&.name, project_language_count: 1})
|
||||
end
|
||||
if changes[:is_public].present?
|
||||
if changes[:is_public][0] && !changes[:is_public][1]
|
||||
CacheAsyncClearJob.perform_later('project_rank_service', self.id)
|
||||
end
|
||||
if !changes[:is_public][0] && changes[:is_public][1]
|
||||
$redis_cache.srem("v2-project-rank-deleted", self.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def decre_project_common
|
||||
CacheAsyncClearJob.perform_later('project_common_service', self.id)
|
||||
end
|
||||
|
||||
def decre_forked_from_project_count
|
||||
forked_project = self.forked_from_project
|
||||
if forked_project.present?
|
||||
forked_project.decrement(:forked_count, 1)
|
||||
forked_project.save
|
||||
end
|
||||
end
|
||||
|
||||
def incre_user_statistic
|
||||
CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: 1, project_language_count_key: self.project_language&.name, project_language_count: 1}, self.user_id)
|
||||
end
|
||||
|
||||
def decre_user_statistic
|
||||
CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: -1, project_language_count_key: self.project_language&.name, project_language_count: -1}, self.user_id)
|
||||
end
|
||||
|
||||
def incre_platform_statistic
|
||||
CacheAsyncSetJob.perform_later("platform_statistic_service", {project_count: 1, project_language_count_key: self.project_language&.name, project_language_count: 1})
|
||||
end
|
||||
|
||||
def decre_platform_statistic
|
||||
CacheAsyncSetJob.perform_later("platform_statistic_service", {project_count: -1, project_language_count_key: self.project_language&.name, project_language_count: -1})
|
||||
end
|
||||
|
||||
def is_full_public
|
||||
owner = self.owner
|
||||
if owner.is_a?(Organization)
|
||||
return self.is_public && owner&.visibility == "common"
|
||||
else
|
||||
return self.is_public
|
||||
end
|
||||
end
|
||||
|
||||
def reset_unmember_followed
|
||||
if changes[:is_public].present? && changes[:is_public] == [true, false]
|
||||
self.watchers.where.not(user_id: self.all_collaborators).destroy_all
|
||||
end
|
||||
end
|
||||
|
||||
def set_invite_code
|
||||
if self.invite_code.nil?
|
||||
self.invite_code= self.generate_dcode('invite_code', 6)
|
||||
end
|
||||
end
|
||||
|
||||
def set_recommend_and_is_pinned
|
||||
self.recommend = self.recommend_index.zero? ? false : true
|
||||
# 私有项目不允许设置精选和推荐
|
||||
unless self.is_public
|
||||
self.recommend = false
|
||||
self.recommend_index = 0
|
||||
self.is_pinned = false
|
||||
end
|
||||
end
|
||||
|
||||
def self.search_project(search)
|
||||
ransack(name_or_identifier_cont: search)
|
||||
end
|
||||
# 创建者
|
||||
def creator
|
||||
User.find(user_id).full_name
|
||||
end
|
||||
|
||||
def members_user_infos
|
||||
members.joins(:roles).where("roles.name in ('Manager', 'Developer', 'Reporter')").joins("left join users on members.user_id = users.id ").includes(:user).where("users.type = ?", "User")
|
||||
# members.joins("left join users on members.user_id = users.id").select("users.id", "users.login","users.firstname","users.lastname")
|
||||
# .pluck("users.id", "users.login","users.lastname", "users.firstname")
|
||||
end
|
||||
|
||||
def to_param
|
||||
self.identifier.parameterize
|
||||
end
|
||||
|
||||
def get_issues_count(status_id)
|
||||
if status_id.present?
|
||||
self&.issues.issue_issue.select(:id, :status_id).where(status_id: status_id)&.pluck(:id).size
|
||||
else
|
||||
self&.issues.issue_issue.select(:id)&.pluck(:id).size
|
||||
end
|
||||
end
|
||||
|
||||
def get_pull_requests_count(status_id)
|
||||
if status_id.present?
|
||||
self&.pull_requests.select(:id, :status).where(status: status_id)&.pluck(:id).size
|
||||
else
|
||||
self&.pull_requests.select(:id)&.pluck(:id).size
|
||||
end
|
||||
end
|
||||
|
||||
#创建项目管理员
|
||||
def check_project_members
|
||||
return if owner.is_a?(Organization)
|
||||
unless members.present? && members.exists?(user_id: self.user_id)
|
||||
member_params = {
|
||||
user_id: self.user_id,
|
||||
project_id: self.id
|
||||
}
|
||||
user_member = Member.new(member_params)
|
||||
if user_member.save
|
||||
role_id = Role.select(:id,:position).where(position: 3)&.first&.id
|
||||
MemberRole.create!(member_id: user_member.id ,role_id: role_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def self.init_bluck_repository
|
||||
Project.includes(:repository).find_each do |project|
|
||||
puts project.id
|
||||
next if project.owner.blank?
|
||||
if project.repository.blank?
|
||||
puts "########### start create repositoy #############"
|
||||
Repository.create!(project_id: project.id, identifier: Project.generate_identifier, user_id: project&.owner&.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.generate_identifier
|
||||
str_arr = (("a".."z").to_a + ("A".."Z").to_a)
|
||||
|
||||
str = str_arr.shuffle[0..8].join
|
||||
while Repository.exists?(identifier: str)
|
||||
str = str_arr.shuffle[0..8].join
|
||||
end
|
||||
str
|
||||
end
|
||||
|
||||
def self.list_user_projects(user_id)
|
||||
projects = Project.is_private.select(:id,:user_id)
|
||||
user_not_show_1 = projects.where("user_id != ?",user_id).pluck(:id).uniq
|
||||
|
||||
user_show_2 = projects.joins(:members).where("members.user_id = ?", user_id).pluck(:id).uniq
|
||||
Project.where.not(id: (user_not_show_1 - user_show_2).uniq)
|
||||
end
|
||||
|
||||
def members_count
|
||||
members.select(:id).size
|
||||
end
|
||||
|
||||
|
||||
def can_visited?
|
||||
is_public? || User.current.admin? || member?(User.current)
|
||||
end
|
||||
|
||||
def releases_size(current_user_id, type)
|
||||
if current_user_id == self.user_id && type.to_s == "all"
|
||||
self.repository.version_releases_count
|
||||
else
|
||||
self.repository.version_releases.releases_size
|
||||
end
|
||||
end
|
||||
|
||||
def contributor_users
|
||||
self.pull_requests.select(:user_id).pluck(:user_id).uniq.size
|
||||
end
|
||||
|
||||
def open_issues_count
|
||||
issues_count - closed_issues_count
|
||||
end
|
||||
|
||||
def numerical_for_project_type
|
||||
self.class.name.constantize.project_types["#{self.project_type}"]
|
||||
end
|
||||
|
||||
def watched_by? user
|
||||
watchers.pluck(:user_id).include? user&.id
|
||||
end
|
||||
|
||||
def praised_by? user
|
||||
praise_treads.pluck(:user_id).include? user&.id
|
||||
end
|
||||
|
||||
def get_premission user
|
||||
return "Owner" if owner?(user)
|
||||
return "Manager" if manager?(user)
|
||||
return "Developer" if develper?(user)
|
||||
return "Reporter" if reporter?(user)
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
def fork_project
|
||||
Project.find_by(id: self.forked_from_project_id)
|
||||
end
|
||||
|
||||
def self.members_projects(member_user_id)
|
||||
joins(:members).where(members: { user_id: member_user_id})
|
||||
end
|
||||
|
||||
def self.find_with_namespace(namespace_path, identifier)
|
||||
logger.info "########namespace_path: #{namespace_path} ########identifier: #{identifier} "
|
||||
|
||||
user = Owner.find_by_login namespace_path
|
||||
project = user&.projects&.find_by(identifier: identifier) || Project.find_by(identifier: "#{namespace_path}/#{identifier}")
|
||||
return nil if project.blank?
|
||||
|
||||
[project, user]
|
||||
end
|
||||
|
||||
def ci_reactivate?
|
||||
open_devops_count > 0
|
||||
end
|
||||
|
||||
def ci_reactivate!(ci_repo)
|
||||
ci_repo.update_column(:repo_active, 1)
|
||||
update_column(:open_devops, true)
|
||||
increment!(:open_devops_count)
|
||||
end
|
||||
|
||||
def self.sync_educoder_shixun(url, private_token, page, per_page)
|
||||
SyncEducoderShixunJob.perform_later(url, private_token, page, per_page)
|
||||
end
|
||||
|
||||
def self.update_common_projects_count!
|
||||
ps = ProjectStatistic.first
|
||||
ps.increment!(:common_projects_count) unless ps.blank?
|
||||
end
|
||||
|
||||
def self.update_mirror_projects_count!
|
||||
ps = ProjectStatistic.first
|
||||
ps.increment!(:mirror_projects_count) unless ps.blank?
|
||||
end
|
||||
|
||||
def set_updated_on(time)
|
||||
return if time.blank?
|
||||
update_column(:updated_on, time)
|
||||
end
|
||||
|
||||
def is_transfering
|
||||
applied_transfer_project&.common? ? true : false
|
||||
end
|
||||
end
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: projects
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# name :string(255) default(""), not null
|
||||
# description :text(4294967295)
|
||||
# homepage :string(255) default("")
|
||||
# is_public :boolean default("1"), not null
|
||||
# parent_id :integer
|
||||
# created_on :datetime
|
||||
# updated_on :datetime
|
||||
# identifier :string(255)
|
||||
# status :integer default("1"), not null
|
||||
# lft :integer
|
||||
# rgt :integer
|
||||
# inherit_members :boolean default("0"), not null
|
||||
# project_type :integer default("0")
|
||||
# hidden_repo :boolean default("0"), not null
|
||||
# attachmenttype :integer default("1")
|
||||
# user_id :integer
|
||||
# dts_test :integer default("0")
|
||||
# enterprise_name :string(255)
|
||||
# organization_id :integer
|
||||
# project_new_type :integer
|
||||
# gpid :integer
|
||||
# forked_from_project_id :integer
|
||||
# forked_count :integer default("0")
|
||||
# publish_resource :integer default("0")
|
||||
# visits :integer default("0")
|
||||
# hot :integer default("0")
|
||||
# invite_code :string(255)
|
||||
# qrcode :string(255)
|
||||
# qrcode_expiretime :integer default("0")
|
||||
# script :text(65535)
|
||||
# training_status :integer default("0")
|
||||
# rep_identifier :string(255)
|
||||
# project_category_id :integer
|
||||
# project_language_id :integer
|
||||
# license_id :integer
|
||||
# ignore_id :integer
|
||||
# praises_count :integer default("0")
|
||||
# watchers_count :integer default("0")
|
||||
# issues_count :integer default("0")
|
||||
# pull_requests_count :integer default("0")
|
||||
# language :string(255)
|
||||
# versions_count :integer default("0")
|
||||
# issue_tags_count :integer default("0")
|
||||
# closed_issues_count :integer default("0")
|
||||
# open_devops :boolean default("0")
|
||||
# gitea_webhook_id :integer
|
||||
# open_devops_count :integer default("0")
|
||||
# recommend :boolean default("0")
|
||||
# platform :integer default("0")
|
||||
# default_branch :string(255) default("master")
|
||||
# website :string(255)
|
||||
# lesson_url :string(255)
|
||||
# is_pinned :boolean default("0")
|
||||
# recommend_index :integer default("0")
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_projects_on_forked_from_project_id (forked_from_project_id)
|
||||
# index_projects_on_identifier (identifier)
|
||||
# index_projects_on_invite_code (invite_code)
|
||||
# index_projects_on_is_public (is_public)
|
||||
# index_projects_on_lft (lft)
|
||||
# index_projects_on_license_id (license_id)
|
||||
# index_projects_on_name (name)
|
||||
# index_projects_on_platform (platform)
|
||||
# index_projects_on_project_category_id (project_category_id)
|
||||
# index_projects_on_project_language_id (project_language_id)
|
||||
# index_projects_on_project_type (project_type)
|
||||
# index_projects_on_recommend (recommend)
|
||||
# index_projects_on_rgt (rgt)
|
||||
# index_projects_on_status (status)
|
||||
# index_projects_on_updated_on (updated_on)
|
||||
#
|
||||
|
||||
class Project < ApplicationRecord
|
||||
include Matchable
|
||||
include Publicable
|
||||
include Watchable
|
||||
include ProjectOperable
|
||||
include Dcodes
|
||||
|
||||
# common:开源托管项目
|
||||
# mirror:普通镜像项目,没有定时同步功能
|
||||
# sync_mirror:同步镜像项目,有系统定时同步功能,且用户可手动同步操作
|
||||
#
|
||||
enum project_type: { sync_mirror: 2, mirror: 1, common: 0 }
|
||||
|
||||
# forge: trustie平台项目, educoder: educoder平台项目, 默认为forge平台
|
||||
enum platform: { forge: 0, educoder: 1 }
|
||||
|
||||
belongs_to :ignore, optional: true
|
||||
belongs_to :license, optional: true
|
||||
belongs_to :owner, class_name: 'Owner', foreign_key: :user_id, optional: true
|
||||
belongs_to :organization_extension, foreign_key: :user_id, primary_key: :organization_id, optional: true, counter_cache: :num_projects
|
||||
belongs_to :project_category, optional: true , :counter_cache => true
|
||||
belongs_to :project_language, optional: true , :counter_cache => true
|
||||
belongs_to :forked_from_project, class_name: 'Project', optional: true, foreign_key: :forked_from_project_id
|
||||
has_many :project_trends, dependent: :destroy
|
||||
has_many :watchers, as: :watchable, dependent: :destroy
|
||||
has_many :fork_users, dependent: :destroy
|
||||
has_many :forked_users, class_name: 'ForkUser', foreign_key: :fork_project_id, dependent: :destroy
|
||||
has_many :forked_projects, class_name: 'Project', foreign_key: :forked_from_project_id
|
||||
has_one :project_educoder, dependent: :destroy
|
||||
|
||||
has_one :project_score, dependent: :destroy
|
||||
has_one :repository, dependent: :destroy
|
||||
has_many :pull_requests, dependent: :destroy
|
||||
has_many :issue_tags, -> { order("issue_tags.created_at DESC") }, dependent: :destroy
|
||||
has_many :issues, dependent: :destroy
|
||||
# has_many :user_grades, dependent: :destroy
|
||||
has_many :attachments, as: :container, dependent: :destroy
|
||||
has_one :project_score, dependent: :destroy
|
||||
has_many :versions, -> { order("versions.created_on DESC, versions.name DESC") }, dependent: :destroy
|
||||
has_many :praise_treads, as: :praise_tread_object, dependent: :destroy
|
||||
has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
|
||||
has_one :project_detail, dependent: :destroy
|
||||
has_many :project_units, dependent: :destroy
|
||||
has_one :applied_transfer_project,-> { order created_at: :desc }, dependent: :destroy
|
||||
has_many :pinned_projects, dependent: :destroy
|
||||
has_many :has_pinned_users, through: :pinned_projects, source: :user
|
||||
has_many :webhooks, class_name: "Gitea::Webhook", primary_key: :gpid, foreign_key: :repo_id
|
||||
has_many :user_trace_tasks, dependent: :destroy
|
||||
after_create :incre_user_statistic, :incre_platform_statistic
|
||||
after_save :check_project_members
|
||||
before_save :set_invite_code, :reset_unmember_followed, :set_recommend_and_is_pinned, :reset_cache_data
|
||||
before_destroy :decre_project_common, :decre_forked_from_project_count
|
||||
after_destroy :decre_user_statistic, :decre_platform_statistic
|
||||
scope :project_statics_select, -> {select(:id,:name, :is_public, :identifier, :status, :project_type, :user_id, :forked_count, :description, :visits, :project_category_id, :project_language_id, :license_id, :ignore_id, :watchers_count, :created_on)}
|
||||
scope :no_anomory_projects, -> {where("projects.user_id is not null and projects.user_id != ?", 2)}
|
||||
scope :recommend, -> { visible.project_statics_select.where(recommend: true) }
|
||||
scope :pinned, -> {where(is_pinned: true)}
|
||||
|
||||
delegate :content, to: :project_detail, allow_nil: true
|
||||
delegate :name, to: :license, prefix: true, allow_nil: true
|
||||
|
||||
validate :validate_sensitive_string
|
||||
|
||||
def self.all_visible(user_id=nil)
|
||||
user_projects_sql = Project.joins(:owner).where(users: {type: 'User'}).to_sql
|
||||
org_public_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension).where(organization_extensions: {visibility: 'common'})).to_sql
|
||||
if user_id.present?
|
||||
org_limit_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension).where(organization_extensions: {visibility: 'limited'})).to_sql
|
||||
org_privacy_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension, :organization_users).where(organization_extensions: {visibility: 'privacy'}, organization_users: {user_id: user_id})).to_sql
|
||||
return Project.from("( #{ user_projects_sql } UNION #{ org_public_projects_sql } UNION #{ org_limit_projects_sql } UNION #{org_privacy_projects_sql} ) AS projects").visible
|
||||
else
|
||||
return Project.from("( #{ user_projects_sql } UNION #{ org_public_projects_sql } ) AS projects").visible
|
||||
end
|
||||
end
|
||||
|
||||
def reset_cache_data
|
||||
CacheAsyncResetJob.set(wait: 5.seconds).perform_later("project_common_service", self.id)
|
||||
if changes[:user_id].present?
|
||||
CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: -1}, changes[:user_id].first)
|
||||
CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: 1}, changes[:user_id].last)
|
||||
end
|
||||
if changes[:project_language_id].present?
|
||||
first_language = ProjectLanguage.find_by_id(changes[:project_language_id].first)
|
||||
last_language = ProjectLanguage.find_by_id(changes[:project_language_id].last)
|
||||
CacheAsyncSetJob.perform_later("user_statistic_service", {project_language_count_key: first_language&.name, project_language_count: -1}, self.user_id)
|
||||
CacheAsyncSetJob.perform_later("user_statistic_service", {project_language_count_key: last_language&.name, project_language_count: 1}, self.user_id)
|
||||
CacheAsyncSetJob.perform_later("platform_statistic_service", {project_language_count_key: first_language&.name, project_language_count: -1})
|
||||
CacheAsyncSetJob.perform_later("platform_statistic_service", {project_language_count_key: last_language&.name, project_language_count: 1})
|
||||
end
|
||||
if changes[:is_public].present?
|
||||
if changes[:is_public][0] && !changes[:is_public][1]
|
||||
CacheAsyncClearJob.perform_later('project_rank_service', self.id)
|
||||
end
|
||||
if !changes[:is_public][0] && changes[:is_public][1]
|
||||
$redis_cache.srem("v2-project-rank-deleted", self.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def decre_project_common
|
||||
CacheAsyncClearJob.perform_later('project_common_service', self.id)
|
||||
end
|
||||
|
||||
def decre_forked_from_project_count
|
||||
forked_project = self.forked_from_project
|
||||
if forked_project.present?
|
||||
forked_project.decrement(:forked_count, 1)
|
||||
forked_project.save
|
||||
end
|
||||
end
|
||||
|
||||
def incre_user_statistic
|
||||
CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: 1, project_language_count_key: self.project_language&.name, project_language_count: 1}, self.user_id)
|
||||
end
|
||||
|
||||
def decre_user_statistic
|
||||
CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: -1, project_language_count_key: self.project_language&.name, project_language_count: -1}, self.user_id)
|
||||
end
|
||||
|
||||
def incre_platform_statistic
|
||||
CacheAsyncSetJob.perform_later("platform_statistic_service", {project_count: 1, project_language_count_key: self.project_language&.name, project_language_count: 1})
|
||||
end
|
||||
|
||||
def decre_platform_statistic
|
||||
CacheAsyncSetJob.perform_later("platform_statistic_service", {project_count: -1, project_language_count_key: self.project_language&.name, project_language_count: -1})
|
||||
end
|
||||
|
||||
def is_full_public
|
||||
owner = self.owner
|
||||
if owner.is_a?(Organization)
|
||||
return self.is_public && owner&.visibility == "common"
|
||||
else
|
||||
return self.is_public
|
||||
end
|
||||
end
|
||||
|
||||
def reset_unmember_followed
|
||||
if changes[:is_public].present? && changes[:is_public] == [true, false]
|
||||
self.watchers.where.not(user_id: self.all_collaborators).destroy_all
|
||||
end
|
||||
end
|
||||
|
||||
def set_invite_code
|
||||
if self.invite_code.nil?
|
||||
self.invite_code= self.generate_dcode('invite_code', 6)
|
||||
end
|
||||
end
|
||||
|
||||
def set_recommend_and_is_pinned
|
||||
self.recommend = self.recommend_index.zero? ? false : true
|
||||
# 私有项目不允许设置精选和推荐
|
||||
unless self.is_public
|
||||
self.recommend = false
|
||||
self.recommend_index = 0
|
||||
self.is_pinned = false
|
||||
end
|
||||
end
|
||||
|
||||
def self.search_project(search)
|
||||
ransack(name_or_identifier_cont: search)
|
||||
end
|
||||
# 创建者
|
||||
def creator
|
||||
User.find(user_id).full_name
|
||||
end
|
||||
|
||||
def members_user_infos
|
||||
members.joins(:roles).where("roles.name in ('Manager', 'Developer', 'Reporter')").joins("left join users on members.user_id = users.id ").includes(:user).where("users.type = ?", "User")
|
||||
# members.joins("left join users on members.user_id = users.id").select("users.id", "users.login","users.firstname","users.lastname")
|
||||
# .pluck("users.id", "users.login","users.lastname", "users.firstname")
|
||||
end
|
||||
|
||||
def to_param
|
||||
self.identifier.parameterize
|
||||
end
|
||||
|
||||
def get_issues_count(status_id)
|
||||
if status_id.present?
|
||||
self&.issues.issue_issue.select(:id, :status_id).where(status_id: status_id)&.pluck(:id).size
|
||||
else
|
||||
self&.issues.issue_issue.select(:id)&.pluck(:id).size
|
||||
end
|
||||
end
|
||||
|
||||
def get_pull_requests_count(status_id)
|
||||
if status_id.present?
|
||||
self&.pull_requests.select(:id, :status).where(status: status_id)&.pluck(:id).size
|
||||
else
|
||||
self&.pull_requests.select(:id)&.pluck(:id).size
|
||||
end
|
||||
end
|
||||
|
||||
#创建项目管理员
|
||||
def check_project_members
|
||||
return if owner.is_a?(Organization)
|
||||
unless members.present? && members.exists?(user_id: self.user_id)
|
||||
member_params = {
|
||||
user_id: self.user_id,
|
||||
project_id: self.id
|
||||
}
|
||||
user_member = Member.new(member_params)
|
||||
if user_member.save
|
||||
role_id = Role.select(:id,:position).where(position: 3)&.first&.id
|
||||
MemberRole.create!(member_id: user_member.id ,role_id: role_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def self.init_bluck_repository
|
||||
Project.includes(:repository).find_each do |project|
|
||||
puts project.id
|
||||
next if project.owner.blank?
|
||||
if project.repository.blank?
|
||||
puts "########### start create repositoy #############"
|
||||
Repository.create!(project_id: project.id, identifier: Project.generate_identifier, user_id: project&.owner&.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.generate_identifier
|
||||
str_arr = (("a".."z").to_a + ("A".."Z").to_a)
|
||||
|
||||
str = str_arr.shuffle[0..8].join
|
||||
while Repository.exists?(identifier: str)
|
||||
str = str_arr.shuffle[0..8].join
|
||||
end
|
||||
str
|
||||
end
|
||||
|
||||
def self.list_user_projects(user_id)
|
||||
projects = Project.is_private.select(:id,:user_id)
|
||||
user_not_show_1 = projects.where("user_id != ?",user_id).pluck(:id).uniq
|
||||
|
||||
user_show_2 = projects.joins(:members).where("members.user_id = ?", user_id).pluck(:id).uniq
|
||||
Project.where.not(id: (user_not_show_1 - user_show_2).uniq)
|
||||
end
|
||||
|
||||
def members_count
|
||||
members.select(:id).size
|
||||
end
|
||||
|
||||
|
||||
def can_visited?
|
||||
is_public? || User.current.admin? || member?(User.current)
|
||||
end
|
||||
|
||||
def releases_size(current_user_id, type)
|
||||
if current_user_id == self.user_id && type.to_s == "all"
|
||||
self.repository.version_releases_count
|
||||
else
|
||||
self.repository.version_releases.releases_size
|
||||
end
|
||||
end
|
||||
|
||||
def contributor_users
|
||||
self.pull_requests.select(:user_id).pluck(:user_id).uniq.size
|
||||
end
|
||||
|
||||
def open_issues_count
|
||||
issues_count - closed_issues_count
|
||||
end
|
||||
|
||||
def numerical_for_project_type
|
||||
self.class.name.constantize.project_types["#{self.project_type}"]
|
||||
end
|
||||
|
||||
def watched_by? user
|
||||
watchers.pluck(:user_id).include? user&.id
|
||||
end
|
||||
|
||||
def praised_by? user
|
||||
praise_treads.pluck(:user_id).include? user&.id
|
||||
end
|
||||
|
||||
def get_premission user
|
||||
return "Owner" if owner?(user)
|
||||
return "Manager" if manager?(user)
|
||||
return "Developer" if develper?(user)
|
||||
return "Reporter" if reporter?(user)
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
def fork_project
|
||||
Project.find_by(id: self.forked_from_project_id)
|
||||
end
|
||||
|
||||
def self.members_projects(member_user_id)
|
||||
joins(:members).where(members: { user_id: member_user_id})
|
||||
end
|
||||
|
||||
def self.find_with_namespace(namespace_path, identifier)
|
||||
logger.info "########namespace_path: #{namespace_path} ########identifier: #{identifier} "
|
||||
|
||||
user = Owner.find_by_login namespace_path
|
||||
user = Owner.new(login: namespace_path) if user.nil?
|
||||
project = user&.projects&.find_by(identifier: identifier) || Project.find_by(identifier: "#{namespace_path}/#{identifier}")
|
||||
return nil if project.blank?
|
||||
|
||||
[project, user]
|
||||
end
|
||||
|
||||
def ci_reactivate?
|
||||
open_devops_count > 0
|
||||
end
|
||||
|
||||
def ci_reactivate!(ci_repo)
|
||||
ci_repo.update_column(:repo_active, 1)
|
||||
update_column(:open_devops, true)
|
||||
increment!(:open_devops_count)
|
||||
end
|
||||
|
||||
def self.sync_educoder_shixun(url, private_token, page, per_page)
|
||||
SyncEducoderShixunJob.perform_later(url, private_token, page, per_page)
|
||||
end
|
||||
|
||||
def self.update_common_projects_count!
|
||||
ps = ProjectStatistic.first
|
||||
ps.increment!(:common_projects_count) unless ps.blank?
|
||||
end
|
||||
|
||||
def self.update_mirror_projects_count!
|
||||
ps = ProjectStatistic.first
|
||||
ps.increment!(:mirror_projects_count) unless ps.blank?
|
||||
end
|
||||
|
||||
def set_updated_on(time)
|
||||
return if time.blank?
|
||||
update_column(:updated_on, time)
|
||||
end
|
||||
|
||||
def is_transfering
|
||||
applied_transfer_project&.common? ? true : false
|
||||
end
|
||||
|
||||
def validate_sensitive_string
|
||||
raise("项目名称包含敏感词汇,请重新输入") if name && !HarmoniousDictionary.clean?(name)
|
||||
raise("项目描述包含敏感词汇,请重新输入") if description && !HarmoniousDictionary.clean?(description)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
class ProjectUnit < ApplicationRecord
|
||||
belongs_to :project
|
||||
|
||||
enum unit_type: {code: 1, issues: 2, pulls: 3, wiki:4, devops: 5, versions: 6, resources: 7}
|
||||
enum unit_type: {code: 1, issues: 2, pulls: 3, wiki:4, devops: 5, versions: 6, resources: 7, services: 8}
|
||||
|
||||
validates :unit_type, uniqueness: { scope: :project_id}
|
||||
|
||||
|
|
|
@ -48,6 +48,10 @@ class Repository < ApplicationRecord
|
|||
self.identifier.parameterize
|
||||
end
|
||||
|
||||
def url
|
||||
self['url'].blank? ? "#{Rails.application.config_for(:configuration)['platform_url']}/#{self.owner&.login}/#{self.identifier}.git" : self['url']
|
||||
end
|
||||
|
||||
# with repository is mirror
|
||||
def set_mirror!
|
||||
self.build_mirror(status: Mirror.statuses[:waiting]).save
|
||||
|
|
|
@ -175,6 +175,7 @@ class User < Owner
|
|||
has_many :system_notification_histories
|
||||
has_many :system_notifications, through: :system_notification_histories
|
||||
has_one :trace_user, dependent: :destroy
|
||||
has_many :user_trace_tasks, dependent: :destroy
|
||||
|
||||
# Groups and active users
|
||||
scope :active, lambda { where(status: [STATUS_ACTIVE, STATUS_EDIT_INFO]) }
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: user_trace_tasks
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# user_id :integer
|
||||
# project_id :integer
|
||||
# branch_tag :string(255)
|
||||
# task_id :string(255)
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_user_trace_tasks_on_project_id (project_id)
|
||||
# index_user_trace_tasks_on_user_id (user_id)
|
||||
#
|
||||
|
||||
class UserTraceTask < ApplicationRecord
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :project
|
||||
|
||||
|
||||
end
|
|
@ -0,0 +1,71 @@
|
|||
class Admins::ImportUserFromExcelService < ApplicationService
|
||||
Error = Class.new(StandardError)
|
||||
|
||||
attr_reader :file, :result
|
||||
|
||||
def initialize(file)
|
||||
@file = file
|
||||
@result = { success: 0, fail: [] }
|
||||
end
|
||||
|
||||
def call
|
||||
raise Error, '文件不存在' if file.blank?
|
||||
excel = Admins::NewImportUserFromExcel.new(file)
|
||||
|
||||
excel.read_each(&method(:save_user))
|
||||
result
|
||||
rescue ApplicationImport::Error => ex
|
||||
raise Error, ex.message
|
||||
end
|
||||
|
||||
private
|
||||
def save_user(data)
|
||||
user = find_user(data)
|
||||
if user.blank?
|
||||
create_user(data)
|
||||
result[:success] +=1
|
||||
else
|
||||
fail_data = data.as_json
|
||||
fail_data[:data] = fail_data.values.join(",")
|
||||
fail_data[:message] = '用户已存在'
|
||||
result[:fail] << fail_data
|
||||
end
|
||||
|
||||
rescue Exception => ex
|
||||
fail_data = data.as_json
|
||||
fail_data[:data] = fail_data.values.join(",")
|
||||
fail_data[:message] = ex.message
|
||||
result[:fail] << fail_data
|
||||
end
|
||||
|
||||
def create_user(data)
|
||||
ActiveRecord::Base.transaction do
|
||||
username = data.login&.gsub(/\s+/, "")
|
||||
email = data.email&.gsub(/\s+/, "")
|
||||
password = data.password
|
||||
nickname = data.nickname&.gsub(/\s+/, "")
|
||||
raise Error, "无法使用以下关键词:#{username},请重新命名" if ReversedKeyword.check_exists?(data.login)
|
||||
Register::RemoteForm.new({username: username, email: email, password: password, platform: 'forge'}).validate!
|
||||
user = User.new(admin: false, login: username, mail: email, nickname: nickname, platform: 'forge' , type: "User")
|
||||
user.password = password
|
||||
user.activate
|
||||
raise Error, user.errors.full_messages.join(",") unless user.valid?
|
||||
interactor = Gitea::RegisterInteractor.call({username: username, email: email, password: password})
|
||||
if interactor.success?
|
||||
gitea_user = interactor.result
|
||||
result = Gitea::User::GenerateTokenService.call(username, password)
|
||||
user.gitea_token = result['sha1']
|
||||
user.gitea_uid = gitea_user[:body]['id']
|
||||
UserExtension.create!(user_id: user.id) if user.save!
|
||||
else
|
||||
raise interactor.error, 'gitea user create error'
|
||||
end
|
||||
|
||||
user
|
||||
end
|
||||
end
|
||||
|
||||
def find_user(data)
|
||||
User.find_by(login: data.login)
|
||||
end
|
||||
end
|
|
@ -24,7 +24,8 @@ class Projects::TransferService < ApplicationService
|
|||
|
||||
private
|
||||
def update_owner
|
||||
project.members.map{|m| m.destroy! if m.user_id == owner.id || (new_owner.is_a?(Organization) && new_owner.is_member?(m.user_id)) }
|
||||
project.members.map{|m| m.destroy! if m.user_id == owner.id || project.member(new_owner.id) || (new_owner.is_a?(Organization) && new_owner.is_member?(m.user_id)) }
|
||||
project.set_owner_permission(new_owner)
|
||||
project.update!(user_id: new_owner.id)
|
||||
end
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# 代码溯源 查询检测结果
|
||||
class Trace::CheckResultService < Trace::ClientService
|
||||
|
||||
attr_accessor :token, :project_name, :file_name, :page_num, :page_size
|
||||
attr_accessor :token, :project, :file_name, :page_num, :page_size
|
||||
|
||||
def initialize(token, project_name=nil, file_name=nil, page_num=1, page_size=15)
|
||||
def initialize(token, project, file_name=nil, page_num=1, page_size=15)
|
||||
@token = token
|
||||
@project_name = project_name
|
||||
@project = project
|
||||
@file_name = file_name
|
||||
@page_num = page_num
|
||||
@page_size = page_size
|
||||
|
@ -19,7 +19,7 @@ class Trace::CheckResultService < Trace::ClientService
|
|||
private
|
||||
def request_params
|
||||
{
|
||||
product_name: project_name,
|
||||
product_name: Digest::MD5.hexdigest(project&.id.to_s)[0...20],
|
||||
file_name: file_name,
|
||||
pageNum: page_num,
|
||||
pageSize: page_size,
|
||||
|
|
|
@ -11,26 +11,25 @@ class Trace::CheckService < Trace::ClientService
|
|||
end
|
||||
|
||||
def call
|
||||
result = authed_post(token, url, {data: request_params})
|
||||
result = http_authed_post(token, url, {data: request_params})
|
||||
reponse = render_response(result)
|
||||
end
|
||||
|
||||
private
|
||||
def request_params
|
||||
repo = Gitea::Repository::GetService.call(project&.owner&.login, project&.identifier)
|
||||
repo = Gitea::Repository::GetService.call(project&.owner, project&.identifier)
|
||||
{
|
||||
product_name: project&.name,
|
||||
product_type: project&.category&.name,
|
||||
code_type: project&.language&.name,
|
||||
product_name: Digest::MD5.hexdigest(project&.id.to_s)[0...20],
|
||||
product_type: project&.project_category&.name || '其他',
|
||||
code_type: project&.project_language&.name || '其他',
|
||||
product_desc: project&.description,
|
||||
git_url: repo['clone_url'],
|
||||
if_branch: if_branch,
|
||||
branch_tag: branch_tag
|
||||
}
|
||||
}.compact
|
||||
end
|
||||
|
||||
def url
|
||||
"/user/check".freeze
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -12,6 +12,19 @@ class Trace::ClientService < ApplicationService
|
|||
conn.post(full_url(url), params[:data])
|
||||
end
|
||||
|
||||
def http_authed_post(token, url, params={})
|
||||
puts "[trace][POST] request params: #{params}"
|
||||
puts "[trace][POST] request token: #{token}"
|
||||
url = URI("#{full_url(url)}")
|
||||
http = Net::HTTP.new(url.host, url.port)
|
||||
http.read_timeout = 1200
|
||||
request = Net::HTTP::Post.new(url)
|
||||
request["Authorization"] = token
|
||||
form_data = params[:data].stringify_keys.to_a
|
||||
request.set_form form_data, 'multipart/form-data'
|
||||
http.request(request)
|
||||
end
|
||||
|
||||
def get(url, params={})
|
||||
puts "[trace][GET] request params: #{params}"
|
||||
conn.get do |req|
|
||||
|
@ -100,11 +113,22 @@ class Trace::ClientService < ApplicationService
|
|||
end
|
||||
|
||||
def render_response(response)
|
||||
status = response.status
|
||||
body = JSON.parse(response&.body)
|
||||
if response.is_a?(Faraday::Response)
|
||||
status = response.status
|
||||
body = JSON.parse(response&.body)
|
||||
|
||||
log_error(status, body)
|
||||
log_error(status, body)
|
||||
|
||||
return [body["code"], body["data"], body["error"]]
|
||||
return [body["code"], body["data"], body["error"]]
|
||||
end
|
||||
|
||||
if response.is_a?(Net::HTTPOK)
|
||||
status = 200
|
||||
body = JSON.parse(response&.body)
|
||||
|
||||
log_error(status, body)
|
||||
|
||||
return [body["code"], body["data"], body["error"]]
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,4 +1,7 @@
|
|||
# 代码溯源 导出pdf
|
||||
require 'open-uri'
|
||||
require 'fileutils'
|
||||
|
||||
class Trace::PdfReportService < Trace::ClientService
|
||||
|
||||
attr_accessor :token, :task_id
|
||||
|
@ -9,15 +12,23 @@ class Trace::PdfReportService < Trace::ClientService
|
|||
end
|
||||
|
||||
def call
|
||||
result = authed_get(token, url, request_params)
|
||||
response = render_response(result)
|
||||
content = open("#{domain}#{base_url}#{url}?task_id=#{task_id}", "Authorization" => token)
|
||||
if content.is_a?(Tempfile)
|
||||
check_file_path
|
||||
IO.copy_stream(content, "#{save_path}/#{task_id}.pdf")
|
||||
return {code: 200, download_url: "/trace_task_results/#{task_id}.pdf"}
|
||||
else
|
||||
return {code: 404}
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def request_params
|
||||
{
|
||||
task_id: task_id
|
||||
}
|
||||
def check_file_path
|
||||
FileUtils.mkdir_p save_path
|
||||
end
|
||||
|
||||
def save_path
|
||||
"public/trace_task_results"
|
||||
end
|
||||
|
||||
def url
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
<% if message_templates.present? %>
|
||||
<% message_templates.each_with_index do |message_template_type, index| %>
|
||||
<% message_template = message_template_type.constantize.last%>
|
||||
<% message_templates.each_with_index do |message_template, index| %>
|
||||
<%# message_template = message_template_type.constantize.last%>
|
||||
<tr class="project-language-item-<%= message_template.id %>">
|
||||
<td><%= list_index_no((params[:page] || 1).to_i, index) %></td>
|
||||
<td><%= message_template.simple_type %></td>
|
||||
|
|
|
@ -3,7 +3,13 @@
|
|||
<% end %>
|
||||
<div id="admins-message-templates-content">
|
||||
<div class="box search-form-container project-list-form">
|
||||
<%= link_to "初始化数据", init_data_admins_message_templates_path, class: "btn btn-primary pull-right", "data-disabled-with":"...初始化数据" %>
|
||||
<%= form_tag(admins_message_templates_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %>
|
||||
<%= text_field_tag(:search, params[:search], class: 'form-control col-12 col-md-2 mr-3', placeholder: '名称检索') %>
|
||||
<%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
|
||||
<input type="reset" class="btn btn-secondary clear-btn" value="清空"/>
|
||||
<% end %>
|
||||
<%= link_to "初始化数据", init_data_admins_message_templates_path, class: "btn btn-primary mr-3 pull-right", "data-disabled-with":"...初始化数据" %>
|
||||
<%= link_to "新增", new_admins_message_template_path, remote: true, class: "btn btn-primary pull-right", "data-disabled-with":"...新增" %>
|
||||
</div>
|
||||
<div class="box admin-list-container message-templates-list-container">
|
||||
<%= render partial: 'admins/message_templates/list', locals: { message_templates: @message_templates } %>
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
$("#admins-message-templates-content").html("<%= j render partial: 'admins/message_templates/form', locals:{type: 'create'} %>")
|
||||
createMDEditor('message-template-email-editor', { height: 500, placeholder: '请输入邮件模版' });
|
|
@ -31,7 +31,9 @@
|
|||
<%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
|
||||
<% end %>
|
||||
|
||||
<%= javascript_void_link '导入用户', class: 'btn btn-secondary btn-sm', data: { toggle: 'modal', target: '.admin-import-user-modal'} %>
|
||||
<%= link_to '下载导入模板', "/导入用户模板.xlsx", class: 'btn btn-secondary mr-3' %>
|
||||
|
||||
<%= javascript_void_link '导入用户', class: 'btn btn-secondary', data: { toggle: 'modal', target: '.admin-import-user-modal'} %>
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
json.total_count @projects.total_count
|
||||
json.projects @projects.each do |project|
|
||||
json.(project, :id, :name, :identifier, :description, :forked_count, :praises_count, :forked_from_project_id)
|
||||
json.(project, :id, :name, :identifier, :description, :forked_count, :praises_count, :forked_from_project_id, :is_public)
|
||||
json.mirror_url project.repository&.mirror_url
|
||||
json.type project.numerical_for_project_type
|
||||
json.praised project.praised_by?(current_user)
|
||||
|
|
|
@ -9,7 +9,7 @@ json.num_projects team.num_projects
|
|||
json.num_users team.num_users
|
||||
json.units team.team_units.pluck(:unit_type)
|
||||
json.users team.team_users.each do |user|
|
||||
json.partial! "organizations/user_detail", user: user&.user
|
||||
json.partial! "organizations/user_detail", user: user&.user if user&.user
|
||||
end
|
||||
json.is_admin @is_admin
|
||||
json.is_member team.is_member?(current_user.id)
|
||||
|
|
|
@ -9,7 +9,10 @@ if @project.forge?
|
|||
json.path entry['path']
|
||||
json.type entry['type']
|
||||
json.size entry['size']
|
||||
|
||||
is_readme = is_readme?(entry['type'], entry['name'])
|
||||
if is_readme
|
||||
json.replace_content readme_decode64_content(entry, @owner, @repository, @ref, @path)
|
||||
end
|
||||
json.content (direct_download || image_type || is_dir) ? nil : decode64_content(entry, @owner, @repository, @ref, @path)
|
||||
json.target entry['target']
|
||||
|
||||
|
@ -25,7 +28,7 @@ if @project.forge?
|
|||
|
||||
json.direct_download direct_download
|
||||
json.image_type image_type
|
||||
json.is_readme_file is_readme?(entry['type'], entry['name'])
|
||||
json.is_readme_file is_readme
|
||||
json.commit do
|
||||
json.partial! 'last_commit', latest_commit: entry['latest_commit']
|
||||
end
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
if @user.present?
|
||||
json.partial! 'users/user', locals: { user: @user }
|
||||
else
|
||||
json.null
|
||||
end
|
|
@ -1,27 +1,29 @@
|
|||
json.username @user.full_name
|
||||
json.real_name @user.real_name
|
||||
json.nickname @user.nickname
|
||||
json.gender @user.gender
|
||||
json.login @user.login
|
||||
json.user_id @user.id
|
||||
json.image_url url_to_avatar(@user)
|
||||
json.admin @user.admin?
|
||||
json.is_teacher @user.user_extension&.teacher?
|
||||
json.user_identity @user.identity
|
||||
json.tidding_count 0
|
||||
json.user_phone_binded @user.phone.present?
|
||||
json.need_edit_info @user.need_edit_info?
|
||||
# json.phone @user.phone
|
||||
# json.email @user.mail
|
||||
json.profile_completed @user.profile_is_completed?
|
||||
json.professional_certification @user.professional_certification
|
||||
json.devops_step @user.devops_step
|
||||
json.ci_certification @user.ci_certification?
|
||||
json.email @user.mail
|
||||
json.province @user.province
|
||||
json.city @user.city
|
||||
json.custom_department @user.custom_department
|
||||
json.description @user.description
|
||||
json.super_description @user.super_description
|
||||
json.(@user, :show_email, :show_department, :show_location, :show_super_description)
|
||||
json.message_unread_total @message_unread_total
|
||||
json.username @user.full_name
|
||||
json.real_name @user.real_name
|
||||
json.nickname @user.nickname
|
||||
json.gender @user.gender
|
||||
json.login @user.login
|
||||
json.user_id @user.id
|
||||
json.image_url url_to_avatar(@user)
|
||||
json.admin @user.admin?
|
||||
json.is_teacher @user.user_extension&.teacher?
|
||||
json.user_identity @user.identity
|
||||
json.tidding_count 0
|
||||
json.user_phone_binded @user.phone.present?
|
||||
json.need_edit_info @user.need_edit_info?
|
||||
# json.phone @user.phone
|
||||
# json.email @user.mail
|
||||
json.profile_completed @user.profile_is_completed?
|
||||
json.professional_certification @user.professional_certification
|
||||
json.devops_step @user.devops_step
|
||||
json.ci_certification @user.ci_certification?
|
||||
json.email @user.mail
|
||||
json.province @user.province
|
||||
json.city @user.city
|
||||
json.custom_department @user.custom_department
|
||||
json.description @user.description
|
||||
json.super_description @user.super_description
|
||||
json.(@user, :show_email, :show_department, :show_location, :show_super_description)
|
||||
json.message_unread_total @message_unread_total
|
||||
json.has_trace_user @user.trace_user.present?
|
||||
json.is_new @user.login.present? && params[:login].to_s.include?("#{@user.login}")
|
|
@ -214,6 +214,8 @@ Rails.application.routes.draw do
|
|||
post :remote_password
|
||||
post :change_password
|
||||
post :check
|
||||
post :login_check
|
||||
post :simple_update
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -247,6 +249,7 @@ Rails.application.routes.draw do
|
|||
get :trustie_projects
|
||||
get :trustie_related_projects
|
||||
post :sync_user_info
|
||||
get :email_search
|
||||
|
||||
scope '/ci', module: :ci do
|
||||
scope do
|
||||
|
@ -427,6 +430,20 @@ Rails.application.routes.draw do
|
|||
end
|
||||
end
|
||||
|
||||
namespace :traces do
|
||||
resources :trace_users, only: [:create]
|
||||
scope "/:owner/:repo" do
|
||||
resource :projects, path: '/', only: [:index] do
|
||||
member do
|
||||
post :tasks
|
||||
get :task_results
|
||||
get :reload_task
|
||||
get :task_pdf
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Project Area START
|
||||
scope "/:owner/:repo" do
|
||||
scope do
|
||||
|
@ -712,7 +729,7 @@ Rails.application.routes.draw do
|
|||
get :history
|
||||
end
|
||||
end
|
||||
resources :message_templates, only: [:index, :edit, :update] do
|
||||
resources :message_templates, only: [:index, :new, :create, :edit, :update] do
|
||||
collection do
|
||||
get :init_data
|
||||
end
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
class CreateUserTraceTasks < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :user_trace_tasks do |t|
|
||||
t.references :user
|
||||
t.references :project
|
||||
t.string :branch_tag
|
||||
t.string :task_id
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,35 @@
|
|||
namespace :sync_outer_repo do
|
||||
desc "sync outer repository to gitlink"
|
||||
task done: :environment do
|
||||
file = ENV['file'] || "rcore-os_repo.xlsx"
|
||||
user_type = ENV['type'] || "User"
|
||||
doc = SimpleXlsxReader.open("#{Rails.root}/public/#{file}")
|
||||
data = doc.sheets.first.rows
|
||||
data.each_with_index do |row, index|
|
||||
next if index == 0
|
||||
begin
|
||||
user = (user_type == "User" ? User.find_by(login: row[1]) : Owner.find_by(login: row[1]))
|
||||
project = user.projects.find_by(identifier: row[4])
|
||||
unless project.present?
|
||||
p_category = ProjectCategory.find_or_create_by(name: row[6])
|
||||
p_language = ProjectLanguage.find_or_create_by(name: row[7].to_s.split("/")[0]) if row[7]
|
||||
p_license = License.find_by(name: row[8])
|
||||
|
||||
mirror_params = {
|
||||
user_id: user.id,
|
||||
name: row[5],
|
||||
description: row[9],
|
||||
repository_name: row[4],
|
||||
project_category_id: p_category.id,
|
||||
project_language_id: p_language&.id,
|
||||
clone_addr: row[10]
|
||||
}
|
||||
Projects::MigrateService.call(user, mirror_params)
|
||||
end
|
||||
puts "sync outer repository to gitlink Success repo: #{row[5]} username: #{row[0]}"
|
||||
rescue Exception => e
|
||||
puts "sync outer repository to gitlink Error repo: #{row[5]} username: #{row[0]}, error:#{e}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -467,6 +467,9 @@
|
|||
<li>
|
||||
<a href="#4f8579f6bd" class="toc-h2 toc-link" data-title="取消迁移项目">取消迁移项目</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#88c0b2e25f" class="toc-h2 toc-link" data-title="退出项目">退出项目</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -543,6 +546,26 @@
|
|||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#traces" class="toc-h1 toc-link" data-title="Traces">Traces</a>
|
||||
<ul class="toc-list-h2">
|
||||
<li>
|
||||
<a href="#ca438fc3ca" class="toc-h2 toc-link" data-title="代码溯源初始化">代码溯源初始化</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#bb16c601f1" class="toc-h2 toc-link" data-title="代码分析结果列表">代码分析结果列表</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#32497859e0" class="toc-h2 toc-link" data-title="新建分析">新建分析</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#7b3a48e274" class="toc-h2 toc-link" data-title="重新扫描">重新扫描</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#87775b1430" class="toc-h2 toc-link" data-title="下载报告">下载报告</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#pulls" class="toc-h1 toc-link" data-title="Pulls">Pulls</a>
|
||||
<ul class="toc-list-h2">
|
||||
|
@ -4968,7 +4991,7 @@ http://localhost:3000/api/yystopf/ceshi/menu_list | jq
|
|||
<tr>
|
||||
<td>menu_name</td>
|
||||
<td>string</td>
|
||||
<td>导航名称, home:主页,code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑,activity:动态,setting:仓库设置</td>
|
||||
<td>导航名称, home:主页,code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑,wiki:维基,services:服务,activity:动态,setting:仓库设置</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
|
@ -5131,7 +5154,7 @@ http://localhost:3000/api/yystopf/ceshi/project_units.json
|
|||
<td>是</td>
|
||||
<td></td>
|
||||
<td>array</td>
|
||||
<td>项目模块内容, 支持以下参数:code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑</td>
|
||||
<td>项目模块内容, 支持以下参数:code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑,wiki:维基,resources:资源库,services:服务</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
<h3 id='7447e4874e-2'>返回字段说明:</h3>
|
||||
|
@ -6026,6 +6049,49 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat
|
|||
</span><span class="nl">"created_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2021-04-26 09:54"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"time_ago"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1分钟前"</span><span class="w">
|
||||
</span><span class="p">}</span><span class="w">
|
||||
</span></code></pre></div><h2 id='88c0b2e25f'>退出项目</h2>
|
||||
<p>供项目成员(开发者、报告者)退出项目用</p>
|
||||
|
||||
<blockquote>
|
||||
<p>示例:</p>
|
||||
</blockquote>
|
||||
<div class="highlight"><pre class="highlight shell tab-shell"><code>curl <span class="nt">-X</span> POST http://localhost:3000/api/ceshi1/ceshi_repo1/quit.json
|
||||
</code></pre></div><div class="highlight"><pre class="highlight javascript tab-javascript"><code><span class="k">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">POST /api/:owner/:repo/quit.json</span><span class="dl">'</span><span class="p">)</span>
|
||||
</code></pre></div><h3 id='http-15'>HTTP 请求</h3>
|
||||
<p><code>POST /api/:owner/:repo/quit.json</code></p>
|
||||
<h3 id='1f9ac54b15-13'>请求参数</h3>
|
||||
<table><thead>
|
||||
<tr>
|
||||
<th>参数</th>
|
||||
<th>必选</th>
|
||||
<th>默认</th>
|
||||
<th>类型</th>
|
||||
<th>字段说明</th>
|
||||
</tr>
|
||||
</thead><tbody>
|
||||
<tr>
|
||||
<td>owner</td>
|
||||
<td>是</td>
|
||||
<td></td>
|
||||
<td>string</td>
|
||||
<td>用户登录名</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>repo</td>
|
||||
<td>是</td>
|
||||
<td></td>
|
||||
<td>string</td>
|
||||
<td>项目标识identifier</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
<blockquote>
|
||||
<p>返回的JSON示例:</p>
|
||||
</blockquote>
|
||||
<div class="highlight"><pre class="highlight json tab-json"><code><span class="p">{</span><span class="w">
|
||||
</span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w">
|
||||
</span><span class="p">}</span><span class="w">
|
||||
</span></code></pre></div><h1 id='repositories'>Repositories</h1><h2 id='0a366f0c25'>仓库详情</h2>
|
||||
<p>仓库详情</p>
|
||||
|
||||
|
@ -9302,6 +9368,289 @@ http://localhost:3000/api/yystopf/ceshi/webhooks/3/test.json
|
|||
<aside class="success">
|
||||
Success Data.
|
||||
</aside>
|
||||
<h1 id='traces'>Traces</h1><h2 id='ca438fc3ca'>代码溯源初始化</h2>
|
||||
<p>用户同意协议后请求的接口,创建代码溯源的账号</p>
|
||||
|
||||
<blockquote>
|
||||
<p>示例:</p>
|
||||
</blockquote>
|
||||
<div class="highlight"><pre class="highlight shell tab-shell"><code>curl <span class="nt">-X</span> POST <span class="se">\</span>
|
||||
http://localhost:3000/api/traces/trace_users.json
|
||||
</code></pre></div><div class="highlight"><pre class="highlight javascript tab-javascript"><code><span class="k">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">POST /api/traces/trace_users.json</span><span class="dl">'</span><span class="p">)</span>
|
||||
</code></pre></div><h3 id='http'>HTTP 请求</h3>
|
||||
<p><code>POST api/traces/trace_users.json</code></p>
|
||||
|
||||
<blockquote>
|
||||
<p>返回的JSON示例:</p>
|
||||
</blockquote>
|
||||
<div class="highlight"><pre class="highlight json tab-json"><code><span class="p">{</span><span class="w">
|
||||
</span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w">
|
||||
</span><span class="p">}</span><span class="w">
|
||||
</span></code></pre></div>
|
||||
<aside class="success">
|
||||
Success — a happy kitten is an authenticated kitten!
|
||||
</aside>
|
||||
<h2 id='bb16c601f1'>代码分析结果列表</h2>
|
||||
<p>查询项目下代码分析的结果</p>
|
||||
|
||||
<blockquote>
|
||||
<p>示例:</p>
|
||||
</blockquote>
|
||||
<div class="highlight"><pre class="highlight shell tab-shell"><code>curl <span class="nt">-X</span> GET <span class="se">\</span>
|
||||
http://localhost:3000/api/traces/yystopf/many_branch/task_results.json
|
||||
</code></pre></div><div class="highlight"><pre class="highlight javascript tab-javascript"><code><span class="k">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">GET /api/traces/:owner/:repo/task_results.json</span><span class="dl">'</span><span class="p">)</span>
|
||||
</code></pre></div><h3 id='http-2'>HTTP 请求</h3>
|
||||
<p><code>GET api/traces/:owner/:repo/task_results.json</code></p>
|
||||
<h3 id='1f9ac54b15'>请求参数</h3>
|
||||
<table><thead>
|
||||
<tr>
|
||||
<th>参数</th>
|
||||
<th>必选</th>
|
||||
<th>默认</th>
|
||||
<th>类型</th>
|
||||
<th>字段说明</th>
|
||||
</tr>
|
||||
</thead><tbody>
|
||||
<tr>
|
||||
<td>owner</td>
|
||||
<td>是</td>
|
||||
<td>否</td>
|
||||
<td>string</td>
|
||||
<td>项目所有者标识</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>repo</td>
|
||||
<td>是</td>
|
||||
<td>否</td>
|
||||
<td>string</td>
|
||||
<td>项目标识</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>page</td>
|
||||
<td>否</td>
|
||||
<td>1</td>
|
||||
<td>int</td>
|
||||
<td>页码</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>limit</td>
|
||||
<td>否</td>
|
||||
<td>15</td>
|
||||
<td>int</td>
|
||||
<td>每页数量</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
<h3 id='90889036d2'>返回字段说明(暂缺)</h3>
|
||||
<!-- 参数 | 类型 | 字段说明
|
||||
--------- | ----------- | -----------
|
||||
total_count |int |总数 |
|
||||
public_keys.id |int |ID|
|
||||
public_keys.name |string|密钥标题|
|
||||
public_keys.content |string|密钥内容|
|
||||
public_keys.fingerprint |string|密钥标识|
|
||||
public_keys.created_time |string|密钥创建时间| -->
|
||||
|
||||
<blockquote>
|
||||
<p>返回的JSON示例:</p>
|
||||
</blockquote>
|
||||
<div class="highlight"><pre class="highlight json tab-json"><code><span class="p">{</span><span class="w">
|
||||
</span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
|
||||
</span><span class="p">{</span><span class="w">
|
||||
</span><span class="nl">"accuracy"</span><span class="p">:</span><span class="w"> </span><span class="s2">"20"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"code_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"C"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"depth"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"detect_flag"</span><span class="p">:</span><span class="w"> </span><span class="s2">"快速-组件级"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"detect_rule"</span><span class="p">:</span><span class="w"> </span><span class="s2">"快速-组件级,2,20,,开源软件,50,10"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"detect_startdate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-05-10 15:59:46 "</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"detect_status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"fail"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"detectflag"</span><span class="p">:</span><span class="w"> </span><span class="s2">"快速-组件级"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"fail_reason"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Invalid package type"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"file_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"many_branch.zip"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"license_process"</span><span class="p">:</span><span class="w"> </span><span class="s2">"100"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"licenseparam"</span><span class="p">:</span><span class="w"> </span><span class="s2">"开源软件"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"package_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"product_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"84727546110"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"project_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6dbc3e42-5857-4ca4-a54d-58fd9dbf6dc5"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"sim_process"</span><span class="p">:</span><span class="w"> </span><span class="s2">"100"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"similarity_process"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"task_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"15139171-091b-4316-98b1-6068970efa44"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"totalsize"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"uid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"78"</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"vuln_process"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"vulnlevel"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
|
||||
</span><span class="p">}</span><span class="w">
|
||||
</span><span class="p">]</span><span class="w">
|
||||
</span><span class="p">}</span><span class="w">
|
||||
</span></code></pre></div>
|
||||
<aside class="success">
|
||||
Success — a happy kitten is an authenticated kitten!
|
||||
</aside>
|
||||
<h2 id='32497859e0'>新建分析</h2>
|
||||
<p>用户选择仓库分支进行代码分析的接口</p>
|
||||
|
||||
<blockquote>
|
||||
<p>示例:</p>
|
||||
</blockquote>
|
||||
<div class="highlight"><pre class="highlight shell tab-shell"><code>curl <span class="nt">-X</span> POST <span class="se">\</span>
|
||||
http://localhost:3000/api/traces/yystopf/many_branch/tasks.json
|
||||
</code></pre></div><div class="highlight"><pre class="highlight javascript tab-javascript"><code><span class="k">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">POST /api/traces/:owner/:repo/tasks.json</span><span class="dl">'</span><span class="p">)</span>
|
||||
</code></pre></div><h3 id='http-3'>HTTP 请求</h3>
|
||||
<p><code>POST api/traces/:owner/:repo/tasks.json</code></p>
|
||||
<h3 id='1f9ac54b15-2'>请求参数</h3>
|
||||
<table><thead>
|
||||
<tr>
|
||||
<th>参数</th>
|
||||
<th>必选</th>
|
||||
<th>默认</th>
|
||||
<th>类型</th>
|
||||
<th>字段说明</th>
|
||||
</tr>
|
||||
</thead><tbody>
|
||||
<tr>
|
||||
<td>owner</td>
|
||||
<td>是</td>
|
||||
<td>否</td>
|
||||
<td>string</td>
|
||||
<td>项目所有者标识</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>repo</td>
|
||||
<td>是</td>
|
||||
<td>否</td>
|
||||
<td>string</td>
|
||||
<td>项目标识</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>branch_name</td>
|
||||
<td>是</td>
|
||||
<td>否</td>
|
||||
<td>string</td>
|
||||
<td>分支名称</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
<blockquote>
|
||||
<p>返回的JSON示例:</p>
|
||||
</blockquote>
|
||||
<div class="highlight"><pre class="highlight json tab-json"><code><span class="p">{</span><span class="w">
|
||||
</span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w">
|
||||
</span><span class="p">}</span><span class="w">
|
||||
</span></code></pre></div>
|
||||
<aside class="success">
|
||||
Success — a happy kitten is an authenticated kitten!
|
||||
</aside>
|
||||
<h2 id='7b3a48e274'>重新扫描</h2>
|
||||
<p>对代码分析结果进行再次分析</p>
|
||||
|
||||
<blockquote>
|
||||
<p>示例:</p>
|
||||
</blockquote>
|
||||
<div class="highlight"><pre class="highlight shell tab-shell"><code>curl <span class="nt">-X</span> GET <span class="se">\</span>
|
||||
http://localhost:3000/api/traces/yystopf/many_branch/reload_task.json
|
||||
</code></pre></div><div class="highlight"><pre class="highlight javascript tab-javascript"><code><span class="k">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">GET /api/traces/:owner/:repo/reload_task.json</span><span class="dl">'</span><span class="p">)</span>
|
||||
</code></pre></div><h3 id='http-4'>HTTP 请求</h3>
|
||||
<p><code>GET api/traces/:owner/:repo/reload_task.json</code></p>
|
||||
<h3 id='1f9ac54b15-3'>请求参数</h3>
|
||||
<table><thead>
|
||||
<tr>
|
||||
<th>参数</th>
|
||||
<th>必选</th>
|
||||
<th>默认</th>
|
||||
<th>类型</th>
|
||||
<th>字段说明</th>
|
||||
</tr>
|
||||
</thead><tbody>
|
||||
<tr>
|
||||
<td>owner</td>
|
||||
<td>是</td>
|
||||
<td>否</td>
|
||||
<td>string</td>
|
||||
<td>项目所有者标识</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>repo</td>
|
||||
<td>是</td>
|
||||
<td>否</td>
|
||||
<td>string</td>
|
||||
<td>项目标识</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>project_id</td>
|
||||
<td>是</td>
|
||||
<td>否</td>
|
||||
<td>string</td>
|
||||
<td>代码分析结果里的project_id</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
<blockquote>
|
||||
<p>返回的JSON示例:</p>
|
||||
</blockquote>
|
||||
<div class="highlight"><pre class="highlight json tab-json"><code><span class="p">{</span><span class="w">
|
||||
</span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w">
|
||||
</span><span class="p">}</span><span class="w">
|
||||
</span></code></pre></div>
|
||||
<aside class="success">
|
||||
Success — a happy kitten is an authenticated kitten!
|
||||
</aside>
|
||||
<h2 id='87775b1430'>下载报告</h2>
|
||||
<p>把代码分析的结果下载到本地</p>
|
||||
|
||||
<blockquote>
|
||||
<p>示例:</p>
|
||||
</blockquote>
|
||||
<div class="highlight"><pre class="highlight shell tab-shell"><code>curl <span class="nt">-X</span> GET <span class="se">\</span>
|
||||
http://localhost:3000/api/traces/yystopf/many_branch/task_pdf.json
|
||||
</code></pre></div><div class="highlight"><pre class="highlight javascript tab-javascript"><code><span class="k">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">GET /api/traces/:owner/:repo/task_pdf.json</span><span class="dl">'</span><span class="p">)</span>
|
||||
</code></pre></div><h3 id='http-5'>HTTP 请求</h3>
|
||||
<p><code>GET api/traces/:owner/:repo/task_pdf.json</code></p>
|
||||
<h3 id='1f9ac54b15-4'>请求参数</h3>
|
||||
<table><thead>
|
||||
<tr>
|
||||
<th>参数</th>
|
||||
<th>必选</th>
|
||||
<th>默认</th>
|
||||
<th>类型</th>
|
||||
<th>字段说明</th>
|
||||
</tr>
|
||||
</thead><tbody>
|
||||
<tr>
|
||||
<td>owner</td>
|
||||
<td>是</td>
|
||||
<td>否</td>
|
||||
<td>string</td>
|
||||
<td>项目所有者标识</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>repo</td>
|
||||
<td>是</td>
|
||||
<td>否</td>
|
||||
<td>string</td>
|
||||
<td>项目标识</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>task_id</td>
|
||||
<td>是</td>
|
||||
<td>否</td>
|
||||
<td>string</td>
|
||||
<td>代码分析结果里的task_id</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
<blockquote>
|
||||
<p>返回的JSON示例:</p>
|
||||
</blockquote>
|
||||
<div class="highlight"><pre class="highlight json tab-json"><code><span class="p">{</span><span class="w">
|
||||
</span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
|
||||
</span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w">
|
||||
</span><span class="p">}</span><span class="w">
|
||||
</span></code></pre></div>
|
||||
<aside class="success">
|
||||
Success — a happy kitten is an authenticated kitten!
|
||||
</aside>
|
||||
<h1 id='pulls'>Pulls</h1><h2 id='get-a-pull-request'>Get a pull request</h2>
|
||||
<p>获取合并请求详情接口</p>
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,5 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe UserTraceTask, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
Loading…
Reference in New Issue