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
gem 'omniauth', '~> 1.9.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
gem 'request_store'
@ -135,4 +139,4 @@ gem 'doorkeeper'
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!
user = Users::RegisterService.call(register_params)
user.mail = "#{user.login}@example.org" if user.mail.blank?
password = register_params[:password].strip
# gitea用户注册, email, username, password
@ -152,6 +153,10 @@ class AccountsController < ApplicationController
user.gitea_uid = gitea_user[:body]['id']
if user.save!
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)
render_ok
end
@ -393,7 +398,7 @@ class AccountsController < ApplicationController
end
def register_params
params.permit(:login, :namespace, :password, :password_confirmation, :code)
params.permit(:login, :namespace, :password, :password_confirmation, :code, :type)
end
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
def banner_params
params.require(:topic_banner).permit(:title, :order_index)
params.require(:topic_banner).permit(:title, :order_index, :url)
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
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
@result_object = Api::V1::Users::UpdateEmailService.call(@observe_user, params, current_user.gitea_token)
if @result_object
@ -78,4 +93,13 @@ class Api::V1::UsersController < Api::V1::BaseController
return render_error('更改邮箱失败!')
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

View File

@ -79,8 +79,7 @@ class ApplicationController < ActionController::Base
# 判断用户的邮箱或者手机是否可用
# params[:type] 1: 注册2忘记密码3绑定
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}$/ ||
login =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])$/
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}$/
tip_exception(-2, "请输入正确的手机号或邮箱")
end
@ -103,8 +102,10 @@ class ApplicationController < ActionController::Base
when 1, 2, 4, 9
# 手机类型的发送
sigle_para = {phone: value}
status = Gitlink::Sms.send(mobile: value, code: code)
tip_exception(-2, code_msg(status)) if status != 0
# status = Gitlink::Sms.send(mobile: value, code: code)
# 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
# 邮箱类型的发送
sigle_para = {email: value}
@ -116,8 +117,13 @@ class ApplicationController < ActionController::Base
send_email_control = LimitForbidControl::SendEmailCode.new(value)
tip_exception(-1, '邮件发送太频繁,请稍后再试') if send_email_control.forbid?
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)
send_email_control.increment!
# Mailer.run.email_register(code, value)
@ -149,6 +155,27 @@ class ApplicationController < ActionController::Base
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)
normal_status(2, "参数") if params.has_key?(:sort_type) && !SORT_TYPE.include?(params[:sort_type].strip)
end
@ -638,7 +665,7 @@ class ApplicationController < ActionController::Base
def kaminari_paginate(relation)
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
relation.page(page).per(limit)

View File

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

View File

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

View File

@ -11,7 +11,7 @@ module LoginHelper
def set_autologin_cookie(user)
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}"
cookie_options = {

View File

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

View File

@ -7,7 +7,7 @@ class IssueTagsController < ApplicationController
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))
@page = params[:page] || 1
@limit = params[:limit] || 15

View File

@ -3,6 +3,7 @@ class Oauth::BaseController < ActionController::Base
include LoginHelper
include ControllerRescueHandler
include LoggerHelper
include RegisterHelper
# include LaboratoryHelper
skip_before_action :verify_authenticity_token
@ -13,13 +14,13 @@ class Oauth::BaseController < ActionController::Base
private
def tip_exception(status = -1, message)
raise Educoder::TipException.new(status, message)
raise Gitlink::TipException.new(status, message)
end
def tip_show_exception(status = -2, message)
raise Educoder::TipException.new(status, message)
raise Gitlink::TipException.new(status, message)
end
def tip_show(exception)
uid_logger("Tip show status is #{exception.status}, message is #{exception.message}")
render json: exception.tip_json
@ -35,7 +36,7 @@ class Oauth::BaseController < ActionController::Base
end
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']
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.save!
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)
Util.write_file(@image, avatar_path(@organization)) if params[:image].present?
@ -123,5 +129,20 @@ class Organizations::OrganizationsController < Organizations::BaseController
def sync_organization_extension!
@organization.organization_extension.update_attributes!(organization_extension_params)
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

View File

@ -10,7 +10,7 @@ class ProjectCategoriesController < ApplicationController
end
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
# @category_group_list = projects.joins(:project_category).group("project_categories.id", "project_categories.name").size
end

View File

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

View File

