Merge branch 'develop'

This commit is contained in:
xxq250 2023-01-12 16:58:48 +08:00
commit 196d1d74af
92 changed files with 1264 additions and 165 deletions

View File

@ -118,6 +118,10 @@ gem 'deep_cloneable', '~> 3.0.0'
# oauth2 # oauth2
gem 'omniauth', '~> 1.9.0' gem 'omniauth', '~> 1.9.0'
gem 'omniauth-oauth2', '~> 1.6.0' gem 'omniauth-oauth2', '~> 1.6.0'
gem "omniauth-github"
gem "omniauth-rails_csrf_protection"
gem 'omniauth-gitee', '~> 1.0.0'
gem "omniauth-wechat-oauth2"
# global var # global var
gem 'request_store' gem 'request_store'
@ -135,4 +139,4 @@ gem 'doorkeeper'
gem 'doorkeeper-jwt' gem 'doorkeeper-jwt'
gem 'gitea-client', '~> 0.10.5' gem 'gitea-client', '~> 0.11.1'

View File

@ -141,6 +141,7 @@ class AccountsController < ApplicationController
Register::Form.new(register_params).validate! Register::Form.new(register_params).validate!
user = Users::RegisterService.call(register_params) user = Users::RegisterService.call(register_params)
user.mail = "#{user.login}@example.org" if user.mail.blank?
password = register_params[:password].strip password = register_params[:password].strip
# gitea用户注册, email, username, password # gitea用户注册, email, username, password
@ -152,6 +153,10 @@ class AccountsController < ApplicationController
user.gitea_uid = gitea_user[:body]['id'] user.gitea_uid = gitea_user[:body]['id']
if user.save! if user.save!
UserExtension.create!(user_id: user.id) UserExtension.create!(user_id: user.id)
# 绑定授权账号
if ["qq", "wechat", "gitee", "github", "educoder"].include?(params[:type].to_s) && session[:unionid].present?
"OpenUsers::#{params[:type].to_s.capitalize}".constantize.create!(user: user, uid: session[:unionid])
end
successful_authentication(user) successful_authentication(user)
render_ok render_ok
end end
@ -393,7 +398,7 @@ class AccountsController < ApplicationController
end end
def register_params def register_params
params.permit(:login, :namespace, :password, :password_confirmation, :code) params.permit(:login, :namespace, :password, :password_confirmation, :code, :type)
end end
def reset_password_params def reset_password_params

View File

@ -0,0 +1,49 @@
class Admins::FeedbacksController < Admins::BaseController
before_action :get_feedback, only: [:new_history, :create_history, :destroy]
def index
sort_by = Feedback.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at'
sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc'
feedbacks = Feedback.order("#{sort_by} #{sort_direction}")
@feedbacks = paginate(feedbacks)
end
def destroy
if @feedback.destroy
redirect_to admins_feedbacks_path
flash[:success] = "反馈意见删除成功"
else
redirect_to admins_feedbacks_path
flash[:danger] = "反馈意见删除失败"
end
end
def new_history
@feedback_message_history = FeedbackMessageHistory.new
end
def create_history
@feedback_message_history = @feedback.feedback_message_histories.new(feedback_message_history_params)
@feedback_message_history.user = current_user
if @feedback_message_history.save
redirect_to admins_feedbacks_path
flash[:success] = "发送通知成功"
else
redirect_to admins_feedbacks_path
flash[:danger] = @feedback_message_history.errors.full_messages.join(", ")
end
end
private
def feedback_params
params.require(:feedback).permit!
end
def feedback_message_history_params
params.require(:feedback_message_history).permit(:title, :content)
end
def get_feedback
@feedback = Feedback.find_by_id(params[:id])
end
end

View File

@ -53,6 +53,6 @@ class Admins::Topic::BannersController < Admins::Topic::BaseController
end end
def banner_params def banner_params
params.require(:topic_banner).permit(:title, :order_index) params.require(:topic_banner).permit(:title, :order_index, :url)
end end
end end

View File

@ -0,0 +1,8 @@
class Api::V1::Projects::CodeStatsController < Api::V1::BaseController
before_action :require_public_and_member_above, only: [:index]
def index
@result_object = Api::V1::Projects::CodeStats::ListService.call(@project, {ref: params[:ref]}, current_user&.gitea_token)
puts @result_object
end
end

View File

@ -0,0 +1,16 @@
class Api::V1::Users::FeedbacksController < Api::V1::BaseController
before_action :load_observe_user
before_action :check_auth_for_observe_user
def create
@result = Api::V1::Users::Feedbacks::CreateService.call(@observe_user, feedback_params)
return render_error("反馈意见创建失败.") if @result.nil?
return render_ok
end
private
def feedback_params
params.permit(:content)
end
end

View File

@ -70,6 +70,21 @@ class Api::V1::UsersController < Api::V1::BaseController
render_ok render_ok
end end
def check_phone_verify_code
code = strip(params[:code])
phone = strip(params[:phone])
code_type = params[:code_type]
return tip_exception(-2, "手机号格式有误") unless phone =~ CustomRegexp::PHONE
verifi_code = VerificationCode.where(phone: phone, code: code, code_type: code_type).last
return render_ok if code == "123123" && EduSetting.get("code_debug") # 万能验证码,用于测试 # TODO 万能验证码,用于测试
return tip_exception(-6, "验证码不正确") if verifi_code&.code != code
return tip_exception(-6, "验证码已失效") if !verifi_code&.effective?
render_ok
end
def update_email def update_email
@result_object = Api::V1::Users::UpdateEmailService.call(@observe_user, params, current_user.gitea_token) @result_object = Api::V1::Users::UpdateEmailService.call(@observe_user, params, current_user.gitea_token)
if @result_object if @result_object
@ -78,4 +93,13 @@ class Api::V1::UsersController < Api::V1::BaseController
return render_error('更改邮箱失败!') return render_error('更改邮箱失败!')
end end
end end
def update_phone
@result_object = Api::V1::Users::UpdatePhoneService.call(@observe_user, params)
if @result_object
return render_ok
else
return render_error('更改手机号失败!')
end
end
end end

View File

@ -79,8 +79,7 @@ class ApplicationController < ActionController::Base
# 判断用户的邮箱或者手机是否可用 # 判断用户的邮箱或者手机是否可用
# params[:type] 1: 注册2忘记密码3绑定 # params[:type] 1: 注册2忘记密码3绑定
def check_mail_and_phone_valid login, type def check_mail_and_phone_valid login, type
unless login =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/ || login =~ /^1\d{10}$/ || unless login =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/ || login =~ /^1\d{10}$/
login =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])$/
tip_exception(-2, "请输入正确的手机号或邮箱") tip_exception(-2, "请输入正确的手机号或邮箱")
end end
@ -103,8 +102,10 @@ class ApplicationController < ActionController::Base
when 1, 2, 4, 9 when 1, 2, 4, 9
# 手机类型的发送 # 手机类型的发送
sigle_para = {phone: value} sigle_para = {phone: value}
status = Gitlink::Sms.send(mobile: value, code: code) # status = Gitlink::Sms.send(mobile: value, code: code)
tip_exception(-2, code_msg(status)) if status != 0 # tip_exception(-2, code_msg(status)) if status != 0
status = Sms::UcloudService.call(value, code, send_type)
tip_exception(-2, ucloud_code_msg(status)) if status != 0
when 8, 3, 5 when 8, 3, 5
# 邮箱类型的发送 # 邮箱类型的发送
sigle_para = {email: value} sigle_para = {email: value}
@ -116,8 +117,13 @@ class ApplicationController < ActionController::Base
send_email_control = LimitForbidControl::SendEmailCode.new(value) send_email_control = LimitForbidControl::SendEmailCode.new(value)
tip_exception(-1, '邮件发送太频繁,请稍后再试') if send_email_control.forbid? tip_exception(-1, '邮件发送太频繁,请稍后再试') if send_email_control.forbid?
begin begin
UserMailer.register_email(value, code).deliver_now if send_type == 3
UserMailer.find_password(value, code).deliver_now
elsif send_type == 5
UserMailer.bind_email(value, code).deliver_now
else
UserMailer.register_email(value, code).deliver_now
end
Rails.cache.write(send_email_limit_cache_key, 1, expires_in: 1.minute) Rails.cache.write(send_email_limit_cache_key, 1, expires_in: 1.minute)
send_email_control.increment! send_email_control.increment!
# Mailer.run.email_register(code, value) # Mailer.run.email_register(code, value)
@ -149,6 +155,27 @@ class ApplicationController < ActionController::Base
end end
end end
def ucloud_code_msg status
case status
when 0
"验证码已经发送到您的手机,请注意查收"
when 171
"API签名错误"
when 18014
"无效手机号码"
when 18017
"无效模板"
when 18018
"短信模板参数与短信模板不匹配"
when 18023
"短信内容中含有运营商拦截的关键词"
when 18033
"变量内容不符合规范"
else
"错误码#{status}"
end
end
def validate_type(object_type) def validate_type(object_type)
normal_status(2, "参数") if params.has_key?(:sort_type) && !SORT_TYPE.include?(params[:sort_type].strip) normal_status(2, "参数") if params.has_key?(:sort_type) && !SORT_TYPE.include?(params[:sort_type].strip)
end end
@ -638,7 +665,7 @@ class ApplicationController < ActionController::Base
def kaminari_paginate(relation) def kaminari_paginate(relation)
limit = params[:limit] || params[:per_page] limit = params[:limit] || params[:per_page]
limit = (limit.to_i.zero? || limit.to_i > 25) ? 25 : limit.to_i limit = (limit.to_i.zero? || limit.to_i > 50) ? 50 : limit.to_i
page = params[:page].to_i.zero? ? 1 : params[:page].to_i page = params[:page].to_i.zero? ? 1 : params[:page].to_i
relation.page(page).per(limit) relation.page(page).per(limit)

View File

@ -32,7 +32,7 @@ class AttachmentsController < ApplicationController
def get_file def get_file
normal_status(-1, "参数缺失") if params[:download_url].blank? normal_status(-1, "参数缺失") if params[:download_url].blank?
url = base_url.starts_with?("https:") ? URI.encode(params[:download_url].to_s.gsub("http:", "https:")) : URI.encode(params[:download_url].to_s) url = base_url.starts_with?("https:") ? URI.encode(params[:download_url].to_s.gsub("http:", "https:")) : URI.encode(params[:download_url].to_s)
if url.starts_with?(base_url) if url.starts_with?(base_url) && !url.starts_with?("#{base_url}/repo")
domain = GiteaService.gitea_config[:domain] domain = GiteaService.gitea_config[:domain]
api_url = GiteaService.gitea_config[:base_url] api_url = GiteaService.gitea_config[:base_url]
url = ("/repos"+url.split(base_url + "/api")[1]).gsub('?filepath=', '/').gsub('&', '?') url = ("/repos"+url.split(base_url + "/api")[1]).gsub('?filepath=', '/').gsub('&', '?')
@ -213,20 +213,17 @@ class AttachmentsController < ApplicationController
def attachment_candown def attachment_candown
unless current_user.admin? || current_user.business? unless current_user.admin? || current_user.business?
candown = true candown = true
unless params[:type] == 'history' if @file.container
if @file.container && current_user.logged? if @file.container.is_a?(Issue)
if @file.container.is_a?(Issue) project = @file.container.project
course = @file.container.project candown = project.is_public || (current_user.logged? && project.member?(current_user))
candown = course.member?(current_user) || course.is_public elsif @file.container.is_a?(Journal)
elsif @file.container.is_a?(Journal) project = @file.container.issue.project
course = @file.container.issue.project candown = project.is_public || (current_user.logged? && project.member?(current_user))
candown = course.member?(current_user) || course.is_public else
else project = nil
course = nil
end
tip_exception(403, "您没有权限进入") if course.present? && !candown
tip_exception(403, "您没有权限进入") if @file.container.is_a?(ApplyUserAuthentication)
end end
tip_exception(403, "您没有权限进入") if project.present? && !candown
end end
end end
end end

View File

