require 'oauth2' class ApplicationController < ActionController::Base include CodeExample include RenderExpand include RenderHelper include ControllerRescueHandler include GitHelper include LoggerHelper protect_from_forgery prepend: true, unless: -> { request.format.json? } before_action :user_setup #before_action :check_account DCODES = %W(2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z) helper_method :current_user # 全局配置参数 # 返回name对应的value def edu_setting(name) EduSetting.get(name) end # 实训的访问权限 def shixun_access_allowed if !current_user.shixun_permission(@shixun) tip_exception(403, "..") end end def admin_or_business? User.current.admin? || User.current.business? end # 访问课堂时没权限直接弹加入课堂的弹框 :409 def user_course_identity @user_course_identity = current_user.course_identity(@course) if @user_course_identity > Course::STUDENT && @course.is_public == 0 tip_exception(409, "您没有权限进入") end uid_logger("###############user_course_identity:#{@user_course_identity}") end # 判断用户的邮箱或者手机是否可用 # params[:type] 1: 注册;2:忘记密码 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])$/ tip_exception(-2, "请输入正确的手机号或邮箱") end # 考虑到安全参数问题,多一次查询,去掉Union user = User.where(phone: login).first || User.where(mail: login).first if type.to_i == 1 && !user.nil? tip_exception(-2, "该手机号码或邮箱已被注册") elsif type.to_i == 2 && user.nil? tip_exception(-2, "该手机号码或邮箱未注册") end sucess_status end # 发送及记录激活码 # 发送验证码:type 1:注册手机验证码 2:找回密码手机验证码 3:找回密码邮箱验证码 4:绑定手机 5:绑定邮箱 # 6:手机验证码登录 7:邮箱验证码登录 8:邮箱注册验证码 def check_verification_code(code, send_type, value) case send_type when 1, 2, 4 # 手机类型的发送 sigle_para = {phone: value} status = Educoder::Sms.send(mobile: value, code: code) tip_exception(-2, code_msg(status)) if status != 0 when 8, 3, 5 # 邮箱类型的发送 sigle_para = {email: value} begin UserMailer.register_email(value, code).deliver_now # Mailer.run.email_register(code, value) rescue Exception => e logger_error(e) tip_exception(-2,"邮件发送失败,请稍后重试") end end ver_params = {code_type: send_type, code: code}.merge(sigle_para) VerificationCode.create!(ver_params) end def code_msg status case status when 0 "验证码已经发送到您的手机,请注意查收" when 8 "同一手机号30秒内重复提交相同的内容" when 9 "同一手机号5分钟内重复提交相同的内容超过3次" when 22 "1小时内同一手机号发送次数超过限制" when 33 "验证码发送次数超过频率" when 43 "一天内同一手机号发送次数超过限制" end end def find_course return normal_status(2, '缺少course_id参数!') if params[:course_id].blank? @course = Course.find(params[:course_id]) rescue Exception => e tip_exception(e.message) end def course_manager return normal_status(403, '只有课堂管理员才有权限') if @user_course_identity > Course::CREATOR end def find_board return normal_status(2, "缺少board_id参数") if params[:board_id].blank? @board = Board.find(params[:board_id]) rescue Exception => e uid_logger_error(e.message) tip_exception(e.message) end def validate_type(object_type) normal_status(2, "参数") if params.has_key?(:sort_type) && !SORT_TYPE.include?(params[:sort_type].strip) end def set_pagination @page = params[:page] || 1 @page_size = params[:page_size] || 15 end # 课堂教师权限 def teacher_allowed logger.info("#####identity: #{current_user.course_identity(@course)}") unless current_user.course_identity(@course) < Course::STUDENT normal_status(403, "") end end # 课堂教师、课堂管理员、超级管理员的权限(不包含助教) def teacher_or_admin_allowed unless current_user.course_identity(@course) < Course::ASSISTANT_PROFESSOR normal_status(403, "") end end def require_admin normal_status(403, "") unless User.current.admin? end def require_business normal_status(403, "") unless admin_or_business? end # 前端会捕捉401,弹登录弹框 # 未授权的捕捉407,弹试用申请弹框 def require_login #6.13 -hs tip_exception(401, "..") unless User.current.logged? end # 异常提醒 def tip_exception(status = -1, message) raise Educoder::TipException.new(status, message) end def missing_template tip_exception(404, "...") end # 弹框提醒 def tip_show_exception(status = -2, message) raise Educoder::TipException.new(status, message) end def normal_status(status = 0, message) case status when 403 message = "您没有权限进行该操作" when 404 message = "您访问的页面不存在或已被删除" end render :json => { status: status, message: message } end # 系统全局认证 def check_auth day_cer = UserDayCertification.find_by(user_id: current_user.id) # 如果注册超过24小时则需要完善资料及授权 if (Time.now.to_i - day_cer.try(:created_at).to_i) > 86400 if !current_user.profile_completed? info_url = '/account/profile' tip_exception(402, info_url) elsif current_user.certification != 1 if current_user.apply_actions.exists?(container_type: 'TrialAuthorization', status: 0) tip_exception(408, "您的试用申请正在审核中,请耐心等待") end tip_exception(407, "系统未授权") end end # if current_user.certification != 1 && current_user.apply_actions.exists?(container_type: 'TrialAuthorization', status: 0) # tip_exception(408, "您的试用申请正在审核中,请耐心等待") # elsif (Time.now.to_i - day_cer.try(:created_at).to_i) < 86400 # if !current_user.profile_completed? # info_url = '/account/profile' # tip_exception(402, info_url) # elsif current_user.certification != 1 # day_cer = UserDayCertification.find_by(user_id: current_user.id) # tip_exception(407, "系统未授权") unless (Time.now.to_i - day_cer.try(:created_at).to_i) < 86400 # end # end end def start_user_session(user) session[:user_id] = user.id session[:ctime] = Time.now.utc.to_i session[:atime] = Time.now.utc.to_i end def user_setup # reacct静态资源加载不需要走这一步 return if params[:controller] == "main" # Find the current user User.current = find_current_user uid_logger("user_setup: " + (User.current.logged? ? "#{User.current.try(:login)} (id=#{User.current.try(:id)})" : "anonymous")) if !User.current.logged? && Rails.env.development? User.current = User.find 12 end # User.current = User.find 81403 if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除 User.current = User.find 81403 elsif params[:debug] == 'student' User.current = User.find 8686 elsif params[:debug] == 'admin' User.current = User.find 1 elsif params[:debug] User.current = User.find_by_login params[:debug] end end # Sets the logged in user def logged_user=(user) reset_session if user && user.is_a?(User) User.current = user start_user_session(user) else User.current = User.anonymous end end # Returns the current user or nil if no user is logged in # and starts a session if needed def find_current_user uid_logger("user setup start: session[:user_id] is #{session[:user_id]}") if session[:user_id] # existing session (User.active.find(session[:user_id]) rescue nil) elsif autologin_user = try_to_autologin autologin_user elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth? # RSS key authentication does not start a session User.find_by_rss_key(params[:key]) end end def autologin_cookie_name edu_setting('autologin_cookie_name').presence || 'autologin' end def try_to_autologin if cookies[autologin_cookie_name] # auto-login feature starts a new session user = User.try_to_autologin(cookies[autologin_cookie_name]) if user start_user_session(user) end user end end def api_request? %w(xml json).include? params[:format] end def current_user User.current end ## 默认输出json def render_json respond_to do |format| format.json end end ## 输出错误信息 def error_status(message = nil) @status = -1 @message = message end # 实训等对应的仓库地址 def repo_ip_url(repo_path) "#{edu_setting('git_address_ip')}/#{repo_path}" end def repo_url(repo_path) "#{edu_setting('git_address_domain')}/#{repo_path}" end # 通关后,把最后一次成功的代码存到数据库 # type 0 创始内容, 1 最新内容 def game_passed_code(path, myshixun, game_id) # 如果代码窗口是隐藏的,则不用保存代码 return if myshixun.shixun.hide_code file_content = git_fle_content myshixun.repo_path, path unless file_content.present? raise("获取文件代码异常") end game_code = GameCode.where(:game_id => game_id, :path => path).first if game_code.nil? GameCode.create!(:game_id => game_id, :new_code => file_content, :path => path) else game_code.update_attributes!(:new_code => file_content) end end # Post请求 def uri_post(uri, params) begin uid_logger("--uri_exec: params is #{params}, url is #{uri}") uri = URI.parse(URI.encode(uri.strip)) res = Net::HTTP.post_form(uri, params).body logger.info("--uri_exec: .....res is #{res}") JSON.parse(res) rescue Exception => e uid_logger("--uri_exec: exception #{e.message}") raise Educoder::TipException.new("实训平台繁忙(繁忙等级:84)") end end # 处理返回非0就报错的请求 def interface_post(uri, params, status, message) begin uid_logger("--uri_exec: params is #{params}, url is #{uri}") uri = URI.parse(URI.encode(uri.strip)) res = Net::HTTP.post_form(uri, params).body logger.info("--uri_exec: .....res is #{res}") res = JSON.parse(res) if (res && res['code'] != 0) tip_exception(status, message) else res end rescue Exception => e uid_logger("--uri_exec: exception #{e.message}") raise Educoder::TipException.new("实训平台繁忙(繁忙等级:84)") end end # 适用与已经用url_safe编码后,回调字符串形式 def tran_base64_decode64(str) s_size = str.size % 4 if s_size != 0 str += "=" * (4 - s_size) end if str.blank? str else Base64.decode64(str.tr("-_", "+/")).force_encoding("utf-8") end end def sucess_status(message = 'success') render :json => { status: 1, message: message } end # 随机生成字符 def generate_identifier(container, num) code = DCODES.sample(num).join if container == User while container.exists?(login: code) do code = DCODES.sample(num).join end else while container.exists?(identifier: code) do code = DCODES.sample(num).join end end code end # 实训主类别列表,自带描述 def shixun_main_type list = [] mirrors = MirrorRepository.select([:id, :type_name]).published_main_mirror mirrors.try(:each) do |mirror| list << {id: mirror.id, type_name: mirror.type_name, description: mirror.try(:description)} end list end # 小类别列表 def shixun_small_type list = [] mirrors = MirrorRepository.select([:id, :type_name]).published_small_mirror mirrors.try(:each) do |mirror| list << {id: mirror.id, type_name: mirror.type_name} end list end def container_limit(mirror_repositories) container = [] mirror_repositories.each do |mr| if mr.name.present? container << {:image => mr.name, :cpuLimit => mr.cpu_limit, :memoryLimit => "#{mr.memory_limit}M", :type => mr.try(:main_type) == "1" ? "main" : "sub"} end end container.to_json end # 实训中间层pod配置 def shixun_container_limit shixun container = [] shixun.shixun_service_configs.each do |config| mirror = config.mirror_repository if mirror.name.present? container << {:image => mirror.name, :cpuLimit => config.cpu_limit, :cpuRequest => config.lower_cpu_limit, :memoryLimit => "#{config.memory_limit}M", :memoryRequest => "#{config.request_limit}M", :resourceLimit => "#{config.resource_limit}K", :type => mirror.try(:main_type) == "1" ? "main" : "sub"} end end container.to_json end # 毕设任务列表的赛选 def course_work(task, **option) logger.info("#############{option}") course = task.course work_list = task.graduation_works.includes(user: [:user_extension]) # 教师评阅搜索 0: 未评, 1 已评 if option[:teacher_comment] graduation_work_ids = GraduationWorkScore.where(graduation_work_id: work_list.map(&:id)).pluck(:graduation_work_id) if option[:teacher_comment].zero? work_list = work_list.where.not(id: graduation_work_ids) elsif option[:teacher_comment] == 1 work_list = work_list.where(id: graduation_work_ids).where.not(work_status: 0) end end # 作品状态 0: 未提交, 1 按时提交, 2 延迟提交 if option[:task_status] work_list = work_list.where(work_status: option[:task_status]) end # 分班情况 if option[:course_group] group_user_ids = course.course_members.where(course_group_id: option[:course_group]).pluck(:user_id) # 有分组只可能是老师身份查看列表 work_list = work_list.where(user_id: group_user_ids) end # 只看我的交叉评阅 if option[:cross_comment] graduation_work_id = task.graduation_work_comment_assignations.where(:user_id => current_user.id) .pluck(:graduation_work_id).uniq if task.graduation_work_comment_assignations work_list = work_list.where(id: graduation_work_id) end # 输入姓名和学号搜索 # TODO user_extension 如果修改 请调整 if option[:search] work_list = work_list.joins(user: :user_extension).where("concat(lastname, firstname) like ? or student_id like ?", "%#{option[:search]}%", "%#{option[:search]}%") end # 排序 rorder = option[:order] || "updated_at" b_order = option[:b_order] || "desc" if rorder == "created_at" || rorder == "work_score" work_list = work_list.order("graduation_works.#{rorder} #{b_order}") elsif rorder == "student_id" work_list = work_list.joins(user: :user_extension).order("user_extensions.#{rorder} #{b_order}") end work_list end def strip_html(text, len=0, endss="...") ss = "" if !text.nil? && text.length>0 ss=text.gsub(/<\/?.*?>/, '').strip ss = ss.gsub(/ */, '') ss = ss.gsub(/\r\n/,'') #新增 ss = ss.gsub(/\n/,'') #新增 if len > 0 && ss.length > len ss = ss[0, len] + endss elsif len > 0 && ss.length <= len ss = ss #ss = truncate(ss, :length => len) end end ss end # Returns a string that can be used as filename value in Content-Disposition header def filename_for_content_disposition(name) request.env['HTTP_USER_AGENT'] =~ %r{MSIE|Trident|Edge} ? ERB::Util.url_encode(name) : name end def format_time(time) time.blank? ? '' : time.strftime("%Y-%m-%d %H:%M") end # 获取Oauth Client def get_client(site) client_id = Rails.configuration.educoder['client_id'] client_secret = Rails.configuration.educoder['client_secret'] OAuth2::Client.new(client_id, client_secret, site: site) end def paginate(relation) limit = params[:limit].to_i.zero? ? 20 : params[:limit].to_i page = params[:page].to_i.zero? ? 1 : params[:page].to_i offset = (page - 1) * limit if relation.is_a?(Array) relation[offset, limit] else relation.limit(limit).offset(offset) end end def strf_time(time) time.blank? ? '' : time.strftime("%Y-%m-%d %H:%M:%S") end def logger_error(error) Rails.logger.error(error.message) error.backtrace.each { |msg| Rails.logger.error(msg) } end private def object_not_found uid_logger("Missing template or cant't find record, responding with 404") render json: {message: "您访问的页面不存在或已被删除", status: 404} false end def tip_show(exception) uid_logger("Tip show status is #{exception.status}, message is #{exception.message}") render json: exception.tip_json end def render_parameter_missing render json: { status: -1, message: '参数缺失' } end end