@ -40,8 +40,9 @@ class ProjectsController < ApplicationController
category_id = params[:category_id]
@total_count =
if category_id.blank?
ps = ProjectStatistic.first
ps.common_projects_count + ps.mirror_projects_count unless ps.blank?
# ps = ProjectStatistic.first
# ps.common_projects_count + ps.mirror_projects_count unless ps.blank?
@projects.total_count
else
cate = ProjectCategory.find_by(id: category_id)
cate&.projects_count || 0
@ -52,7 +53,7 @@ class ProjectsController < ApplicationController
ActiveRecord::Base.transaction do
Projects::CreateForm.new(project_params).validate!
@project = Projects::CreateService.new(current_user, project_params).call
OpenProjectDevOpsJob.perform_later(@project&.id, current_user.id)
end
rescue Exception => e
uid_logger_error(e.message)
@ -154,6 +155,15 @@ class ProjectsController < ApplicationController
}
gitea_repo = Gitea::Repository::UpdateService.call(@owner, @project&.repository&.identifier, gitea_params)
@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
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
@ -171,6 +181,8 @@ class ProjectsController < ApplicationController
Gitea::Repository::DeleteService.new(@project.owner, @project.identifier).call
@project.destroy!
@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
end
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!
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])
@pull_request.reviewers = reviewers
@ -165,6 +151,8 @@ class PullRequestsController < ApplicationController
colsed = PullRequests::CloseService.call(@owner, @repository, @pull_request, current_user)
if colsed === true
@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?
normal_status(1, "已拒绝")
else
@ -210,6 +198,8 @@ class PullRequestsController < ApplicationController
# @pull_request.project_trend_status!
@pull_request.project_trends.create!(user: current_user, project: @project,action_type: ProjectTrend::MERGE)
@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?
normal_status(1, "合并成功")
else

View File

@ -7,6 +7,7 @@ class SettingsController < ApplicationController
get_sub_competitions
get_personal_menu
get_third_party
get_third_party_new
get_top_system_notification
end
@ -67,6 +68,24 @@ class SettingsController < ApplicationController
url: EducoderOauth.oauth_url
}
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
@top_system_notification = SystemNotification.is_top.first

View File

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

View File

@ -57,6 +57,13 @@ class UsersController < ApplicationController
Cache::V2::OwnerCommonService.new(@user.id).read
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
watchers = Watcher.watching_users(@user.id).includes(:user).order("watchers.created_at desc")
if params[:search].present?

View File

@ -6,7 +6,7 @@ module Admins::ProjectsHelper
if owner.is_a?(User)
link_to(project.owner&.real_name, "/#{project&.owner&.login}", target: '_blank')
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
""
end

View File

@ -10,7 +10,7 @@ module RepositoriesHelper
end
def download_type(str)
default_type = %w(xlsx xls ppt pptx pdf zip 7z rar exe pdb obj idb RData rdata doc docx mpp vsdx dot otf eot ttf woff woff2 mp4 mov wmv flv mpeg avi avchd webm mkv 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?
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
end
content = content.gsub(/src=\"#{r_content}\"/, "src=\"#{new_r_content}\"").gsub(/src='#{r_content}'/, "src=\"#{new_r_content}\"")
rescue
rescue
next
end
end

View File

@ -62,7 +62,7 @@ module Gitea
file_params = {}
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(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(committer: @params[:committer])
file_params

View File

@ -1,7 +1,7 @@
class MigrateRemoteRepositoryJob < ApplicationJob
queue_as :default
def perform(repo_id, token, params)
def perform(repo_id, token, user_id, params)
repo = Repository.find_by(id: repo_id)
return if repo.blank?
@ -12,6 +12,10 @@ class MigrateRemoteRepositoryJob < ApplicationJob
if gitea_repository[0]==201
repo&.project&.update_columns(gpid: gitea_repository[2]["id"])
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} ############"
else
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_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})
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'
operator_id, pull_request_id = args[0], args[1]
operator = User.find_by_id(operator_id)

View File

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

View File

@ -1,7 +1,8 @@
class UserMailer < ApplicationMailer
# 注意:这个地方一定要和你的邮箱服务域名一致
# 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)
@ -9,8 +10,24 @@ class UserMailer < ApplicationMailer
mail(to: mail, subject: 'Gitink | 注册验证码')
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)
@code = code
mail(to: mail, subject: 'Gitink | 更改邮箱验证码')
end
def feedback_email(mail, title, content)
mail(to: mail, subject: title, content_type: "text/html", body: content)
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")
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::ProjectOpenDevOps', sys_notice: '您的仓库 <b>{repository}</b> 已成功开通引擎服务,可通过简单的节点编排完成自动化集成与部署。欢迎体验!', notification_url: '{baseurl}/{owner}/{identifier}/devops')
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} 提交了一个合并请求")
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)
#
class OpenUsers::QQ < OpenUser
class OpenUsers::Qq < OpenUser
def nickname
extra&.[]('nickname')
end

View File

@ -68,4 +68,9 @@ class Owner < ApplicationRecord
has_many :repositories, foreign_key: :user_id, 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

View File

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

View File

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

View File