@ -1,35 +1,19 @@
class BindUsersController < ApplicationController class BindUsersController < ApplicationController
# before_action :require_login
def create def create
# user = CreateBindUserService.call(create_params) Rails.logger.debug "--------------开始绑定用户------------"
# Rails.logger.debug "--------------params: #{params.to_unsafe_h}"
if params[:type] == "qq" tip_exception '系统错误' if session[:unionid].blank?
begin
user = CreateBindUserService.call(current_user, create_params)
successful_authentication(user) if user.id != current_user.id
render_ok bind_user = User.try_to_login(params[:username], params[:password])
rescue ApplicationService::Error => ex tip_exception '用户名或者密码错误' if bind_user.blank?
render_error(ex.message) tip_exception '用户名或者密码错误' unless bind_user.check_password?(params[:password].to_s)
end tip_exception '参数错误' unless ["qq", "wechat", "gitee", "github", "educoder"].include?(params[:type].to_s)
else tip_exception '该账号已被绑定,请更换其他账号进行绑定' if bind_user.bind_open_user?(params[:type].to_s)
begin
tip_exception '系统错误' if session[:unionid].blank?
bind_user = User.try_to_login(params[:username], params[:password]) "OpenUsers::#{params[:type].to_s.capitalize}".constantize.create!(user: bind_user, uid: session[:unionid])
tip_exception '用户名或者密码错误' if bind_user.blank? successful_authentication(bind_user)
tip_exception '用户名或者密码错误' unless bind_user.check_password?(params[:password].to_s) @user = bind_user
tip_exception '该账号已被绑定,请更换其他账号进行绑定' if bind_user.bind_open_user?(params[:type].to_s)
OpenUsers::Wechat.create!(user: bind_user, uid: session[:unionid])
successful_authentication(bind_user)
render_ok
rescue Exception => e
render_error(e.message)
end
end
end end
def new_user def new_user

View File

@ -11,7 +11,7 @@ module LoginHelper
def set_autologin_cookie(user) def set_autologin_cookie(user)
token = Token.get_or_create_permanent_login_token(user, "autologin") token = Token.get_or_create_permanent_login_token(user, "autologin")
sync_user_token_to_trustie(user.login, token.value) # sync_user_token_to_trustie(user.login, token.value)
Rails.logger.info "###### def set_autologin_cookie and get_or_create_permanent_login_token result: #{token&.value}" Rails.logger.info "###### def set_autologin_cookie and get_or_create_permanent_login_token result: #{token&.value}"
cookie_options = { cookie_options = {

View File

@ -1,18 +1,21 @@
module RegisterHelper module RegisterHelper
extend ActiveSupport::Concern extend ActiveSupport::Concern
def autologin_register(username, email, password, platform= 'forge', need_edit_info = false) def autologin_register(username, email, password, platform = 'forge', phone = nil, nickname =nil, need_edit_info = false)
result = {message: nil, user: nil} result = {message: nil, user: nil}
email = email.blank? ? "#{username}@example.org" : email
user = User.new(admin: false, login: username, mail: email, type: "User") user = User.new(admin: false, login: username, mail: email, type: "User")
user.password = password user.password = password
user.platform = platform user.platform = platform
user.phone = phone if phone.present?
user.nickname = nickname if nickname.present?
if need_edit_info if need_edit_info
user.need_edit_info user.need_edit_info
else else
user.activate user.activate
end end
return unless user.valid? return unless user.valid?
interactor = Gitea::RegisterInteractor.call({username: username, email: email, password: password}) interactor = Gitea::RegisterInteractor.call({username: username, email: email, password: password})
@ -67,7 +70,7 @@ module RegisterHelper
user.password = params[:password] user.password = params[:password]
user.mail = params[:email] user.mail = params[:email]
if user.save! if user.save!
sync_params = { sync_params = {
password: params[:password].to_s, password: params[:password].to_s,
email: params[:email], email: params[:email],
@ -75,9 +78,9 @@ module RegisterHelper
new_name: params[:username], new_name: params[:username],
source_id: 0 source_id: 0
} }
interactor = Gitea::User::UpdateInteractor.call(before_login, sync_params) interactor = Gitea::User::UpdateInteractor.call(before_login, sync_params)
if interactor.success? if interactor.success?
result[:user] = user result[:user] = user
else else
result[:message] = '用户同步Gitea失败!' result[:message] = '用户同步Gitea失败!'

View File

@ -7,7 +7,7 @@ class IssueTagsController < ApplicationController
def index def index
issue_tags = @project.issue_tags.reorder("#{order_name} #{order_type}") issue_tags = @project.issue_tags.includes(:issues).reorder("issue_tags.#{order_name} #{order_type}")
@user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user)) @user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user))
@page = params[:page] || 1 @page = params[:page] || 1
@limit = params[:limit] || 15 @limit = params[:limit] || 15

View File

@ -3,6 +3,7 @@ class Oauth::BaseController < ActionController::Base
include LoginHelper include LoginHelper
include ControllerRescueHandler include ControllerRescueHandler
include LoggerHelper include LoggerHelper
include RegisterHelper
# include LaboratoryHelper # include LaboratoryHelper
skip_before_action :verify_authenticity_token skip_before_action :verify_authenticity_token
@ -13,13 +14,13 @@ class Oauth::BaseController < ActionController::Base
private private
def tip_exception(status = -1, message) def tip_exception(status = -1, message)
raise Educoder::TipException.new(status, message) raise Gitlink::TipException.new(status, message)
end end
def tip_show_exception(status = -2, message) def tip_show_exception(status = -2, message)
raise Educoder::TipException.new(status, message) raise Gitlink::TipException.new(status, message)
end end
def tip_show(exception) def tip_show(exception)
uid_logger("Tip show status is #{exception.status}, message is #{exception.message}") uid_logger("Tip show status is #{exception.status}, message is #{exception.message}")
render json: exception.tip_json render json: exception.tip_json
@ -35,7 +36,7 @@ class Oauth::BaseController < ActionController::Base
end end
def auth_hash def auth_hash
Rails.logger.info("[OAuth2] omniauth.auth -> #{request.env['omniauth.auth'].inspect}") # Rails.logger.info("[OAuth2] omniauth.auth -> #{request.env['omniauth.auth'].inspect}")
request.env['omniauth.auth'] request.env['omniauth.auth']
end end

View File

@ -0,0 +1,93 @@
class Oauth::CallbacksController < Oauth::BaseController
def create
process_callback_new
rescue Exception => e
Rails.logger.info "授权失败:#{e}"
tip_exception("授权失败")
end
private
def config_providers
config = Rails.application.config_for(:configuration)
config.dig("oauth").keys
end
# QQ: {"ret":0,"msg":"","is_lost":0,"nickname":"颜值不算太高","gender":"男","gender_type":1,"province":"","city":"","year":"2013","constellation":"","figureurl":"http://qzapp.qlogo.cn/qzapp/101508858/0F860F4B329768F47B22341C5FD9089C/30","figureurl_1":"http://qzapp.qlogo.cn/qzapp/101508858/0F860F4B329768F47B22341C5FD9089C/50","figureurl_2":"http://qzapp.qlogo.cn/qzapp/101508858/0F860F4B329768F47B22341C5FD9089C/100","figureurl_qq_1":"http://thirdqq.qlogo.cn/g?b=oidb\u0026k=My3segFVHFqVmauibJQUltw\u0026s=40\u0026t=1568887757","figureurl_qq_2":"http://thirdqq.qlogo.cn/g?b=oidb\u0026k=My3segFVHFqVmauibJQUltw\u0026s=100\u0026t=1568887757","figureurl_qq":"http://thirdqq.qlogo.cn/g?b=oidb\u0026k=My3segFVHFqVmauibJQUltw\u0026s=140\u0026t=1568887757","figureurl_type":"1","is_yellow_vip":"0","vip":"0","yellow_vip_level":"0","level":"0","is_yellow_year_vip":"0"}
def process_callback
Rails.logger.info("[OAuth2] omniauth.auth -> #{request.env['omniauth.auth'].inspect}")
if auth_hash.blank?
redirect_to("/login") && return
end
new_user = false
platform = auth_hash[:provider]
uid = auth_hash[:uid]
mail = auth_hash.info.email || nil
nickname = ["gitee", "github"].include?(platform) ? auth_hash.info.name : auth_hash.info.nickname
open_user = "OpenUsers::#{platform.to_s.capitalize}".constantize.find_by(uid: uid)
if open_user.present? && open_user.user.present?
successful_authentication(open_user.user)
else
if current_user.blank? || !current_user.logged?
has_user = User.find_by(mail: mail)
if has_user.present?
"OpenUsers::#{platform.to_s.capitalize}".constantize.create!(user_id: has_user.id, uid: uid, extra: auth_hash.extra)
successful_authentication(has_user)
else
new_user = true
login = build_login_name(platform, auth_hash.info.nickname)
mail = "#{login}@example.org" if mail.blank?
code = %W(0 1 2 3 4 5 6 7 8 9)
rand_password = code.sample(10).join
reg_result = autologin_register(login, mail, rand_password, platform, nil, nickname)
Rails.logger.info("[OAuth2] omniauth.auth [reg_result] #{reg_result} ")
if reg_result[:message].blank?
open_user = "OpenUsers::#{platform.to_s.capitalize}".constantize.create!(user_id: reg_result[:user][:id], uid: uid, extra: auth_hash.extra)
successful_authentication(open_user.user)
else
tip_exception(reg_result.present? ? reg_result[:message] : "授权失败")
end
end
else
"OpenUsers::#{platform.to_s.capitalize}".constantize.create!(user: current_user, uid: login, extra: auth_hash.extra)
end
end
redirect_to root_path(new_user: new_user)
end
def process_callback_new
Rails.logger.info("[OAuth2] omniauth.auth -> #{request.env['omniauth.auth'].inspect}")
if auth_hash.blank?
redirect_to("/login") && return
end
platform = auth_hash[:provider]
uid = auth_hash[:uid]
uid = auth_hash.info.unionid if platform == "wechat"
open_user = "OpenUsers::#{platform.to_s.capitalize}".constantize.find_by(uid: uid)
if open_user.present? && open_user.user.present?
successful_authentication(open_user.user)
redirect_to root_path(new_user: false)
return
else
if current_user.blank? || !current_user.logged?
session[:unionid] = uid
else
"OpenUsers::#{platform.to_s.capitalize}".constantize.create!(user: current_user, uid: uid)
end
end
Rails.logger.info("[OAuth2] session[:unionid] -> #{session[:unionid]}")
redirect_to "/bindlogin/#{platform}"
end
# gitee,github nickname=login,如果系统未占用保留原用户名
def build_login_name(provider, nickname)
if ["gitee", "github"].include?(provider) && User.find_by(login: nickname).blank?
nickname
else
User.generate_user_login('p')
end
end
end

View File

@ -46,6 +46,12 @@ class Organizations::OrganizationsController < Organizations::BaseController
@organization.nickname = organization_params[:nickname] if organization_params[:nickname].present? @organization.nickname = organization_params[:nickname] if organization_params[:nickname].present?
@organization.save! @organization.save!
sync_organization_extension! sync_organization_extension!
# 更改组织可见性为私有,则需将该组织下的所有仓库同步更改为私有仓库
if organization_extension_params[:visibility] == "privacy"
Project.where(user_id: @organization.id).where(is_public: true).each do |project|
update_project_private(project)
end
end
Gitea::Organization::UpdateService.call(current_user.gitea_token, login, @organization.reload) Gitea::Organization::UpdateService.call(current_user.gitea_token, login, @organization.reload)
Util.write_file(@image, avatar_path(@organization)) if params[:image].present? Util.write_file(@image, avatar_path(@organization)) if params[:image].present?
@ -123,5 +129,20 @@ class Organizations::OrganizationsController < Organizations::BaseController
def sync_organization_extension! def sync_organization_extension!
@organization.organization_extension.update_attributes!(organization_extension_params) @organization.organization_extension.update_attributes!(organization_extension_params)
end end
def update_project_private(project)
project.update_attributes!(is_public: false)
project.forked_projects.update_all(is_public: project.is_public)
gitea_params = {
private: true,
default_branch: project.default_branch,
website: project.website,
name: project.identifier
}
gitea_repo = Gitea::Repository::UpdateService.call(@organization, project&.repository&.identifier, gitea_params)
project.repository.update_attributes({hidden: gitea_repo["private"], identifier: gitea_repo["name"]})
# 更新对应所属分类下的项目数量(私有)
project.project_category.decrement!(:private_projects_count, 1) if project.project_category.present?
end
end end

View File

@ -10,7 +10,7 @@ class ProjectCategoriesController < ApplicationController
end end
def group_list def group_list
@project_categories = ProjectCategory.where('projects_count > 0').order(projects_count: :desc) @project_categories = ProjectCategory.select("id, name, projects_count, private_projects_count, (projects_count - private_projects_count) as public_projects_count").having('public_projects_count > 0').order(public_projects_count: :desc)
# projects = Project.no_anomory_projects.visible # projects = Project.no_anomory_projects.visible
# @category_group_list = projects.joins(:project_category).group("project_categories.id", "project_categories.name").size # @category_group_list = projects.joins(:project_category).group("project_categories.id", "project_categories.name").size
end end

View File