@ -114,13 +114,13 @@ class User < Owner
# trustie: 来自Trustie平台
# forge: 平台本身注册的用户
# 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
has_one :user_extension, dependent: :destroy
has_many :open_users, dependent: :destroy
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
has_many :fork_users, dependent: :destroy
@ -177,6 +177,7 @@ class User < Owner
has_one :trace_user, dependent: :destroy
has_many :user_trace_tasks, dependent: :destroy
has_many :feedbacks, dependent: :destroy
# Groups and active users
scope :active, lambda { where(status: [STATUS_ACTIVE, STATUS_EDIT_INFO]) }
scope :like, lambda { |keywords|
@ -192,7 +193,8 @@ class User < Owner
:show_email, :show_location, :show_department, :super_description, :show_super_description,
:technical_title, :province, :city, :custom_department, to: :user_extension, allow_nil: true
before_save :update_hashed_password, :set_lastname
# before_save :update_hashed_password, :set_lastname
before_save :update_hashed_password
after_save :reset_cache_data
after_create do
SyncTrustieJob.perform_later("user", 1) if allow_sync_to_trustie?
@ -558,17 +560,19 @@ class User < Owner
def full_name
return '游客' unless logged?
name = show_realname? ? lastname + firstname : nickname
name.blank? ? (nickname.blank? ? login : nickname) : name
# name = show_realname? ? lastname + firstname : nickname
# name.blank? ? (nickname.blank? ? login : nickname) : name
nickname.blank? ? login : nickname
end
# 用户的真实姓名(不考虑用户是否隐藏了真实姓名,课堂模块都用真实姓名)
def real_name
return '游客' unless logged?
name = lastname + firstname
name = name.blank? ? (nickname.blank? ? login : nickname) : name
# name = lastname + firstname
# name = name.blank? ? (nickname.blank? ? login : nickname) : name
# name.gsub(/\s+/, '').strip #6.11 -hs
name.strip
# name.strip
nickname.blank? ? login : nickname
end
def only_real_name
@ -701,12 +705,13 @@ class User < Owner
end
def show_real_name
name = lastname + firstname
if name.blank?
nickname.blank? ? login : nickname
else
name
end
# name = lastname + firstname
# if name.blank?
# nickname.blank? ? login : nickname
# else
# name
# end
nickname.blank? ? login : nickname
end
def update_hashed_password
@ -787,6 +792,15 @@ class User < Owner
login
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)
case type
when 'wechat' then wechat_open_user.present?

View File

@ -12,11 +12,16 @@ class Projects::ListQuery < ApplicationQuery
def call
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)
sort = params[:sort_by] || "updated_on"
sort_direction = params[:sort_direction] || "desc"
collection = optimize_sorting(collection, sort) if params[:category_id].present?
custom_sort(collection, sort, sort_direction)
# scope = scope.reorder("projects.#{sort} #{sort_direction}")
@ -36,10 +41,11 @@ class Projects::ListQuery < ApplicationQuery
ids = Projects::ElasticsearchService.call(params[:search])
items = items.where(platform: 'forge')
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
items.by_name_or_identifier(params[:search])
items = items.by_name_or_identifier(params[:search])
end
items.or(items.where(user_id: Owner.like(params[:search]).pluck(:id)))
end
def by_project_type(items)
@ -57,5 +63,20 @@ class Projects::ListQuery < ApplicationQuery
def by_pinned(items)
(params[:pinned].present? && params[:category_id].present?) ? items.pinned : items
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

View File

@ -15,13 +15,14 @@ class Admins::UpdateUserService < ApplicationService
user.firstname = ''
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
ActiveRecord::Base.transaction do
user.save!
user.user_extension.save!
user.user_extension.save! if user.user_extension.present?
update_gitea_user(old_login)
update_gitea_user_email(user.previous_changes[:mail])
end
user
@ -65,4 +66,14 @@ class Admins::UpdateUserService < ApplicationService
Util.logger_error(ex)
raise Error, '保存失败'
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

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]
@old_mail = user.mail
@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
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 404 then error("你操作的链接不存在!")
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("不合法的分支名称!")
else
error("#{filepath}文件已存在,不能重复创建!")

View File

@ -17,7 +17,11 @@ class Issues::ListQueryService < ApplicationService
issues = all_issues.issue_index_includes
issues = issues.includes(pull_request: :reviewers)
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"
if(select_type == "Issue")
issues = issues.where.not(status_id: 5) #默认显示开启中的

View File

@ -10,7 +10,7 @@ class Oauth::CreateOrFindQqAccountService < ApplicationService
def call
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?
if user.blank? || !user.logged?
@ -32,7 +32,7 @@ class Oauth::CreateOrFindQqAccountService < ApplicationService
Util.download_file(params.dig('info', 'image'), avatar_path)
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 # 方便后面进行账号绑定
end