@ -1,10 +1,10 @@
class ProjectRankController < ApplicationController class ProjectRankController < ApplicationController
# 根据时间获取热门项目 # 根据时间获取热门项目
def index def index
$redis_cache.zunionstore("recent-days-project-rank", get_timeable_key_names) $redis_cache.zunionstore("recent-days-project-rank-#{time}", get_timeable_key_names)
deleted_data = $redis_cache.smembers("v2-project-rank-deleted") deleted_data = $redis_cache.smembers("v2-project-rank-deleted")
$redis_cache.zrem("recent-days-project-rank", deleted_data) unless deleted_data.blank? $redis_cache.zrem("recent-days-project-rank-#{time}", deleted_data) unless deleted_data.blank?
@project_rank = $redis_cache.zrevrange("recent-days-project-rank", 0, 4, withscores: true) @project_rank = $redis_cache.zrevrange("recent-days-project-rank-#{time}", 0, 9, withscores: true)
rescue Exception => e rescue Exception => e
@project_rank = [] @project_rank = []
end end

View File

@ -40,8 +40,9 @@ class ProjectsController < ApplicationController
category_id = params[:category_id] category_id = params[:category_id]
@total_count = @total_count =
if category_id.blank? if category_id.blank?
ps = ProjectStatistic.first # ps = ProjectStatistic.first
ps.common_projects_count + ps.mirror_projects_count unless ps.blank? # ps.common_projects_count + ps.mirror_projects_count unless ps.blank?
@projects.total_count
else else
cate = ProjectCategory.find_by(id: category_id) cate = ProjectCategory.find_by(id: category_id)
cate&.projects_count || 0 cate&.projects_count || 0
@ -52,7 +53,7 @@ class ProjectsController < ApplicationController
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
Projects::CreateForm.new(project_params).validate! Projects::CreateForm.new(project_params).validate!
@project = Projects::CreateService.new(current_user, project_params).call @project = Projects::CreateService.new(current_user, project_params).call
OpenProjectDevOpsJob.perform_later(@project&.id, current_user.id)
end end
rescue Exception => e rescue Exception => e
uid_logger_error(e.message) uid_logger_error(e.message)
@ -154,6 +155,15 @@ class ProjectsController < ApplicationController
} }
gitea_repo = Gitea::Repository::UpdateService.call(@owner, @project&.repository&.identifier, gitea_params) gitea_repo = Gitea::Repository::UpdateService.call(@owner, @project&.repository&.identifier, gitea_params)
@project.repository.update_attributes({hidden: gitea_repo["private"], identifier: gitea_repo["name"]}) @project.repository.update_attributes({hidden: gitea_repo["private"], identifier: gitea_repo["name"]})
# 更新对应所属分类下的项目数量(私有)
before_is_public = @project.previous_changes[:is_public].present? ? @project.previous_changes[:is_public][0] : @project.is_public
after_is_public = @project.previous_changes[:is_public].present? ? @project.previous_changes[:is_public][1] : @project.is_public
before_pc_id = @project.previous_changes[:project_category_id].present? ? @project.previous_changes[:project_category_id][0] : @project.project_category_id
after_pc_id = @project.previous_changes[:project_category_id].present? ? @project.previous_changes[:project_category_id][1] : @project.project_category_id
before_pc = ProjectCategory.find_by_id(before_pc_id)
after_pc = ProjectCategory.find_by_id(after_pc_id)
before_pc.decrement!(:private_projects_count, 1) if before_pc.present? && !before_is_public
after_pc.increment!(:private_projects_count, 1) if after_pc.present? && !after_is_public
end end
SendTemplateMessageJob.perform_later('ProjectSettingChanged', current_user.id, @project&.id, @project.previous_changes.slice(:name, :description, :project_category_id, :project_language_id, :is_public, :identifier)) if Site.has_notice_menu? SendTemplateMessageJob.perform_later('ProjectSettingChanged', current_user.id, @project&.id, @project.previous_changes.slice(:name, :description, :project_category_id, :project_language_id, :is_public, :identifier)) if Site.has_notice_menu?
end end
@ -171,6 +181,8 @@ class ProjectsController < ApplicationController
Gitea::Repository::DeleteService.new(@project.owner, @project.identifier).call Gitea::Repository::DeleteService.new(@project.owner, @project.identifier).call
@project.destroy! @project.destroy!
@project.forked_projects.update_all(forked_from_project_id: nil) @project.forked_projects.update_all(forked_from_project_id: nil)
# 如果该项目有所属的项目分类以及为私有项目,需要更新对应数量
@project.project_category.decrement!(:private_projects_count, 1) if @project.project_category.present? && !@project.is_public
render_ok render_ok
end end
else else

View File

@ -100,20 +100,6 @@ class PullRequestsController < ApplicationController
Issues::UpdateForm.new({subject: params[:title], description: params[:body].blank? ? params[:body] : params[:body].b}).validate! Issues::UpdateForm.new({subject: params[:title], description: params[:body].blank? ? params[:body] : params[:body].b}).validate!
merge_params merge_params
@issue&.issue_tags_relates&.destroy_all if params[:issue_tag_ids].blank?
if params[:issue_tag_ids].present? && !@issue&.issue_tags_relates.where(issue_tag_id: params[:issue_tag_ids]).exists?
if params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size > 1
return normal_status(-1, "最多只能创建一个标记。")
elsif params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size == 1
@issue&.issue_tags_relates&.destroy_all
params[:issue_tag_ids].each do |tag|
IssueTagsRelate.create!(issue_id: @issue.id, issue_tag_id: tag)
end
else
return normal_status(-1, "请输入正确的标记。")
end
end
reviewers = User.where(id: params[:reviewer_ids]) reviewers = User.where(id: params[:reviewer_ids])
@pull_request.reviewers = reviewers @pull_request.reviewers = reviewers
@ -165,6 +151,8 @@ class PullRequestsController < ApplicationController
colsed = PullRequests::CloseService.call(@owner, @repository, @pull_request, current_user) colsed = PullRequests::CloseService.call(@owner, @repository, @pull_request, current_user)
if colsed === true if colsed === true
@pull_request.project_trends.create!(user: current_user, project: @project,action_type: ProjectTrend::CLOSE) @pull_request.project_trends.create!(user: current_user, project: @project,action_type: ProjectTrend::CLOSE)
# 合并请求下issue处理为关闭
@issue&.update_attributes!({status_id:5})
SendTemplateMessageJob.perform_later('PullRequestClosed', current_user.id, @pull_request.id) if Site.has_notice_menu? SendTemplateMessageJob.perform_later('PullRequestClosed', current_user.id, @pull_request.id) if Site.has_notice_menu?
normal_status(1, "已拒绝") normal_status(1, "已拒绝")
else else
@ -210,6 +198,8 @@ class PullRequestsController < ApplicationController
# @pull_request.project_trend_status! # @pull_request.project_trend_status!
@pull_request.project_trends.create!(user: current_user, project: @project,action_type: ProjectTrend::MERGE) @pull_request.project_trends.create!(user: current_user, project: @project,action_type: ProjectTrend::MERGE)
@issue&.custom_journal_detail("merge", "", "该合并请求已被合并", current_user&.id) @issue&.custom_journal_detail("merge", "", "该合并请求已被合并", current_user&.id)
# 合并请求下issue处理为关闭
@issue&.update_attributes!({status_id:5})
SendTemplateMessageJob.perform_later('PullRequestMerged', current_user.id, @pull_request.id) if Site.has_notice_menu? SendTemplateMessageJob.perform_later('PullRequestMerged', current_user.id, @pull_request.id) if Site.has_notice_menu?
normal_status(1, "合并成功") normal_status(1, "合并成功")
else else

View File

@ -7,6 +7,7 @@ class SettingsController < ApplicationController
get_sub_competitions get_sub_competitions
get_personal_menu get_personal_menu
get_third_party get_third_party
get_third_party_new
get_top_system_notification get_top_system_notification
end end
@ -67,6 +68,24 @@ class SettingsController < ApplicationController
url: EducoderOauth.oauth_url url: EducoderOauth.oauth_url
} }
end end
def get_third_party_new
@third_party_new = []
@third_party_new << {
name: 'educoder',
url: EducoderOauth.oauth_url,
method: 'get'
}
platform_url = Rails.application.config_for(:configuration)['platform_url']
config = Rails.application.config_for(:configuration)
(config.dig("oauth").keys - ["educoder", "wechat"]).each do |provider|
@third_party_new << {
name: provider,
url: "#{platform_url}/auth/#{provider}",
method: 'get'
}
end
end
def get_top_system_notification def get_top_system_notification
@top_system_notification = SystemNotification.is_top.first @top_system_notification = SystemNotification.is_top.first

View File

@ -2,24 +2,24 @@ class StatisticController < ApplicationController
# 平台概况 # 平台概况
def platform_profile def platform_profile
@platform_user_query = Statistic::PlatformUserQuery.new(params).call @platform_user_query = Statistic::PlatformUserQuery.new(params).call rescue [0, 0, 0]
@platform_project_query = Statistic::PlatformProjectQuery.new(params).call @platform_project_query = Statistic::PlatformProjectQuery.new(params).call rescue [0, 0, 0]
@platform_course_query = Statistic::PlatformCourseQuery.new(params).call @platform_course_query = Statistic::PlatformCourseQuery.new(params).call rescue [0, 0, 0]
end end
# 平台代码提交数据 # 平台代码提交数据
def platform_code def platform_code
@platform_pull_request_query = Statistic::PlatformPullRequestQuery.new(params).call @platform_pull_request_query = Statistic::PlatformPullRequestQuery.new(params).call rescue [0, 0]
@platform_commit_query = Statistic::PlatformCommitQuery.new(params,current_user).call @platform_commit_query = Statistic::PlatformCommitQuery.new(params,current_user).call rescue [0, 0]
end end
# 项目案例活跃度排行榜 # 项目案例活跃度排行榜
def active_project_rank def active_project_rank
@active_project_rank_query = Statistic::ActiveProjectRankQuery.new(params, current_user).call @active_project_rank_query = Statistic::ActiveProjectRankQuery.new(params, current_user).call rescue []
end end
# 开发者活跃度排行榜 # 开发者活跃度排行榜
def active_developer_rank def active_developer_rank
@active_developer_rank_query = Statistic::ActiveDeveloperRankQuery.new(params, current_user).call @active_developer_rank_query = Statistic::ActiveDeveloperRankQuery.new(params, current_user).call rescue []
end end
end end

View File

@ -57,6 +57,13 @@ class UsersController < ApplicationController
Cache::V2::OwnerCommonService.new(@user.id).read Cache::V2::OwnerCommonService.new(@user.id).read
end end
def action
if params[:action_id].present? && params[:action_type].present?
UserAction.create(:action_id => params[:action_id], :action_type => "#{params[:action_type]}", :user_id => User.current.id, :ip => request.remote_ip)
end
render_ok
end
def watch_users def watch_users
watchers = Watcher.watching_users(@user.id).includes(:user).order("watchers.created_at desc") watchers = Watcher.watching_users(@user.id).includes(:user).order("watchers.created_at desc")
if params[:search].present? if params[:search].present?

View File

@ -6,7 +6,7 @@ module Admins::ProjectsHelper
if owner.is_a?(User) if owner.is_a?(User)
link_to(project.owner&.real_name, "/#{project&.owner&.login}", target: '_blank') link_to(project.owner&.real_name, "/#{project&.owner&.login}", target: '_blank')
elsif owner.is_a?(Organization) elsif owner.is_a?(Organization)
link_to(project.owner&.real_name, "/organize/#{project&.owner&.login}", target: '_blank') link_to(project.owner&.real_name, "/#{project&.owner&.login}", target: '_blank')
else else
"" ""
end end

View File

@ -10,7 +10,7 @@ module RepositoriesHelper
end end
def download_type(str) 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 apk) default_type = %w(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 apk xlsx xls)
default_type.include?(str&.downcase) || str.blank? default_type.include?(str&.downcase) || str.blank?
end end
@ -91,7 +91,7 @@ module RepositoriesHelper
new_r_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw?filepath=#{path_current}/#{path_last}&ref=#{ref}"].join new_r_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw?filepath=#{path_current}/#{path_last}&ref=#{ref}"].join
end end
content = content.gsub(/src=\"#{r_content}\"/, "src=\"#{new_r_content}\"").gsub(/src='#{r_content}'/, "src=\"#{new_r_content}\"") content = content.gsub(/src=\"#{r_content}\"/, "src=\"#{new_r_content}\"").gsub(/src='#{r_content}'/, "src=\"#{new_r_content}\"")
rescue rescue
next next
end end
end end

View File

@ -62,7 +62,7 @@ module Gitea
file_params = {} file_params = {}
file_params = file_params.merge(branch: @params[:branch]) unless @params[:branch].blank? file_params = file_params.merge(branch: @params[:branch]) unless @params[:branch].blank?
file_params = file_params.merge(new_branch: @params[:new_branch]) unless @params[:new_branch].blank? file_params = file_params.merge(new_branch: @params[:new_branch]) unless @params[:new_branch].blank?
file_params = file_params.merge(content: Base64.encode64(@params[:content] || "")) file_params = file_params.merge(content: @params[:content] || "")
file_params = file_params.merge(message: @params[:message]) unless @params[:message].blank? file_params = file_params.merge(message: @params[:message]) unless @params[:message].blank?
file_params = file_params.merge(committer: @params[:committer]) file_params = file_params.merge(committer: @params[:committer])
file_params file_params

View File

@ -1,7 +1,7 @@
class MigrateRemoteRepositoryJob < ApplicationJob class MigrateRemoteRepositoryJob < ApplicationJob
queue_as :default queue_as :default
def perform(repo_id, token, params) def perform(repo_id, token, user_id, params)
repo = Repository.find_by(id: repo_id) repo = Repository.find_by(id: repo_id)
return if repo.blank? return if repo.blank?
@ -12,6 +12,10 @@ class MigrateRemoteRepositoryJob < ApplicationJob
if gitea_repository[0]==201 if gitea_repository[0]==201
repo&.project&.update_columns(gpid: gitea_repository[2]["id"]) repo&.project&.update_columns(gpid: gitea_repository[2]["id"])
repo&.mirror&.succeeded! repo&.mirror&.succeeded!
## open jianmu devops
project_id = repo&.project&.id
puts "############ mirror project_id,user_id: #{project_id},#{user_id} ############"
OpenProjectDevOpsJob.perform_later(project_id, user_id) if project_id.present? && user_id.present?
puts "############ mirror status: #{repo.mirror.status} ############" puts "############ mirror status: #{repo.mirror.status} ############"
else else
repo&.mirror&.failed! repo&.mirror&.failed!

View File

@ -0,0 +1,16 @@
class OpenProjectDevOpsJob < ApplicationJob
include ProjectsHelper
queue_as :message
def perform(project_id, user_id)
project = Project.find_by(id: project_id)
user = User.find_by(id: user_id)
code = jianmu_devops_code(project, user)
uri = URI.parse("#{jianmu_devops_url}/activate?code=#{URI.encode_www_form_component(code)}")
response = Net::HTTP.get_response(uri)
puts "jianmu_devops_url response.code ===== #{response.code}"
SendTemplateMessageJob.perform_later('ProjectOpenDevOps', user_id, project_id)
end
end

View File

@ -217,6 +217,14 @@ class SendTemplateMessageJob < ApplicationJob
receivers = project&.all_managers.where.not(id: operator&.id) receivers = project&.all_managers.where.not(id: operator&.id)
receivers_string, content, notification_url = MessageTemplate::ProjectPraised.get_message_content(receivers, operator, project) receivers_string, content, notification_url = MessageTemplate::ProjectPraised.get_message_content(receivers, operator, project)
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, project_id: project.id}) Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, project_id: project.id})
when 'ProjectOpenDevOps'
operator_id, project_id = args[0], args[1]
operator = User.find_by_id(operator_id)
project = Project.find_by_id(project_id)
return unless operator.present? && project.present?
receivers = User.where(id: operator.id)
receivers_string, content, notification_url = MessageTemplate::ProjectOpenDevOps.get_message_content(receivers, operator, project)
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, project_id: project.id})
when 'ProjectPullRequest' when 'ProjectPullRequest'
operator_id, pull_request_id = args[0], args[1] operator_id, pull_request_id = args[0], args[1]
operator = User.find_by_id(operator_id) operator = User.find_by_id(operator_id)

View File

@ -15,7 +15,7 @@ module EducoderOauth::Service
result result
rescue Exception => e rescue Exception => e
raise Educoder::TipException.new(e.message) raise Gitlink::TipException.new(e.message)
end end
end end
@ -27,7 +27,7 @@ module EducoderOauth::Service
result = client.auth_code.get_token(code, redirect_uri: EducoderOauth.redirect_uri).to_hash result = client.auth_code.get_token(code, redirect_uri: EducoderOauth.redirect_uri).to_hash
return result return result
rescue Exception => e rescue Exception => e
raise Educoder::TipException.new(e.message) raise Gitlink::TipException.new(e.message)
end end
end end

View File

@ -1,7 +1,8 @@
class UserMailer < ApplicationMailer class UserMailer < ApplicationMailer
# 注意:这个地方一定要和你的邮箱服务域名一致 # 注意:这个地方一定要和你的邮箱服务域名一致
# default from: 'notification@trustie.org' # default from: 'notification@trustie.org'
default from: 'noreply@gitlink.org.cn' # default from: 'noreply@gitlink.org.cn'
default from: 'GitLink <noreply@gitlink.org.cn>'
# 用户注册验证码 # 用户注册验证码
def register_email(mail, code) def register_email(mail, code)
@ -9,8 +10,24 @@ class UserMailer < ApplicationMailer
mail(to: mail, subject: 'Gitink | 注册验证码') mail(to: mail, subject: 'Gitink | 注册验证码')
end end
# 用户找回密码
def find_password(mail, code)
@code = code
mail(to: mail, subject: 'Gitink | 找回密码验证码')
end
# 用户绑定邮箱
def bind_email(mail, code)
@code = code
mail(to: mail, subject: 'Gitink | 绑定邮箱验证码')
end
def update_email(mail, code) def update_email(mail, code)
@code = code @code = code
mail(to: mail, subject: 'Gitink | 更改邮箱验证码') mail(to: mail, subject: 'Gitink | 更改邮箱验证码')
end end
def feedback_email(mail, title, content)
mail(to: mail, subject: title, content_type: "text/html", body: content)
end
end end

21
app/models/feedback.rb Normal file
View File

@ -0,0 +1,21 @@
# == Schema Information
#
# Table name: feedbacks
#
# id :integer not null, primary key
# user_id :integer
# content :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_feedbacks_on_user_id (user_id)
#
class Feedback < ApplicationRecord
belongs_to :user
has_many :feedback_message_histories, dependent: :destroy
end

View File

@ -0,0 +1,36 @@
# == Schema Information
#
# Table name: feedback_message_histories
#
# id :integer not null, primary key
# feedback_id :integer
# user_id :integer
# title :string(255)
# content :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_feedback_message_histories_on_feedback_id (feedback_id)
# index_feedback_message_histories_on_user_id (user_id)
#
class FeedbackMessageHistory < ApplicationRecord
belongs_to :feedback
belongs_to :user
before_validation :send_meessage_email, on: :create
private
def send_meessage_email
unless UserMailer.feedback_email(feedback&.user&.mail, title, content).deliver_now
errors[:title] << '邮件发送失败!'
end
rescue
errors[:title] << '邮件发送失败!'
end
end

View File

@ -52,6 +52,7 @@ class MessageTemplate < ApplicationRecord
email_html = File.read("#{email_template_html_dir}/project_milestone_completed.html") email_html = File.read("#{email_template_html_dir}/project_milestone_completed.html")
self.create(type: 'MessageTemplate::ProjectMilestoneCompleted', sys_notice: '在 <b>{nickname}/{repository}</b> 仓库,里程碑 <b>{name}</b> 的完成度已达到100%', notification_url: '{baseurl}/{owner}/{identifier}/milestones/{id}', email: email_html, email_title: "#{PLATFORM}: 仓库 {nickname}/{repository} 有里程碑已完成") self.create(type: 'MessageTemplate::ProjectMilestoneCompleted', sys_notice: '在 <b>{nickname}/{repository}</b> 仓库,里程碑 <b>{name}</b> 的完成度已达到100%', notification_url: '{baseurl}/{owner}/{identifier}/milestones/{id}', email: email_html, email_title: "#{PLATFORM}: 仓库 {nickname}/{repository} 有里程碑已完成")
self.create(type: 'MessageTemplate::ProjectPraised', sys_notice: '<b>{nickname1}</b> 点赞了你管理的仓库 <b>{nickname2}/{repository}</b>', notification_url: '{baseurl}/{login}') self.create(type: 'MessageTemplate::ProjectPraised', sys_notice: '<b>{nickname1}</b> 点赞了你管理的仓库 <b>{nickname2}/{repository}</b>', notification_url: '{baseurl}/{login}')
self.create(type: 'MessageTemplate::ProjectOpenDevOps', sys_notice: '您的仓库 <b>{repository}</b> 已成功开通引擎服务,可通过简单的节点编排完成自动化集成与部署。欢迎体验!', notification_url: '{baseurl}/{owner}/{identifier}/devops')
email_html = File.read("#{email_template_html_dir}/project_pull_request.html") email_html = File.read("#{email_template_html_dir}/project_pull_request.html")
self.create(type: 'MessageTemplate::ProjectPullRequest', sys_notice: '{nickname1}在 <b>{nickname2}/{repository}</b> 提交了一个合并请求:<b>{title}</b>', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}', email: email_html, email_title: "#{PLATFORM}: {nickname1} 在 {nickname2}/{repository} 提交了一个合并请求") self.create(type: 'MessageTemplate::ProjectPullRequest', sys_notice: '{nickname1}在 <b>{nickname2}/{repository}</b> 提交了一个合并请求:<b>{title}</b>', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}', email: email_html, email_title: "#{PLATFORM}: {nickname1} 在 {nickname2}/{repository} 提交了一个合并请求")
email_html = File.read("#{email_template_html_dir}/project_role.html") email_html = File.read("#{email_template_html_dir}/project_role.html")

View File

@ -0,0 +1,28 @@
# == 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::ProjectOpenDevOps < MessageTemplate
# MessageTemplate::ProjectOpenDevOps.get_message_content(User.where(login: 'yystopf'))
def self.get_message_content(receivers, user, project)
return '', '', '' if receivers.blank?
content = sys_notice.gsub('{repository}', project&.name)
url = notification_url.gsub('{owner}', project&.owner&.login).gsub('{identifier}', project&.identifier)
return receivers_string(receivers), content, url
rescue => e
Rails.logger.info("MessageTemplate::ProjectOpenDevOps.get_message_content [ERROR] #{e}")
return '', '', ''
end
end

View File

@ -0,0 +1,27 @@
# == Schema Information
#
# Table name: open_users
#
# id :integer not null, primary key
# user_id :integer
# type :string(255)
# uid :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# extra :text(65535)
#
# Indexes
#
# index_open_users_on_type_and_uid (type,uid) UNIQUE
# index_open_users_on_user_id (user_id)
#
class OpenUsers::Gitee < OpenUser
def nickname
extra&.[]('nickname')
end
def en_type
'gitee'
end
end

View File

@ -0,0 +1,27 @@
# == Schema Information
#
# Table name: open_users
#
# id :integer not null, primary key
# user_id :integer
# type :string(255)
# uid :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# extra :text(65535)
#
# Indexes
#
# index_open_users_on_type_and_uid (type,uid) UNIQUE
# index_open_users_on_user_id (user_id)
#
class OpenUsers::Github < OpenUser
def nickname
extra&.[]('name')
end
def en_type
'github'
end
end

View File

@ -16,7 +16,7 @@
# index_open_users_on_user_id (user_id) # index_open_users_on_user_id (user_id)
# #
class OpenUsers::QQ < OpenUser class OpenUsers::Qq < OpenUser
def nickname def nickname
extra&.[]('nickname') extra&.[]('nickname')
end end

View File

@ -68,4 +68,9 @@ class Owner < ApplicationRecord
has_many :repositories, foreign_key: :user_id, dependent: :destroy has_many :repositories, foreign_key: :user_id, dependent: :destroy
has_many :applied_transfer_projects, dependent: :destroy has_many :applied_transfer_projects, dependent: :destroy
scope :like, lambda { |keywords|
sql = "CONCAT(lastname, firstname) LIKE :search OR nickname LIKE :search OR login LIKE :search "
where(sql, :search => "%#{keywords.strip}%") unless keywords.blank?
}
end end

View File

@ -175,6 +175,9 @@ class Project < ApplicationRecord
$redis_cache.srem("v2-project-rank-deleted", self.id) $redis_cache.srem("v2-project-rank-deleted", self.id)
end end
end end
if !self.common?
CacheAsyncClearJob.perform_later('project_rank_service', self.id)
end
end end
def decre_project_common def decre_project_common

View File

@ -2,14 +2,15 @@
# #
# Table name: project_categories # Table name: project_categories
# #
# id :integer not null, primary key # id :integer not null, primary key
# name :string(255) # name :string(255)
# position :integer # position :integer
# projects_count :integer default("0") # projects_count :integer default("0")
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# ancestry :string(255) # ancestry :string(255)
# pinned_index :integer default("0") # pinned_index :integer default("0")
# private_projects_count :integer default("0")
# #
# Indexes # Indexes
# #

View File