View File

@ -16,6 +16,7 @@ class Projects::CreateService < ApplicationService
Project.update_common_projects_count!
ProjectUnit.init_types(@project.id)
Repositories::CreateService.new(user, @project, repository_params).call
upgrade_project_category_private_projects_count
else
Rails.logger.info("#############___________create_project_erros______###########{@project.errors.messages}")
end
@ -28,6 +29,14 @@ class Projects::CreateService < ApplicationService
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
(user.id == params[:user_id].to_i) || (user.organizations.find_by_id(params[:user_id]).present?)
end

View File

@ -11,7 +11,7 @@ class Repositories::MigrateService < ApplicationService
@repository = Repository.new(repository_params)
if @repository.save!
@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
@repository
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="30%">名称</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%"><%= sort_tag('创建时间', name: 'created_at', path: admins_project_categories_path) %></th>
<th width="25%">操作</th>
@ -20,6 +21,7 @@
</td>
<td><%= project_category.pinned_index == 0 ? "" : "√" %></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.created_at&.strftime('%Y-%m-%d %H:%M') %></td>
<td class="action-container">

View File

@ -5,7 +5,6 @@
<th width="6%">ID</th>
<th width="20%" class="text-left">项目名称</th>
<th width="5%">公开</th>
<th width="5%">精选</th>
<th width="5%">推荐</th>
<th width="5%">Issues</th>
<th width="5%">资源</th>
@ -27,7 +26,6 @@
<%= link_to(project.name, "/#{project&.owner&.login}/#{project.identifier}", target: '_blank') %>
</td>
<td><%= project.is_public ? '√' : '' %></td>
<td><%= project.is_pinned ? '√' : '' %></td>
<td><%= project.recommend ? '√' : '' %></td>
<td><%= project.issues.size %></td>
<td><%= project.attachments.size %></td>
@ -40,8 +38,6 @@
<td><%= project.created_on&.strftime('%Y-%m-%d %H:%M') %></td>
<td class="action-container">
<% 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 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;' %>

View File

@ -56,6 +56,7 @@
<%= 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_nps_path, 'NPS用户调研', icon: 'question-circle', controller: 'admins-nps') %></li>
<li><%= sidebar_item(admins_feedbacks_path, '用户反馈', icon: 'question-circle', controller: 'admins-feedbacks') %></li>
<% end %>
</li>
<li>

View File

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

View File

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

View File

@ -68,12 +68,12 @@
<%= f.label :identity, label: '职业' %>
<%= select_tag('user[identity]', [], class: 'form-control identity-select optional', 'data-value': @user.user_extension&.identity, 'data-first-title': '请选择') %>
</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: '职称' %>
<%= select_tag('user[technical_title]', [], class: 'form-control technical-title-select optional', 'data-value': @user.technical_title) %>
</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 class="form-row">

View File

@ -27,7 +27,7 @@
<td><%= user.identity %></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><%= 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">
<%= 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.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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
json.total_count @projects.total_count
json.total_count @total_count
json.projects @projects do |project|
# json.partial! "/projects/project_detail", project: project
json.id project.id
@ -22,7 +22,7 @@ json.projects @projects do |project|
project_educoder = project.project_educoder
json.name project_educoder&.owner
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
user = project.owner
json.type user.type

View File

@ -2,7 +2,7 @@ if @project.forge?
is_dir = @sub_entries.is_a?(Array)
file_name = entry['name']
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)
json.name file_name
json.sha entry['sha']

View File

@ -61,6 +61,7 @@ json.setting do
json.common @common
json.third_party @third_party
json.third_party_new @third_party_new
if @top_system_notification.present?
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.user_phone_binded @user.phone.present?
json.need_edit_info @user.need_edit_info?
# json.phone @user.phone
json.phone @user.phone
# json.email @user.mail
json.profile_completed @user.profile_is_completed?
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.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::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
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

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:
name: 'git忽略文件名称'
content: 'git忽略文件内容'
feedback_message_history:
title: ''
close_pr: 合并请求
roles:
Developer: 开发者

View File

@ -20,9 +20,10 @@ Rails.application.routes.draw do
get 'attachments/download/:id', 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/cas/callback', to: 'oauth/cas#create'
get 'auth/:provider/callback', to: 'oauth/callbacks#create'
get 'oauth/bind', to: 'oauth/educoder#bind'
get 'oauth/register', to: 'oauth#register'
@ -267,6 +268,7 @@ Rails.application.routes.draw do
get :trustie_related_projects
post :sync_user_info
get :email_search
post :action
scope '/ci', module: :ci do
scope do
@ -942,6 +944,10 @@ Rails.application.routes.draw do
resources :nps do
post :switch_change, on: :collection
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
member do
get :shixuns_for_select

View File

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