@ -114,13 +114,13 @@ class User < Owner
# trustie: 来自Trustie平台 # trustie: 来自Trustie平台
# forge: 平台本身注册的用户 # forge: 平台本身注册的用户
# military: 军科的用户 # military: 军科的用户
enumerize :platform, in: [:forge, :educoder, :trustie, :military], default: :forge, scope: :shallow enumerize :platform, in: [:forge, :educoder, :trustie, :military, :github, :gitee, :qq, :wechat], default: :forge, scope: :shallow
belongs_to :laboratory, optional: true belongs_to :laboratory, optional: true
has_one :user_extension, dependent: :destroy has_one :user_extension, dependent: :destroy
has_many :open_users, dependent: :destroy has_many :open_users, dependent: :destroy
has_one :wechat_open_user, class_name: 'OpenUsers::Wechat' has_one :wechat_open_user, class_name: 'OpenUsers::Wechat'
has_one :qq_open_user, class_name: 'OpenUsers::QQ' has_one :qq_open_user, class_name: 'OpenUsers::Qq'
accepts_nested_attributes_for :user_extension, update_only: true accepts_nested_attributes_for :user_extension, update_only: true
has_many :fork_users, dependent: :destroy has_many :fork_users, dependent: :destroy
@ -177,6 +177,7 @@ class User < Owner
has_one :trace_user, dependent: :destroy has_one :trace_user, dependent: :destroy
has_many :user_trace_tasks, dependent: :destroy has_many :user_trace_tasks, dependent: :destroy
has_many :feedbacks, dependent: :destroy
# Groups and active users # Groups and active users
scope :active, lambda { where(status: [STATUS_ACTIVE, STATUS_EDIT_INFO]) } scope :active, lambda { where(status: [STATUS_ACTIVE, STATUS_EDIT_INFO]) }
scope :like, lambda { |keywords| scope :like, lambda { |keywords|
@ -192,7 +193,8 @@ class User < Owner
:show_email, :show_location, :show_department, :super_description, :show_super_description, :show_email, :show_location, :show_department, :super_description, :show_super_description,
:technical_title, :province, :city, :custom_department, to: :user_extension, allow_nil: true :technical_title, :province, :city, :custom_department, to: :user_extension, allow_nil: true
before_save :update_hashed_password, :set_lastname # before_save :update_hashed_password, :set_lastname
before_save :update_hashed_password
after_save :reset_cache_data after_save :reset_cache_data
after_create do after_create do
SyncTrustieJob.perform_later("user", 1) if allow_sync_to_trustie? SyncTrustieJob.perform_later("user", 1) if allow_sync_to_trustie?
@ -558,17 +560,19 @@ class User < Owner
def full_name def full_name
return '游客' unless logged? return '游客' unless logged?
name = show_realname? ? lastname + firstname : nickname # name = show_realname? ? lastname + firstname : nickname
name.blank? ? (nickname.blank? ? login : nickname) : name # name.blank? ? (nickname.blank? ? login : nickname) : name
nickname.blank? ? login : nickname
end end
# 用户的真实姓名(不考虑用户是否隐藏了真实姓名,课堂模块都用真实姓名) # 用户的真实姓名(不考虑用户是否隐藏了真实姓名,课堂模块都用真实姓名)
def real_name def real_name
return '游客' unless logged? return '游客' unless logged?
name = lastname + firstname # name = lastname + firstname
name = name.blank? ? (nickname.blank? ? login : nickname) : name # name = name.blank? ? (nickname.blank? ? login : nickname) : name
# name.gsub(/\s+/, '').strip #6.11 -hs # name.gsub(/\s+/, '').strip #6.11 -hs
name.strip # name.strip
nickname.blank? ? login : nickname
end end
def only_real_name def only_real_name
@ -701,12 +705,13 @@ class User < Owner
end end
def show_real_name def show_real_name
name = lastname + firstname # name = lastname + firstname
if name.blank? # if name.blank?
nickname.blank? ? login : nickname # nickname.blank? ? login : nickname
else # else
name # name
end # end
nickname.blank? ? login : nickname
end end
def update_hashed_password def update_hashed_password
@ -787,6 +792,15 @@ class User < Owner
login login
end end
# 生成数字账号
CODES = %W(0 1 2 3 4 5 6 7 8 9)
def self.generate_user_login type
code = CODES.sample(8).join
code = type + code.to_s
return User.generate_user_login(type) if User.where(login: code).present?
code
end
def bind_open_user?(type) def bind_open_user?(type)
case type case type
when 'wechat' then wechat_open_user.present? when 'wechat' then wechat_open_user.present?

View File

@ -12,11 +12,16 @@ class Projects::ListQuery < ApplicationQuery
def call def call
collection = Project.visible collection = Project.visible
# 增加私有组织中项目过滤
collection = collection.joins("left join organization_extensions on organization_extensions.organization_id = projects.user_id")
.where("organization_extensions.visibility is null or organization_extensions.visibility in (0,1)")
.where("projects.user_id > 0")
collection = filter_projects(collection) collection = filter_projects(collection)
sort = params[:sort_by] || "updated_on" sort = params[:sort_by] || "updated_on"
sort_direction = params[:sort_direction] || "desc" sort_direction = params[:sort_direction] || "desc"
collection = optimize_sorting(collection, sort) if params[:category_id].present?
custom_sort(collection, sort, sort_direction) custom_sort(collection, sort, sort_direction)
# scope = scope.reorder("projects.#{sort} #{sort_direction}") # scope = scope.reorder("projects.#{sort} #{sort_direction}")
@ -36,10 +41,11 @@ class Projects::ListQuery < ApplicationQuery
ids = Projects::ElasticsearchService.call(params[:search]) ids = Projects::ElasticsearchService.call(params[:search])
items = items.where(platform: 'forge') items = items.where(platform: 'forge')
if ids.present? if ids.present?
items.where(id: ids).by_name_or_identifier(params[:search]) items = items.where(id: ids).by_name_or_identifier(params[:search])
else else
items.by_name_or_identifier(params[:search]) items = items.by_name_or_identifier(params[:search])
end end
items.or(items.where(user_id: Owner.like(params[:search]).pluck(:id)))
end end
def by_project_type(items) def by_project_type(items)
@ -57,5 +63,20 @@ class Projects::ListQuery < ApplicationQuery
def by_pinned(items) def by_pinned(items)
(params[:pinned].present? && params[:category_id].present?) ? items.pinned : items (params[:pinned].present? && params[:category_id].present?) ? items.pinned : items
end end
# 优化排序
def optimize_sorting(relations, sort_by)
if sort_by == "updated_on"
relations.where("projects.updated_on>'2010-01-01'")
elsif sort_by == "created_on"
relations.where("projects.created_on>'2010-01-01'")
elsif sort_by == "forked_count"
relations.where("projects.forked_count>=0")
elsif sort_by == "praises_count"
relations.where("projects.praises_count>=0")
else
relations
end
end
end end

View File

@ -15,13 +15,14 @@ class Admins::UpdateUserService < ApplicationService
user.firstname = '' user.firstname = ''
user.password = params[:password] if params[:password].present? user.password = params[:password] if params[:password].present?
user.user_extension.assign_attributes(user_extension_attributes) user.user_extension.assign_attributes(user_extension_attributes) if user.user_extension.present?
old_login = user.login old_login = user.login
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
user.save! user.save!
user.user_extension.save! user.user_extension.save! if user.user_extension.present?
update_gitea_user(old_login) update_gitea_user(old_login)
update_gitea_user_email(user.previous_changes[:mail])
end end
user user
@ -65,4 +66,14 @@ class Admins::UpdateUserService < ApplicationService
Util.logger_error(ex) Util.logger_error(ex)
raise Error, '保存失败' raise Error, '保存失败'
end end
def update_gitea_user_email(change_options)
return if change_options.blank?
return if user.gitea_uid.blank? || user.gitea_token.blank?
$gitea_client.delete_user_emails({body: {emails: [change_options[0]]}.to_json, query: {access_token: user.gitea_token}})
$gitea_client.post_user_emails({body: {emails: [change_options[1]]}.to_json, query: {access_token: user.gitea_token}})
rescue Exception => ex
Util.logger_error(ex)
raise Error, '保存失败'
end
end end

View File

@ -0,0 +1,34 @@
class Api::V1::Projects::CodeStats::ListService < ApplicationService
attr_reader :project, :ref, :owner, :repo, :token
attr_accessor :gitea_data
def initialize(project, params, token=nil)
@project = project
@ref = params[:ref]
@owner = project&.owner.login
@repo = project&.identifier
@token = token
end
def call
load_gitea_data
gitea_data
end
private
def request_params
param = {
access_token: token
}
param.merge!(ref: ref) if ref.present?
param
end
def load_gitea_data
@gitea_data = $gitea_client.get_repos_code_stats_by_owner_repo(owner, repo, {query: request_params}) rescue nil
raise Error, '获取贡献者贡献度失败!' unless @gitea_data.is_a?(Hash)
end
end

View File

@ -0,0 +1,26 @@
class Api::V1::Users::Feedbacks::CreateService < ApplicationService
include ActiveModel::Model
attr_reader :user, :content
attr_accessor :feedback
validates :content, presence: true
def initialize(user, params)
@user = user
@content = params[:content]
end
def call
raise Error, errors.full_messages.join(",") unless valid?
begin
@feedback = Feedback.new(user: user, content: content)
@feedback.save!
return @feedback.valid? ? @feedback : nil
rescue
raise Error, "服务器错误,请联系系统管理员!"
end
end
end

View File

@ -14,7 +14,7 @@ class Api::V1::Users::UpdateEmailService < ApplicationService
@mail = params[:email] @mail = params[:email]
@old_mail = user.mail @old_mail = user.mail
@code = params[:code] @code = params[:code]
@verify_code = VerificationCode.where(email: @mail, code: @code, code_type: 10).last @verify_code = VerificationCode.where(email: @mail, code_type: 10).last
end end
def call def call

View File

@ -0,0 +1,35 @@
class Api::V1::Users::UpdatePhoneService < ApplicationService
include ActiveModel::Model
attr_reader :user, :password, :phone, :code, :verify_code
validates :password, :code, presence: true
validates :phone, presence: true, format: { with: CustomRegexp::PHONE }
def initialize(user, params)
@user = user
@password = params[:password]
@phone = params[:phone]
@code = params[:code]
@verify_code = VerificationCode.where(phone: @phone, code_type: 4).last
end
def call
raise Error, errors.full_messages.join(",") unless valid?
raise Error, "密码不正确." unless @user.check_password?(@password)
exist_owner = Owner.find_by(phone: @phone)
raise Error, "手机号已被使用." if exist_owner
is_debug = @code == "123123" && EduSetting.get("code_debug") # 万能验证码,用于测试 # TODO 万能验证码,用于测试
raise Error, "验证码不正确." if @verify_code&.code != @code && !is_debug
raise Error, "验证码已失效." if !@verify_code&.effective? && !is_debug
begin
ActiveRecord::Base.transaction do
@user.update_attributes!({phone: @phone})
end
return true
rescue
raise Error, "服务器错误,请联系系统管理员!"
end
end
end

View File

@ -56,7 +56,7 @@ class Gitea::Repository::Entries::CreateService < Gitea::ClientService
when 403 then error("你没有权限操作!") when 403 then error("你没有权限操作!")
when 404 then error("你操作的链接不存在!") when 404 then error("你操作的链接不存在!")
when 422 when 422
if @body[:new_branch].include?('/') || @body[:new_branch].include?('\'') || @body[:new_branch].include?('^') || @body[:new_branch].include?('*') if @body[:new_branch].present? && (@body[:new_branch].include?('/') || @body[:new_branch].include?('\'') || @body[:new_branch].include?('^') || @body[:new_branch].include?('*'))
error("不合法的分支名称!") error("不合法的分支名称!")
else else
error("#{filepath}文件已存在,不能重复创建!") error("#{filepath}文件已存在,不能重复创建!")

View File

@ -17,7 +17,11 @@ class Issues::ListQueryService < ApplicationService
issues = all_issues.issue_index_includes issues = all_issues.issue_index_includes
issues = issues.includes(pull_request: :reviewers) issues = issues.includes(pull_request: :reviewers)
if status_type.to_s == "2" #表示关闭中的 if status_type.to_s == "2" #表示关闭中的
issues = issues.where(status_id: 5) if(select_type == "Issue")
issues = issues.where(status_id: 5)
else
issues = issues.joins(:pull_request).where(pull_requests: {status: 2})
end
elsif status_type.to_s == "1" elsif status_type.to_s == "1"
if(select_type == "Issue") if(select_type == "Issue")
issues = issues.where.not(status_id: 5) #默认显示开启中的 issues = issues.where.not(status_id: 5) #默认显示开启中的

View File

@ -10,7 +10,7 @@ class Oauth::CreateOrFindQqAccountService < ApplicationService
def call def call
new_user = false new_user = false
# 存在该用户 # 存在该用户
open_user = OpenUsers::QQ.find_by(uid: params['uid']) open_user = OpenUsers::Qq.find_by(uid: params['uid'])
return [open_user.user, new_user] if open_user.present? return [open_user.user, new_user] if open_user.present?
if user.blank? || !user.logged? if user.blank? || !user.logged?
@ -32,7 +32,7 @@ class Oauth::CreateOrFindQqAccountService < ApplicationService
Util.download_file(params.dig('info', 'image'), avatar_path) Util.download_file(params.dig('info', 'image'), avatar_path)
end end
new_open_user = OpenUsers::QQ.create!(user: user, uid: params['uid']) new_open_user = OpenUsers::Qq.create!(user: user, uid: params['uid'])
Rails.cache.write(new_open_user.can_bind_cache_key, 1, expires_in: 1.hours) if new_user # 方便后面进行账号绑定 Rails.cache.write(new_open_user.can_bind_cache_key, 1, expires_in: 1.hours) if new_user # 方便后面进行账号绑定
end end

View File

@ -16,6 +16,7 @@ class Projects::CreateService < ApplicationService
Project.update_common_projects_count! Project.update_common_projects_count!
ProjectUnit.init_types(@project.id) ProjectUnit.init_types(@project.id)
Repositories::CreateService.new(user, @project, repository_params).call Repositories::CreateService.new(user, @project, repository_params).call
upgrade_project_category_private_projects_count
else else
Rails.logger.info("#############___________create_project_erros______###########{@project.errors.messages}") Rails.logger.info("#############___________create_project_erros______###########{@project.errors.messages}")
end end
@ -28,6 +29,14 @@ class Projects::CreateService < ApplicationService
private private
def upgrade_project_category_private_projects_count
# 如果为空或者项目为公有项目直接返回
return unless params[:project_category_id].present?
return if repo_is_public
project_category = ProjectCategory.find_by_id(params[:project_category_id])
project_category.increment!(:private_projects_count, 1)
end
def authroize_user_id_success def authroize_user_id_success
(user.id == params[:user_id].to_i) || (user.organizations.find_by_id(params[:user_id]).present?) (user.id == params[:user_id].to_i) || (user.organizations.find_by_id(params[:user_id]).present?)
end end

View File

@ -11,7 +11,7 @@ class Repositories::MigrateService < ApplicationService
@repository = Repository.new(repository_params) @repository = Repository.new(repository_params)
if @repository.save! if @repository.save!
@repository.set_mirror! @repository.set_mirror!
MigrateRemoteRepositoryJob.perform_later(@repository.id, user.gitea_token, gitea_repository_params) MigrateRemoteRepositoryJob.perform_later(@repository.id, user.gitea_token, user.id, gitea_repository_params)
end end
@repository @repository
rescue => e rescue => e

View File

@ -0,0 +1,112 @@
class Sms::UcloudService < ApplicationService
attr_reader :phone, :code, :send_type
def initialize(phone, code, send_type)
@phone = phone
@code = code
@send_type = send_type
end
def call
public_key = EduSetting.get("ucloud_public_key") || "4Z7QYDY0SumplMtmNmd9PERgPPFiMpR1R"
private_key = EduSetting.get("ucloud_private_key") || "7wxMoGoaQ1DtcQjDxgJrOGOXnIiZq4amEWvmi7eBtm2d"
project_id = "org-3ozbh2"
sign_params = {
"Action" => "SendUSMSMessage",
"ProjectId" => project_id,
"TemplateId" => get_template_id(@send_type),
"PublicKey" => public_key,
"PhoneNumbers.0" => @phone,
"TemplateParams.0" => "#{@code}",
"SigContent" => "GitLink确实开源"
}
sequence = sign_params.sort.map { |k, v| "#{k}#{v}" }.join('')
# Rails.logger.info("create_signature=========#{sequence}#{private_key}")
req_params = sign_params.merge("Signature" => Digest::SHA1.hexdigest("#{sequence}#{private_key}"))
uri = URI("https://api.ucloud.cn")
uri.query = req_params.map { |k, v| "#{k}=#{URI.escape(v.to_s)}" }.join('&')
# Rails.logger.info("uri.query=========#{uri.query}")
Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
req = Net::HTTP::Get.new uri.request_uri
response = http.request(req)
# Rails.logger.info("ucloud sms response.body=========#{response.body}")
result = ActiveSupport::JSON.decode(response.body)
result['RetCode']
end
end
def send_by_params(opt={})
public_key = "4Z7QYDY0SumplMtmNmd9PERgPPFiMpRR"
private_key = "7wxMoGoaQ1DtcQjDxgJrOGOXnIiZq4amEWvmi7eBtmd"
project_id = "org-3ozbh2"
sign_params = {
"Action" => "SendUSMSMessage",
"ProjectId" => project_id,
"TemplateId" => "#{opt[:TemplateId]}",
"PublicKey" => public_key,
"PhoneNumbers.0" => "#{opt[:PhoneNumbers]}",
"TemplateParams.0" => "#{opt[:TemplateParams]}",
"SigContent" => "GitLink确实开源"
}
sequence = sign_params.sort.map { |k, v| "#{k}#{v}" }.join('')
# Rails.logger.info("create_signature=========#{sequence}#{private_key}")
req_params = sign_params.merge("Signature" => Digest::SHA1.hexdigest("#{sequence}#{private_key}"))
uri = URI("https://api.ucloud.cn")
uri.query = req_params.map { |k, v| "#{k}=#{URI.escape(v.to_s)}" }.join('&')
# Rails.logger.info("uri.query=========#{uri.query}")
Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
req = Net::HTTP::Get.new uri.request_uri
response = http.request(req)
# Rails.logger.info("ucloud sms response.body=========#{response.body}")
ActiveSupport::JSON.decode(response.body)
end
end
def send_sms(template_id)
end
def GetProjectList
public_key = "4Z7QYDY0SumplMtmNmd9PERgPPFiMpRR"
private_key = "7wxMoGoaQ1DtcQjDxgJrOGOXnIiZq4amEWvmi7eBtmd"
sign_params = {
"Action" => "GetProjectList",
"PublicKey" => public_key
}
sequence = sign_params.sort.map { |k, v| "#{k}#{v}" }.join('')
Rails.logger.info("create_signature=========#{sequence}#{private_key}")
req_params = sign_params.merge("Signature" => Digest::SHA1.hexdigest("#{sequence}#{private_key}"))
uri = URI("https://api.ucloud.cn")
uri.query = req_params.map { |k, v| "#{k}=#{URI.escape(v.to_s)}" }.join('&')
Rails.logger.info("uri.query=========#{uri.query}")
Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
req = Net::HTTP::Get.new uri.request_uri
response = http.request(req)
Rails.logger.info("ucloud sms response.body=========#{response.body}")
response.body
end
end
# 1注册手机验证码 2找回密码手机验证码 4绑定手机 9验证手机号有效
def get_template_id(send_type)
case send_type
when 1, 2, 9
"UTA221114S2MGTY"
when 4
"UTA22112486FXLZ"
else
"UTA221114S2MGTY"
end
end
end

View File

@ -0,0 +1,39 @@
<div class="modal fade feedback-history-change-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document" style="max-width: 800px">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">发送邮件给<%= @feedback&.user&.mail%></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<%= form_for @feedback_message_history, url: {controller: "feedbacks", action: "create_history"} do |p| %>
<div class="modal-body">
<div class="form-group">
<label>
邮件标题 <span class="ml10 color-orange mr20">*</span>
</label>
<%= p.text_field :title,class: "form-control input-lg", placeholder: "邮件标题", value: "您提交的意见反馈有新回复",required: true, maxlength: 64, readonly: true%>
</div>
<div class="form-group">
<label>
<span class="color-grey-6 pt10">
邮件正文
<span class="ml10 color-orange mr20">*</span>
</span>
</label>
<div class="mt-10">
<div class="pl-0 my-3 setting-item-body" id="feedback-history-email-editor">
<%= p.text_area :content, class:"form-control", style: 'display: none;', rows: "10", cols: "20", placeholer: "请输入邮件正文" %>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<%= p.submit "确认", class: "btn btn-primary submit-btn" %>
</div>
<% end %>
</div>
</div>
</div>

View File

@ -0,0 +1,46 @@
<table class="table table-hover text-center subject-list-table">
<thead class="thead-light">
<tr>
<th width="5%">序号</th>
<th width="10%">用户名</th>
<th width="20%">用户邮箱</th>
<th width="20%"><%= sort_tag('创建时间', name: 'created_at', path: admins_feedbacks_path) %></th>
<th width="25%">反馈意见</th>
<th width="15%">操作</th>
</tr>
</thead>
<tbody>
<% if feedbacks.present? %>
<% feedbacks.each_with_index do |feedback, index| %>
<tr class="feedback-item-<%= feedback.id %>">
<td><%= list_index_no((params[:page] || 1).to_i, index) %></td>
<td><%= feedback&.user&.login%></td>
<td><%= feedback&.user&.mail%></td>
<td><%= feedback.created_at&.strftime('%Y-%m-%d %H:%M') %></td>
<td>
<span class="d-inline-block" tabindex="0" data-toggle="tooltip" data-placement="left" title="<%= feedback.content%>">
<a href="javascript:">
<%= feedback.content.truncate(20) %>
</a>
</span>
</td>
<td class="action-container">
<%= link_to "发送邮件", new_history_admins_feedback_path(feedback), remote: true, class: "action" %>
<%#= link_to "删除", admins_feedback_path(feedback), method: :delete, data:{confirm: "确认删除的吗?"}, class: "action" %>
</td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: feedbacks } %>
<style>
.tooltip-inner {
max-width: 800px;
/* width: inherit; will take up least amount of space */
}
</style>

View File

@ -0,0 +1,9 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('反馈意见') %>
<% end %>
<div class="box admin-list-container feedback-list-container" style="min-height: 400px">
<%= render partial: 'admins/feedbacks/list', locals: { feedbacks: @feedbacks } %>
</div>
<div id="feedback-modals">
</div>

View File

@ -0,0 +1 @@
$('.feedback-list-container').html("<%= j( render partial: 'admins/feedbacks/list', locals: { feedbacks: @feedbacks } ) %>");

View File

@ -0,0 +1,3 @@
$("#feedback-modals").html("<%= j render(partial: 'admins/feedbacks/history_form_modal', locals: {type: 'new_history'}) %>")
$(".feedback-history-change-modal").modal('show');
createMDEditor('feedback-history-email-editor', {width: 750, height: 300, placeholder: '请输入邮件正文',watch: false, imageUpload: false });

View File

@ -4,7 +4,8 @@
<th width="5%">序号</th> <th width="5%">序号</th>
<th width="30%">名称</th> <th width="30%">名称</th>
<th width="20%"><%= sort_tag('精选', name: 'pinned_index', path: admins_project_categories_path) %></th> <th width="20%"><%= sort_tag('精选', name: 'pinned_index', path: admins_project_categories_path) %></th>
<th width="20%"><%= sort_tag('项目数', name: 'projects_count', path: admins_project_categories_path) %></th> <th width="10%"><%= sort_tag('项目数', name: 'projects_count', path: admins_project_categories_path) %></th>
<th width="10%"><%= sort_tag('私有项目数', name: 'private_projects_count', path: admins_project_categories_path) %></th>
<th width="20%">精选项目数</th> <th width="20%">精选项目数</th>
<th width="20%"><%= sort_tag('创建时间', name: 'created_at', path: admins_project_categories_path) %></th> <th width="20%"><%= sort_tag('创建时间', name: 'created_at', path: admins_project_categories_path) %></th>
<th width="25%">操作</th> <th width="25%">操作</th>
@ -20,6 +21,7 @@
</td> </td>
<td><%= project_category.pinned_index == 0 ? "" : "√" %></td> <td><%= project_category.pinned_index == 0 ? "" : "√" %></td>
<td><%= project_category.projects_count %></td> <td><%= project_category.projects_count %></td>
<td><%= project_category.private_projects_count %></td>
<td><%= project_category.projects.select(:id).where(is_pinned: true).size %></td> <td><%= project_category.projects.select(:id).where(is_pinned: true).size %></td>
<td><%= project_category.created_at&.strftime('%Y-%m-%d %H:%M') %></td> <td><%= project_category.created_at&.strftime('%Y-%m-%d %H:%M') %></td>
<td class="action-container"> <td class="action-container">

View File

@ -5,7 +5,6 @@
<th width="6%">ID</th> <th width="6%">ID</th>
<th width="20%" class="text-left">项目名称</th> <th width="20%" class="text-left">项目名称</th>
<th width="5%">公开</th> <th width="5%">公开</th>
<th width="5%">精选</th>
<th width="5%">推荐</th> <th width="5%">推荐</th>
<th width="5%">Issues</th> <th width="5%">Issues</th>
<th width="5%">资源</th> <th width="5%">资源</th>
@ -27,7 +26,6 @@
<%= link_to(project.name, "/#{project&.owner&.login}/#{project.identifier}", target: '_blank') %> <%= link_to(project.name, "/#{project&.owner&.login}/#{project.identifier}", target: '_blank') %>
</td> </td>
<td><%= project.is_public ? '√' : '' %></td> <td><%= project.is_public ? '√' : '' %></td>
<td><%= project.is_pinned ? '√' : '' %></td>
<td><%= project.recommend ? '√' : '' %></td> <td><%= project.recommend ? '√' : '' %></td>
<td><%= project.issues.size %></td> <td><%= project.issues.size %></td>
<td><%= project.attachments.size %></td> <td><%= project.attachments.size %></td>
@ -40,8 +38,6 @@
<td><%= project.created_on&.strftime('%Y-%m-%d %H:%M') %></td> <td><%= project.created_on&.strftime('%Y-%m-%d %H:%M') %></td>
<td class="action-container"> <td class="action-container">
<% if project.is_public %> <% if project.is_public %>
<%= javascript_void_link '精选', class: 'action pinned-action', data: { id: project.id }, style: project.is_pinned ? 'display: none;' : '' %>
<%= javascript_void_link '取消精选', class: 'action unpinned-action', data: { id: project.id }, style: project.is_pinned ? '' : 'display: none;' %>
<%= javascript_void_link '推荐', class: 'action recommend-action', data: { id: project.id }, style: project.recommend ? 'display: none;' : '' %> <%= javascript_void_link '推荐', class: 'action recommend-action', data: { id: project.id }, style: project.recommend ? 'display: none;' : '' %>
<%= javascript_void_link '取消推荐', class: 'action unrecommend-action', data: { id: project.id }, style: project.recommend ? '' : 'display: none;' %> <%= javascript_void_link '取消推荐', class: 'action unrecommend-action', data: { id: project.id }, style: project.recommend ? '' : 'display: none;' %>
<%= link_to "设置推荐等级", edit_admins_project_path(project.id), remote: true, class: "action edit-recommend-action", style: project.recommend ? '' : 'display: none;' %> <%= link_to "设置推荐等级", edit_admins_project_path(project.id), remote: true, class: "action edit-recommend-action", style: project.recommend ? '' : 'display: none;' %>

View File

@ -56,6 +56,7 @@
<%= sidebar_item_group('#setting-submenu', '网站建设', icon: 'cogs') do %> <%= sidebar_item_group('#setting-submenu', '网站建设', icon: 'cogs') do %>
<li><%= sidebar_item(admins_faqs_path, 'FAQ', icon: 'question-circle', controller: 'admins-faqs') %></li> <li><%= sidebar_item(admins_faqs_path, 'FAQ', icon: 'question-circle', controller: 'admins-faqs') %></li>
<li><%= sidebar_item(admins_nps_path, 'NPS用户调研', icon: 'question-circle', controller: 'admins-nps') %></li> <li><%= sidebar_item(admins_nps_path, 'NPS用户调研', icon: 'question-circle', controller: 'admins-nps') %></li>
<li><%= sidebar_item(admins_feedbacks_path, '用户反馈', icon: 'question-circle', controller: 'admins-feedbacks') %></li>
<% end %> <% end %>
</li> </li>
<li> <li>

View File

@ -15,6 +15,12 @@
</label> </label>
<%= p.text_field :title,class: "form-control input-lg",required: true%> <%= p.text_field :title,class: "form-control input-lg",required: true%>
</div> </div>
<div class="form-group">
<label>
跳转URL
</label>
<%= p.text_field :url, class: "form-control",placeholder: ""%>
</div>
<div class="form-group"> <div class="form-group">
<label> <label>
排序等级 排序等级

View File

@ -4,8 +4,9 @@
<th width="5%">序号</th> <th width="5%">序号</th>
<th width="20%">标题</th> <th width="20%">标题</th>
<th width="20%">图片</th> <th width="20%">图片</th>
<th width="20%">排序等级</th> <th width="25%">跳转URL</th>
<th width="25%">操作</th> <th width="10%">排序等级</th>
<th width="20%">操作</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -15,6 +16,7 @@
<td><%= list_index_no((params[:page] || 1).to_i, index) %></td> <td><%= list_index_no((params[:page] || 1).to_i, index) %></td>
<td><%= banner.title %></td> <td><%= banner.title %></td>
<td><img style="width:150px" src="<%= banner.image %>" /></td> <td><img style="width:150px" src="<%= banner.image %>" /></td>
<td><a href="<%= banner.url %>" target="_blank"><%= banner.url %></a> </td>
<td><%= banner.order_index %></td> <td><%= banner.order_index %></td>
<td class="action-container"> <td class="action-container">
<%= link_to "编辑", edit_admins_topic_banner_path(banner), remote: true, class: "action" %> <%= link_to "编辑", edit_admins_topic_banner_path(banner), remote: true, class: "action" %>

View File

@ -68,12 +68,12 @@
<%= f.label :identity, label: '职业' %> <%= f.label :identity, label: '职业' %>
<%= select_tag('user[identity]', [], class: 'form-control identity-select optional', 'data-value': @user.user_extension&.identity, 'data-first-title': '请选择') %> <%= select_tag('user[identity]', [], class: 'form-control identity-select optional', 'data-value': @user.user_extension&.identity, 'data-first-title': '请选择') %>
</div> </div>
<div class="form-group technical-title-select-wrapper optional col-md-1" style="<%= @user.user_extension.student? ? 'display:none;' : '' %>"> <div class="form-group technical-title-select-wrapper optional col-md-1" style="<%= @user&.user_extension&.student? ? 'display:none;' : '' %>">
<%= f.label :technical_title, label: '职称' %> <%= f.label :technical_title, label: '职称' %>
<%= select_tag('user[technical_title]', [], class: 'form-control technical-title-select optional', 'data-value': @user.technical_title) %> <%= select_tag('user[technical_title]', [], class: 'form-control technical-title-select optional', 'data-value': @user.technical_title) %>
</div> </div>
<%= f.input :student_id, as: :tel, label: '学号', wrapper_html: { class: 'col-md-2', style: @user.user_extension.student? ? '' : 'display:none;' }, input_html: { class: 'student-id-input' } %> <%= f.input :student_id, as: :tel, label: '学号', wrapper_html: { class: 'col-md-2', style: @user&.user_extension&.student? ? '' : 'display:none;' }, input_html: { class: 'student-id-input' } %>
</div> </div>
<div class="form-row"> <div class="form-row">

View File

@ -27,7 +27,7 @@
<td><%= user.identity %></td> <td><%= user.identity %></td>
<td><%= display_text(user.created_on&.strftime('%Y-%m-%d %H:%M')) %></td> <td><%= display_text(user.created_on&.strftime('%Y-%m-%d %H:%M')) %></td>
<td><%= display_text(user.last_login_on&.strftime('%Y-%m-%d %H:%M')) %></td> <td><%= display_text(user.last_login_on&.strftime('%Y-%m-%d %H:%M')) %></td>
<td><%= link_to user.projects_count, "/users/#{user.login}/projects", target: "_blank" %></td> <td><%= link_to user.projects_count, "/#{user.login}/projects", target: "_blank" %></td>
<td class="action-container"> <td class="action-container">
<%= link_to '编辑', edit_admins_user_path(user), class: 'action' %> <%= link_to '编辑', edit_admins_user_path(user), class: 'action' %>

View File

@ -0,0 +1,14 @@
json.author_count @result_object["author_count"]
json.commit_count @result_object["commit_count"]
json.change_files @result_object["change_files"]
json.additions @result_object["additions"]
json.deletions @result_object["deletions"]
json.commit_count_in_all_branches @result_object["commit_count_in_all_branches"]
json.authors @result_object["authors"].each do |author|
json.author do
json.partial! 'api/v1/users/commit_user_email', locals: { user: render_cache_commit_author(author), name: author['name'], email: author['email'] }
end
json.commits author["commits"]
json.additions author["additions"]
json.deletions author["deletions"]
end

View File

@ -0,0 +1,22 @@
if user.present?
if user.is_a?(Hash)
json.id user["id"]
json.login user["login"]
json.name user["name"]
json.type user["type"]
json.image_url user["avatar_url"]
else
json.id user.id
json.login user.login
json.name user.real_name
json.type user&.type
json.image_url url_to_avatar(user)
end
else
json.id nil
json.login name
json.name name
json.email email
json.type nil
json.image_url User::Avatar.get_letter_avatar_url(name)
end

View File

@ -0,0 +1,9 @@
json.status 0
json.username @user.full_name
json.real_name @user.real_name
json.login @user.login
json.user_id @user.id
json.image_url url_to_avatar(@user)
json.admin @user.admin?
json.user_identity @user.identity
json.is_watch current_user&.watched?(@user)

View File

@ -4,6 +4,7 @@ json.user_admin_or_member @user_admin_or_member
json.issue_tags do json.issue_tags do
json.array! @issue_tags.each.to_a do |tag| json.array! @issue_tags.each.to_a do |tag|
json.extract! tag, :id, :name, :description, :color, :issues_count, :project_id, :gid, :gitea_url json.extract! tag, :id, :name, :description, :color, :project_id, :gid, :gitea_url
json.issues_count tag.issues_count - tag.issues.closed.size
end end
end end

View File

@ -1,4 +1,5 @@
json.total_count @organization_users.total_count json.total_count @organization_users.total_count
json.organization_users @organization_users do |org_user| json.organization_users @organization_users do |org_user|
next if org_user.user.blank?
json.partial! "detail", org_user: org_user, organization: @organization json.partial! "detail", org_user: org_user, organization: @organization
end end

View File

@ -1,5 +1,5 @@
json.array! @project_categories do |category| json.array! @project_categories do |category|
json.id category.id json.id category.id
json.name category.name json.name category.name
json.projects_count category.projects_count json.projects_count category.public_projects_count
end end

View File

@ -1,4 +1,4 @@
json.total_count @projects.total_count json.total_count @total_count
json.projects @projects do |project| json.projects @projects do |project|
# json.partial! "/projects/project_detail", project: project # json.partial! "/projects/project_detail", project: project
json.id project.id json.id project.id
@ -22,7 +22,7 @@ json.projects @projects do |project|
project_educoder = project.project_educoder project_educoder = project.project_educoder
json.name project_educoder&.owner json.name project_educoder&.owner
json.login project_educoder&.repo_name.split('/')[0] json.login project_educoder&.repo_name.split('/')[0]
json.image_url render_educoder_avatar_url(project.project_educoder) # json.image_url render_educoder_avatar_url(project.project_educoder)
else else
user = project.owner user = project.owner
json.type user.type json.type user.type

View File

@ -2,7 +2,7 @@ if @project.forge?
is_dir = @sub_entries.is_a?(Array) is_dir = @sub_entries.is_a?(Array)
file_name = entry['name'] file_name = entry['name']
file_type = File.extname(file_name.to_s)[1..-1] file_type = File.extname(file_name.to_s)[1..-1]
direct_download = download_type(file_type) direct_download = file_name.to_s.downcase != "Makefile".downcase && download_type(file_type)
image_type = image_type?(file_type) image_type = image_type?(file_type)
json.name file_name json.name file_name
json.sha entry['sha'] json.sha entry['sha']

View File

@ -61,6 +61,7 @@ json.setting do
json.common @common json.common @common
json.third_party @third_party json.third_party @third_party
json.third_party_new @third_party_new
if @top_system_notification.present? if @top_system_notification.present?
json.system_notification do json.system_notification do

View File

@ -1 +1 @@
json.(banner, :id, :title, :image) json.(banner, :id, :title, :image, :url)

View File

@ -0,0 +1,62 @@
<html>
<head>
<meta charset="utf-8">
<title>GitLink-验证码发送</title>
<style type="text/css">
/* 验证链接页面 */
body,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,pre,form,fieldset,legend,button,input,textarea,th,td{ margin:0; padding:0;}
body,table,input,textarea,select,button { font-family: "微软雅黑","宋体"; font-size:12px;line-height:1.5; background:#eaebec;}
div,img,tr,td,table{ border:0;}
table,tr,td{border:0;}
ol,ul,li{ list-style-type:none}
.new_content{ background:#fff; width: 100%;}
.email-page-link{ }
.email-link-top{ }
.c_white{ color:#fff;}
.email-link-con{ }
.email-link-line{ }
.email-link-footer{ padding:15px; color:#333; line-height: 1.9; }
.c_grey02{ color: #888;}
.fb{ font-weight: normal;}
.f14{ }
</style>
</head>
<body style="background:#fff;">
<div class="new_content">
<div style="width: 598px; background:#fff; margin:20px auto; font-size:14px; ">
<div style="height:50px; width: 578px; background:#46484c; padding:9px 10px 6px;border:1px solid #ddd; border-bottom:none;">
<a href="https://www.gitlink.org.cn">
<img width="100" style="float:left; margin-top: 8px;" src="https:///www.gitlink.org.cn/images/email_logo.png" alt="确实开源">
</a>
<div style="clear:both; overflow:hidden;"></div>
</div>
<div style="width: 558px; border-left:1px solid #ddd;border-right:1px solid #ddd; background:#fff; padding:30px 20px; color:#333; line-height: 1.9;">
<p style="color:#333; font-size:16px; margin-bottom:15px;font-weight: bold">
您好!
</p>
<p style="color:#333;">
您正在GitLink绑定邮箱请在10分钟内输入此验证码并进行下一步操作。
如非你本人操作,请忽略此邮件。
</p>
<div style="text-align: center;">
<div style="display:block; height: 45px; line-height:45px;padding:0 30px; width:100px; font-size: 20px; font-weight: bold; background:#ffd9d9; color:#e72c37; margin:30px auto;">
<p><%= @code %></p>
</div>
<span style="font-weight: normal;color:#666;">
此邮件为系统所发,请勿直接回复。<br/>
要解决问题或了解您的帐户详情,您可以访问 <a href="https:///www.gitlink.org.cn/forums/1168/detail" style="font-weight: normal; color:#ff7500;">帮助中心</a>。
</span>
</div>
<p style="color:#666; margin-top:30px;">
如果您并未发过此请求,则可能是因为其他用户误输了您的邮件地址,而使您收到了这封邮件,那么您可以放心的忽略此邮件,无需进一步采取任何操作。
</p>
</div>
<div style="padding:20px; color:#333; line-height: 1.9;background:#46484c;border:1px solid #ddd; border-top:none; width: 558px;">
<a href="https:///www.gitlink.org.cn" style="font-weight: normal; color:#fff;">www.gitlink.org.cn</a>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,62 @@
<html>
<head>
<meta charset="utf-8">
<title>GitLink-验证码发送</title>
<style type="text/css">
/* 验证链接页面 */
body,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,pre,form,fieldset,legend,button,input,textarea,th,td{ margin:0; padding:0;}
body,table,input,textarea,select,button { font-family: "微软雅黑","宋体"; font-size:12px;line-height:1.5; background:#eaebec;}
div,img,tr,td,table{ border:0;}
table,tr,td{border:0;}
ol,ul,li{ list-style-type:none}
.new_content{ background:#fff; width: 100%;}
.email-page-link{ }
.email-link-top{ }
.c_white{ color:#fff;}
.email-link-con{ }
.email-link-line{ }
.email-link-footer{ padding:15px; color:#333; line-height: 1.9; }
.c_grey02{ color: #888;}
.fb{ font-weight: normal;}
.f14{ }
</style>
</head>
<body style="background:#fff;">
<div class="new_content">
<div style="width: 598px; background:#fff; margin:20px auto; font-size:14px; ">
<div style="height:50px; width: 578px; background:#46484c; padding:9px 10px 6px;border:1px solid #ddd; border-bottom:none;">
<a href="https://www.gitlink.org.cn">
<img width="100" style="float:left; margin-top: 8px;" src="https:///www.gitlink.org.cn/images/email_logo.png" alt="确实开源">
</a>
<div style="clear:both; overflow:hidden;"></div>
</div>
<div style="width: 558px; border-left:1px solid #ddd;border-right:1px solid #ddd; background:#fff; padding:30px 20px; color:#333; line-height: 1.9;">
<p style="color:#333; font-size:16px; margin-bottom:15px;font-weight: bold">
您好!
</p>
<p style="color:#333;">
您正在GitLink找回密码请在10分钟内输入此验证码并进行下一步操作。
如非你本人操作,请忽略此邮件。
</p>
<div style="text-align: center;">
<div style="display:block; height: 45px; line-height:45px;padding:0 30px; width:100px; font-size: 20px; font-weight: bold; background:#ffd9d9; color:#e72c37; margin:30px auto;">
<p><%= @code %></p>
</div>
<span style="font-weight: normal;color:#666;">
此邮件为系统所发,请勿直接回复。<br/>
要解决问题或了解您的帐户详情,您可以访问 <a href="https:///www.gitlink.org.cn/forums/1168/detail" style="font-weight: normal; color:#ff7500;">帮助中心</a>。
</span>
</div>
<p style="color:#666; margin-top:30px;">
如果您并未发过此请求,则可能是因为其他用户误输了您的邮件地址,而使您收到了这封邮件,那么您可以放心的忽略此邮件,无需进一步采取任何操作。
</p>
</div>
<div style="padding:20px; color:#333; line-height: 1.9;background:#46484c;border:1px solid #ddd; border-top:none; width: 558px;">
<a href="https:///www.gitlink.org.cn" style="font-weight: normal; color:#fff;">www.gitlink.org.cn</a>
</div>
</div>
</div>
</body>
</html>

View File

@ -11,7 +11,7 @@ json.user_identity @user.identity
json.tidding_count 0 json.tidding_count 0
json.user_phone_binded @user.phone.present? json.user_phone_binded @user.phone.present?
json.need_edit_info @user.need_edit_info? json.need_edit_info @user.need_edit_info?
# json.phone @user.phone json.phone @user.phone
# json.email @user.mail # json.email @user.mail
json.profile_completed @user.profile_is_completed? json.profile_completed @user.profile_is_completed?
json.professional_certification @user.professional_certification json.professional_certification @user.professional_certification

View File

@ -1,21 +1,28 @@
OmniAuth.config.add_camelization 'qq', 'QQ' config = Rails.application.config_for(:configuration)
OmniAuth.config.add_camelization 'qq', 'QQ' if config.dig("oauth", "qq")
# OmniAuth.config.add_camelization 'github', 'GitHub' if config.dig("oauth", "github")
# OmniAuth.config.add_camelization 'gitee', 'Gitee' if config.dig("oauth", "gitee")
# OmniAuth.config.add_camelization 'wechat', 'Wechat' if config.dig("oauth", "wechat")
OmniAuth.config.logger = Rails.logger OmniAuth.config.logger = Rails.logger
OmniAuth.config.allowed_request_methods = %i[get post]
OmniAuth.config.before_request_phase = nil
OmniAuth.config.before_callback_phase = nil
OmniAuth.config.on_failure = Proc.new { |env| OmniAuth.config.on_failure = Proc.new { |env|
OmniAuth::FailureEndpoint.new(env).redirect_to_failure OmniAuth::FailureEndpoint.new(env).redirect_to_failure
} }
oauth_config = {}
begin
config = Rails.application.config_for(:configuration)
oauth_config = config.dig('oauth', 'qq')
raise 'oauth qq config missing' if oauth_config.blank?
rescue => ex
raise ex if Rails.env.production?
puts %Q{\033[33m [warning] qq oauth config or configuration.yml missing,
please add it or execute 'cp config/configuration.yml.example config/configuration.yml' \033[0m}
end
Rails.application.config.middleware.use OmniAuth::Builder do Rails.application.config.middleware.use OmniAuth::Builder do
provider :qq, oauth_config['appid'], oauth_config['secret'], { provider_ignores_state: true } if config.dig("oauth", "qq")
provider :qq, config.dig("oauth", "qq", "appid"), config.dig("oauth", "qq", "secret"), { provider_ignores_state: true }
end
if config.dig("oauth", "github").present?
provider :github, config.dig("oauth", "github", "appid"), config.dig("oauth", "github", "secret"), { provider_ignores_state: true, scope: "user:email" }
end
if config.dig("oauth", "gitee").present?
provider :gitee, config.dig("oauth", "gitee", "appid"), config.dig("oauth", "gitee", "secret"), { provider_ignores_state: true, scope: "user_info emails" }
end
if config.dig("oauth", "wechat").present?
provider :wechat, config.dig("oauth", "wechat", "appid"), config.dig("oauth", "wechat", "secret"), { provider_ignores_state: true, scope: "snsapi_login" }
end
end end

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.
# Enable per-form CSRF tokens.
# Rails.application.config.action_controller.per_form_csrf_tokens = true
# Rails.application.config.action_controller.forgery_protection_origin_check = true

View File

@ -0,0 +1,5 @@
'zh-CN':
activemodel:
attributes:
api/v1/users/feedbacks/create_service:
content: "反馈意见"

View File

@ -231,6 +231,8 @@ zh-CN:
ignore: ignore:
name: 'git忽略文件名称' name: 'git忽略文件名称'
content: 'git忽略文件内容' content: 'git忽略文件内容'
feedback_message_history:
title: ''
close_pr: 合并请求 close_pr: 合并请求
roles: roles:
Developer: 开发者 Developer: 开发者

View File

@ -20,9 +20,10 @@ Rails.application.routes.draw do
get 'attachments/download/:id', to: 'attachments#show' get 'attachments/download/:id', to: 'attachments#show'
get 'attachments/download/:id/:filename', to: 'attachments#show' get 'attachments/download/:id/:filename', to: 'attachments#show'
get 'auth/qq/callback', to: 'oauth/qq#create' # get 'auth/qq/callback', to: 'oauth/qq#create'
get 'auth/failure', to: 'oauth/base#auth_failure' get 'auth/failure', to: 'oauth/base#auth_failure'
get 'auth/cas/callback', to: 'oauth/cas#create' get 'auth/cas/callback', to: 'oauth/cas#create'
get 'auth/:provider/callback', to: 'oauth/callbacks#create'
get 'oauth/bind', to: 'oauth/educoder#bind' get 'oauth/bind', to: 'oauth/educoder#bind'
get 'oauth/register', to: 'oauth#register' get 'oauth/register', to: 'oauth#register'
@ -267,6 +268,7 @@ Rails.application.routes.draw do
get :trustie_related_projects get :trustie_related_projects
post :sync_user_info post :sync_user_info
get :email_search get :email_search
post :action
scope '/ci', module: :ci do scope '/ci', module: :ci do
scope do scope do
@ -942,6 +944,10 @@ Rails.application.routes.draw do
resources :nps do resources :nps do
post :switch_change, on: :collection post :switch_change, on: :collection
end end
resources :feedbacks, only: [:index, :destroy] do
get :new_history, on: :member
post :create_history, on: :member
end
resources :laboratories, only: [:index, :create, :destroy, :update] do resources :laboratories, only: [:index, :create, :destroy, :update] do
member do member do
get :shixuns_for_select get :shixuns_for_select

View File

@ -8,11 +8,14 @@ defaults format: :json do
post :check_password post :check_password
post :check_email post :check_email
post :check_email_verify_code post :check_email_verify_code
post :check_phone_verify_code
patch :update_email patch :update_email
patch :update_phone
end end
end end
scope module: :users do scope module: :users do
resources :projects, only: [:index] resources :projects, only: [:index]
resources :feedbacks, only: [:create]
end end
scope ':repo' do scope ':repo' do
@ -51,6 +54,7 @@ defaults format: :json do
end end
end end
resources :commits, only: [:index] resources :commits, only: [:index]
resources :code_stats, only: [:index]
get '/commits/:sha/diff', to: 'commits#diff' get '/commits/:sha/diff', to: 'commits#diff'
get '/git/blobs/:sha', to: 'git#blobs' get '/git/blobs/:sha', to: 'git#blobs'
get '/git/trees/:sha', to: 'git#trees' get '/git/trees/:sha', to: 'git#trees'

View File

@ -0,0 +1,10 @@
class CreateFeedbacks < ActiveRecord::Migration[5.2]
def change
create_table :feedbacks do |t|
t.references :user
t.text :content
t.timestamps
end
end
end

View File

@ -0,0 +1,12 @@
class CreateFeedbackMessageHistories < ActiveRecord::Migration[5.2]
def change
create_table :feedback_message_histories do |t|
t.references :feedback
t.references :user
t.string :title
t.text :content
t.timestamps
end
end
end

View File

@ -0,0 +1,5 @@
class AddPrivateProjectsCountToProjectCategory < ActiveRecord::Migration[5.2]
def change
add_column :project_categories, :private_projects_count, :integer, default: 0
end
end

View File

@ -0,0 +1,23 @@
class UpdateUserNickName < ActiveRecord::Migration[5.2]
def change
execute("ALTER TABLE `users` MODIFY `nickname` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `open_users` MODIFY `extra` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `issues` MODIFY `subject` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `issues` MODIFY `description` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `projects` MODIFY `description` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `project_details` MODIFY `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `journals` MODIFY `notes` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `journal_details` MODIFY `old_value` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `journal_details` MODIFY `value` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `claims` MODIFY `note` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `commit_logs` MODIFY `message` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `user_extensions` MODIFY `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
end
end

View File

@ -0,0 +1,16 @@
class UpdatePullRequestUtfName < ActiveRecord::Migration[5.2]
def change
execute("ALTER TABLE `projects` MODIFY `name` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `pull_requests` MODIFY `title` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `pull_requests` MODIFY `body` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `organization_extensions` MODIFY `description` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `version_releases` MODIFY `name` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `version_releases` MODIFY `body` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `version_releases` MODIFY `tag_name` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `versions` MODIFY `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `versions` MODIFY `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `issue_tags` MODIFY `name` varchar(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `issue_tags` MODIFY `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
execute("ALTER TABLE `projects_activity` MODIFY `project_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
end
end

View File

@ -0,0 +1,5 @@
class UpdateUserSuper < ActiveRecord::Migration[5.2]
def change
execute("ALTER TABLE `user_extensions` MODIFY `super_description` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
end
end

View File