Merge branch 'dev_jk_server' of https://gitlink.org.cn/Trustie/forgeplus into dev_jk_server

This commit is contained in:
谢思 2022-10-14 11:15:40 +08:00
commit e4158d7d3a
41 changed files with 966 additions and 63 deletions

View File

@ -181,6 +181,7 @@ class AccountsController < ApplicationController
user = Users::RegisterService.call(register_params)
user.company_name = params[:company_name] if params[:company_name].present?
user.user_type = params[:user_type] if params[:user_type].present?
user.professional_field = params[:professional_field] if params[:professional_field].present?
user.mail = "#{user.login}@example.org"
password = register_params[:password].strip

View File

@ -1,6 +1,78 @@
class Api::V1::UsersController < Api::V1::BaseController
def index
render_ok
before_action :load_observe_user
before_action :check_auth_for_observe_user
def send_email_vefify_code
code = %W(0 1 2 3 4 5 6 7 8 9)
verification_code = code.sample(6).join
mail = params[:email]
code_type = params[:code_type]
sign = Digest::MD5.hexdigest("#{OPENKEY}#{mail}")
Rails.logger.info sign
tip_exception(501, "请求不合理") if sign != params[:smscode]
# 60s内不能重复发送
send_email_limit_cache_key = "send_email_60_second_limit:#{mail}"
tip_exception(-1, '请勿频繁操作') if Rails.cache.exist?(send_email_limit_cache_key)
send_email_control = LimitForbidControl::SendEmailCode.new(mail)
tip_exception(-1, '邮件发送太频繁,请稍后再试') if send_email_control.forbid?
begin
UserMailer.update_email(mail, verification_code).deliver_now
Rails.cache.write(send_email_limit_cache_key, 1, expires_in: 1.minute)
send_email_control.increment!
rescue Exception => e
logger_error(e)
tip_exception(-2,"邮件发送失败,请稍后重试")
end
ver_params = {code_type: code_type, code: verification_code, email: mail}
data = VerificationCode.new(ver_params)
if data.save!
render_ok
else
tip_exception(-1, "创建数据失败")
end
end
def check_password
password = params[:password]
return render_error("8~16位密码支持字母数字和符号") unless password =~ CustomRegexp::PASSWORD
return render_error("密码错误") unless @observe_user.check_password?(password)
render_ok
end
def check_email
mail = strip(params[:email])
return render_error("邮件格式有误") unless mail =~ CustomRegexp::EMAIL
exist_owner = Owner.find_by(mail: mail)
return render_error('邮箱已被使用') if exist_owner
render_ok
end
def check_email_verify_code
code = strip(params[:code])
mail = strip(params[:email])
code_type = params[:code_type]
return render_error("邮件格式有误") unless mail =~ CustomRegexp::EMAIL
verifi_code = VerificationCode.where(email: mail, code: code, code_type: code_type).last
return render_error("验证码不正确") if verifi_code&.code != code
return render_error("验证码已失效") 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
return render_ok
else
return render_error('更改邮箱失败!')
end
end
end

View File

@ -31,13 +31,11 @@ class AttachmentsController < ApplicationController
def get_file
normal_status(-1, "参数缺失") if params[:download_url].blank?
download_url = params[:download_url].to_s
download_url = download_url.gsub("http:", "https:") if base_url.include?("https:")
url = URI.encode(download_url)
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)
domain = GiteaService.gitea_config[:domain]
api_url = GiteaService.gitea_config[:base_url]
url = url.split(base_url)[1].gsub("api", "repos").gsub('?filepath=', '/').gsub('&', '?')
url = ("/repos"+url.split(base_url + "/api")[1]).gsub('?filepath=', '/').gsub('&', '?')
request_url = [domain, api_url, url, "?ref=#{params[:ref]}&access_token=#{current_user&.gitea_token}"].join
response = Faraday.get(request_url)
filename = url.to_s.split("/").pop()

View File

@ -3,12 +3,13 @@ class CompetitionInfosController < ApplicationController
before_action :find_competition, only: [:show, :edit, :update, :update_zone_or_sub, :enroll, :upload, :enroll_status,
:statistics, :enroll_list, :ranking_list, :enroll_update, :enroll_status_update,
:upload_enroll_template, :enroll_template, :online_switch]
before_action :require_admin, only: [:index, :new, :edit, :create]
before_action :require_admin, only: [:new, :edit, :create]
before_action :check_competition_manager, only: [:update, :enroll_status_update]
def index
competition_infos = CompetitionInfo.order(created_at: :desc)
competition_infos = competition_infos.where("title like ?", "%#{params[:name]}%") if params[:name].present?
competition_infos = competition_infos.where("competition_infos.title like ?", "%#{params[:name]}%") if params[:name].present?
competition_infos = competition_infos.joins(:watchers).where(watchers: { user_id: current_user.id }) if params[:category].to_s == "watched"
@competition_infos = paginate(competition_infos)
respond_to do |format|
format.html
@ -19,7 +20,7 @@ class CompetitionInfosController < ApplicationController
def recommend
sort_direction = ["asc", "desc"].include?(params[:sort_direction].to_s) ? params[:sort_direction] : "desc"
sort_by = "created_at"
competition_infos = CompetitionInfo.where(status: true).order(created_at: :desc)
competition_infos = CompetitionInfo.where(status: true)
competition_infos = competition_infos.where("title like ?", "%#{params[:name]}%") if params[:name].present?
@total_count= competition_infos.count
if params[:sort_by].to_s == "hot"
@ -201,6 +202,7 @@ class CompetitionInfosController < ApplicationController
end
def show
@competition_info.increment!(:visits)
end
def enroll_status_update
@ -323,7 +325,7 @@ class CompetitionInfosController < ApplicationController
def competition_info_params
params.require(:competition_info).permit(:title, :zones, :zones_local, :sub_competitions, :identifier, :content,
:video_url, :is_local, :manager_ids, :upload_date, :enroll_date, :guide, :about_us,
:banner_url, :enroll_template, :enroll_columns, :applet_banner_url)
:banner_url, :enroll_template, :enroll_columns, :applet_banner_url,:start_at)
end
def find_competition

View File

@ -16,4 +16,13 @@ module Api::UserHelper
end
@observe_user
end
# 是否具有查看用户或编辑用户的权限
def check_auth_for_observe_user
return render_forbidden unless current_user.admin? || @observe_user.id == current_user.id
end
def strip(str)
str.to_s.strip.presence
end
end

View File

@ -8,7 +8,7 @@ class Oauth::EducoderController < Oauth::BaseController
::OauthEducoderForm.new({login: login, token: token, callback_url: callback_url}).validate!
open_user= OpenUsers::Educoder.find_by(uid: login)
open_user= OpenUsers::Educoder.find_by(uid: login) || OpenUsers::Educoder.find_by(uid: mail)
if open_user.present? && open_user.user.present? && open_user.user.email_binded?
Rails.logger.info "######## open_user exist and open_user.user exsit and email is binded ok"
@ -32,4 +32,41 @@ class Oauth::EducoderController < Oauth::BaseController
render_error(ex.message)
end
end
# 需要educoder那边设置回调地址
def create
begin
code = params['code'].to_s.strip
tip_exception("code不能为空") if code.blank?
new_user = false
result = EducoderOauth::Service.access_token(code)
result = EducoderOauth::Service.user_info(result[:access_token])
# 存在该用户
open_user = OpenUsers::Educoder.find_by(uid: result['login'])
if open_user.present? && open_user.user.present?
successful_authentication(open_user.user)
else
if current_user.blank? || !current_user.logged?
new_user = true
login = User.generate_login('E')
reg_result = autologin_register(login,"#{login}@forge.com", "Ec#{login}2021#", 'educoder', true)
if reg_result[:message].blank?
open_user = OpenUsers::Educoder.create!(user_id: reg_result[:user][:id], uid: result['login'], extra: result)
autosync_register_trustie(login, "Ec#{login}2021#", "#{login}@forge.com")
successful_authentication(open_user.user)
else
render_error(reg_result[:message])
end
else
OpenUsers::Educoder.create!(user: current_user, uid: result['login'], extra: result)
end
end
redirect_to root_path(new_user: new_user)
rescue Exception => ex
render_error(ex.message)
end
end
end

View File

@ -0,0 +1,71 @@
class Oauth::GitlinkController < Oauth::BaseController
def bind
begin
login = params[:login]
mail = params[:mail] || nil
callback_url = params[:callback_url]
token = params[:token]
::OauthEducoderForm.new({login: login, token: token, callback_url: callback_url}).validate!
open_user= OpenUsers::Gitlink.find_by(uid: login) || OpenUsers::Gitlink.find_by(uid: mail)
if open_user.present? && open_user.user.present? && open_user.user.email_binded?
Rails.logger.info "######## open_user exist and open_user.user exsit and email is binded ok"
successful_authentication(open_user.user)
redirect_to callback_url
else
Rails.logger.info "######## open user not exits"
user = User.find_by(login: login) || User.find_by(mail: mail)
if user.is_a?(User) && !user.is_a?(AnonymousUser)
OpenUsers::Gitlink.create!(user: user, uid: login)
successful_authentication(user)
redirect_to callback_url
else
redirect_to oauth_register_path(login: login, mail: mail, callback_url: callback_url)
end
end
rescue WechatOauth::Error => ex
render_error(ex.message)
end
end
# 需要gitlink那边设置回调地址
def create
begin
code = params['code'].to_s.strip
tip_exception("code不能为空") if code.blank?
new_user = false
result = GitlinkOauth::Service.access_token(code)
result = GitlinkOauth::Service.user_info(result[:access_token])
# 存在该用户
open_user = OpenUsers::Gitlink.find_by(uid: result['login'])
if open_user.present? && open_user.user.present?
successful_authentication(open_user.user)
else
if current_user.blank? || !current_user.logged?
new_user = true
login = User.generate_login('E')
reg_result = autologin_register(login,"#{login}@gitlink.org.cn", "Ec#{login}2021#", 'gitlink', true)
if reg_result[:message].blank?
open_user = OpenUsers::Gitlink.create!(user_id: reg_result[:user][:id], uid: result['login'], extra: result)
successful_authentication(open_user.user)
else
render_error(reg_result[:message])
end
else
OpenUsers::Gitlink.create!(user: current_user, uid: result['login'], extra: result)
end
end
redirect_to root_path(new_user: new_user)
rescue Exception => ex
render_error(ex.message)
end
end
end

View File

@ -12,7 +12,7 @@ class OwnersController < ApplicationController
def show
@owner = Owner.find_by(login: params[:id]) || Owner.find_by(id: params[:id])
return render_not_found unless @owner.present?
# return render_not_found unless @owner.present?
# 组织
if @owner.is_a?(Organization)
return render_forbidden("没有查看组织的权限") if org_limited_condition || org_privacy_condition

View File

@ -268,19 +268,6 @@ class RepositoriesController < ApplicationController
redirect_to file_path
end
def archive
domain = GiteaService.gitea_config[:domain]
api_url = GiteaService.gitea_config[:base_url]
archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{params[:archive]}"
file_path = [domain, api_url, archive_url].join
file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("?") if @repository.hidden?
return render_not_found if !request.format.zip? && !request.format.gzip?
redirect_to file_path
end
private
def find_project

View File

@ -5,6 +5,8 @@ class SettingsController < ApplicationController
get_add_menu
get_common_menu
get_personal_menu
get_third_party
get_professional_fields
get_top_system_notification
end
@ -49,6 +51,29 @@ class SettingsController < ApplicationController
end
end
def get_third_party
@third_party = []
@third_party << {
name: 'GitLink',
url: GitlinkOauth.oauth_url
}
@third_party << {
name: 'Openkyin',
url: GitlinkOauth.oauth_url
}
end
def get_professional_fields
# @professional_fields = Site.dict
if Site.dict.count == 0
"自动化、计算机、机械、材料、航空航天、电子信息、电气、医学、建筑、理学、教育、管理、其他".split("").each_with_index do |key,index|
Site.dict.create!(name: key, url: index, key: "professional_field")
end
end
@professional_fields = Site.get_dict_data("professional_field")
end
def get_top_system_notification
@top_system_notification = SystemNotification.is_top.first
end

View File

@ -48,7 +48,9 @@ class WatchersController < ApplicationController
case target_type
when "project"
@target = target_type.capitalize.constantize.find_by(id: params[:id])
else
when "competition_info"
@target = CompetitionInfo.where(:identifier => params[:id]).exists? ? CompetitionInfo.find_by(:identifier => params[:id]) : CompetitionInfo.find_by(id: params[:id])
else
@target = target_type.capitalize.constantize.find_by(login: params[:id]) #用户
end

View File

@ -2304,4 +2304,189 @@ await octokit.request('GET /api/users/:login/applied_projects/:id/refuse.json')
"created_at": "2021-06-09 16:41",
"time_ago": "7分钟前"
}
```
## 用户发送邮件验证码
用户发送邮件验证码
> 示例:
```shell
curl -X GET http://localhost:3000/api/v1/yystopf/send_email_vefify_code.json
```
```javascript
await octokit.request('GET /api/v1/:login/send_email_vefify_code.json')
```
### HTTP 请求
`GET /api/v1/:login/send_email_vefify_code.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|login |string |用户标识 |
|code_type |int |10: 更新邮箱|
|email |string |邮箱|
|smscode |string |邮箱md5加密值|
### 返回字段说明:
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
## 用户验证邮件验证码
用户验证邮件验证码
> 示例:
```shell
curl -X POST http://localhost:3000/api/v1/yystopf/check_email_verify_code.json
```
```javascript
await octokit.request('POST /api/v1/:login/check_email_verify_code.json')
```
### HTTP 请求
`POST /api/v1/:login/check_email_verify_code.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|login |string |用户标识 |
|code_type |int |10: 更新邮箱|
|email |string |邮箱|
|code |string |邮箱验证码|
### 返回字段说明:
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
## 用户验证密码
用户验证密码,检查是否和用户密码一致
> 示例:
```shell
curl -X POST http://localhost:3000/api/v1/yystopf/check_password.json
```
```javascript
await octokit.request('POST /api/v1/:login/check_password.json')
```
### HTTP 请求
`POST /api/v1/:login/check_password.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|login |string |用户标识 |
|password |string |用户密码|
### 返回字段说明:
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
## 用户验证邮箱
用户验证邮箱是否符合规范以及是否已被使用
> 示例:
```shell
curl -X POST http://localhost:3000/api/v1/yystopf/check_email.json
```
```javascript
await octokit.request('POST /api/v1/:login/check_email.json')
```
### HTTP 请求
`POST /api/v1/:login/check_email.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|login |string |用户标识 |
|email |string |邮箱地址|
### 返回字段说明:
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
## 用户更改邮箱
用户更改一个新的邮箱
> 示例:
```shell
curl -X PATCH http://localhost:3000/api/v1/yystopf/update_email.json
```
```javascript
await octokit.request('PATCH /api/v1/:login/update_email.json')
```
### HTTP 请求
`PATCH /api/v1/:login/update_email.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|login |string |用户标识 |
|password |string |用户密码|
|email |string |邮箱地址|
|code |string |邮箱验证码|
> 请求的JSON示例:
```json
{
"password": "Aa19960425.",
"code": "657134",
"email": "yystopf@163.com"
}
```
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```

View File

@ -0,0 +1,18 @@
module EducoderOauth
class << self
attr_accessor :client_id, :client_secret, :base_url, :redirect_uri
def logger
@_logger ||= STDOUT
end
def logger=(l)
@_logger = l
end
def oauth_url
"#{base_url}/oauth2?call_url=/oauth/authorize?client_id=#{client_id}&redirect_uri=#{URI.encode_www_form_component(redirect_uri)}&response_type=code"
end
end
end

18
app/libs/gitlink_oauth.rb Normal file
View File

@ -0,0 +1,18 @@
module GitlinkOauth
class << self
attr_accessor :client_id, :client_secret, :base_url, :redirect_uri
def logger
@_logger ||= STDOUT
end
def logger=(l)
@_logger = l
end
def oauth_url
"#{base_url}/oauth2?call_url=/oauth/authorize?client_id=#{client_id}&redirect_uri=#{URI.encode_www_form_component(redirect_uri)}&response_type=code"
end
end
end

View File

@ -0,0 +1,37 @@
require 'oauth2'
module GitlinkOauth::Service
module_function
def request(method, url, params)
begin
Rails.logger.info("[GitlinkOauth] [#{method.to_s.upcase}] #{url} || #{params}")
client = Faraday.new(url: GitlinkOauth.base_url)
response = client.public_send(method, url, params)
result = JSON.parse(response.body)
Rails.logger.info("[GitlinkOauth] [#{response.status}] #{result}")
result
rescue Exception => e
raise Gitlink::TipException.new(e.message)
end
end
def access_token(code)
begin
Rails.logger.info("[GitlinkOauth] [code] #{code} ")
Rails.logger.info("[GitlinkOauth] [redirect_uri] #{GitlinkOauth.redirect_uri} ")
client = OAuth2::Client.new(GitlinkOauth.client_id, GitlinkOauth.client_secret, site: GitlinkOauth.base_url)
result = client.auth_code.get_token(code, redirect_uri: GitlinkOauth.redirect_uri).to_hash
return result
rescue Exception => e
raise Gitlink::TipException.new(e.message)
end
end
def user_info(access_token)
request(:get, '/api/users/me.json', {access_token: access_token})
end
end

View File

@ -8,4 +8,8 @@ class UserMailer < ApplicationMailer
mail(to: mail, subject: 'Gitink | 注册验证码')
end
def update_email(mail, code)
@code = code
mail(to: mail, subject: 'Gitink | 更改邮箱验证码')
end
end

View File

@ -30,6 +30,7 @@
class CompetitionInfo < ApplicationRecord
has_many :competition_users
has_many :watchers, as: :watchable, dependent: :destroy
serialize :admin_data, JSON

View File

@ -13,7 +13,7 @@
#
class MessageTemplate < ApplicationRecord
self.inheritance_column = nil
# self.inheritance_column = nil
PLATFORM = 'GitLink'
def self.build_init_data

View File

@ -28,7 +28,7 @@ class MessageTemplate::CompetitionBegin < MessageTemplate
# MessageTemplate::FollowedTip.get_message_content(User.where(login: 'yystopf'), User.last)
def self.get_message_content(receivers, competition)
return receivers_string(receivers), sys_notice.to_s.gsub('{competition_name}', competition&.name), notification_url.to_s.gsub('{to_url}', "/competitions/#{competition.identifier}/home")
return receivers_string(receivers), sys_notice.to_s.gsub('{competition_name}', competition&.name.to_s), notification_url.to_s.gsub('{to_url}', "/competitions/#{competition.identifier}/home")
# rescue
# return '', '', ''
end

View File

@ -28,7 +28,7 @@ class MessageTemplate::CompetitionResult < MessageTemplate
# MessageTemplate::FollowedTip.get_message_content(User.where(login: 'yystopf'), User.last)
def self.get_message_content(receivers, competition)
return receivers_string(receivers), sys_notice.gsub('{competition_name}', competition&.title), notification_url.gsub('{to_url}', "/competitions/#{competition.identifier}/home")
return receivers_string(receivers), sys_notice.gsub('{competition_name}', competition&.name.to_s), notification_url.gsub('{to_url}', "/competitions/#{competition.identifier}/home")
rescue
return '', '', ''
end

View File

@ -28,7 +28,7 @@ class MessageTemplate::CompetitionReview < MessageTemplate
# MessageTemplate::FollowedTip.get_message_content(User.where(login: 'yystopf'), User.last)
def self.get_message_content(receivers, competition)
return receivers_string(receivers), sys_notice.gsub('{competition_name}', competition&.title), notification_url.gsub('{to_url}', "/competitions/#{competition.identifier}/home")
return receivers_string(receivers), sys_notice.gsub('{competition_name}', competition&.name.to_s), notification_url.gsub('{to_url}', "/competitions/#{competition.identifier}/home")
rescue
return '', '', ''
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::Gitlink < OpenUser
def nickname
extra&.[]('nickname')
end
def en_type
'gitlink'
end
end

View File

@ -15,9 +15,10 @@ class Site < ApplicationRecord
# add: 添加类链接
# personal: 个人名下类链接,
# common: 普通链接
enum site_type: { add: 0, personal: 1, common: 2 }
# dict: 字典类型
enum site_type: { add: 0, personal: 1, common: 2, dict: 3 }
scope :by_search, -> (keyword){ where("name LIKE :keyword OR url LIKE :keyword", keyword: "%#{strip_param(keyword)}%") unless strip_param(keyword).blank? }
scope :by_search, -> (keyword){ where("name LIKE :keyword OR url LIKE :keyword OR `key` LIKE :keyword", keyword: "%#{strip_param(keyword)}%") unless strip_param(keyword).blank? }
scope :by_site_type, -> (site_type){ where(site_type: strip_param(site_type)) unless strip_param(site_type).blank? }
def self.set_default_menu
@ -76,4 +77,13 @@ class Site < ApplicationRecord
end
}
end
def self.get_dict_data(type)
professional_fields =[]
Site.dict.where(key: type).select(:name, :url).each do |site|
hash = { name: "#{site.name}", value: "#{site.url}" }
professional_fields << hash
end
professional_fields
end
end

View File

@ -36,6 +36,9 @@ class Admins::UserQuery < ApplicationQuery
# 单位
users = users.where("company_name LIKE :name ", name: "%#{params[:company_name]}%") if params[:company_name].present?
# 专业领域
users = users.where(professional_field: params[:professional_field]) if params[:professional_field].present?
# 授权类型
if params[:auto_trial].present?
users = users.joins(user_extension: :school).where(schools: { auto_users_trial: params[:auto_trial].to_i == 1 })

View File

@ -0,0 +1,68 @@
class Api::V1::Users::UpdateEmailService < ApplicationService
include ActiveModel::Model
attr_reader :user, :token, :password, :mail, :old_mail, :code, :verify_code
attr_accessor :gitea_data
validates :password, :code, presence: true
validates :mail, presence: true, format: { with: CustomRegexp::EMAIL }
def initialize(user, params, token =nil)
@user = user
@token = token
@password = params[:password]
@mail = params[:email]
@old_mail = user.mail
@code = params[:code]
@verify_code = VerificationCode.where(email: @mail, code: @code, code_type: 10).last
end
def call
raise Error, errors.full_messages.join(",") unless valid?
raise Error, "密码不正确." unless @user.check_password?(@password)
raise Error, "验证码不正确." if @verify_code&.code != @code
raise Error, "验证码已失效." if !@verify_code&.effective?
# begin
ActiveRecord::Base.transaction do
change_user_email
excute_data_to_gitea
excute_change_email_from_gitea
end
return gitea_data
# rescue
# raise Error, "服务器错误,请联系系统管理员!"
# end
end
private
def request_params
{
access_token: token
}
end
def request_body
{
email: @mail,
login_name: @user.login,
source_id: 0
}
end
def change_user_email
@user.update_attributes!({mail: @mail})
end
def excute_data_to_gitea
Rails.logger.info request_body
@gitea_data = $gitea_client.patch_admin_users_by_username(@user.login, {body: request_body.to_json})
end
def excute_change_email_from_gitea
$gitea_client.delete_user_emails({body: {emails: [@old_mail]}.to_json, query: request_params})
$gitea_client.post_user_emails({body: {emails: [@mail]}.to_json, query: request_params})
end
end

View File

@ -6,7 +6,7 @@
<%= form_tag(admins_sites_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %>
<div class="form-group mr-2">
<label for="status">类型:</label>
<% type_options = [['全部', ''], ['Add', Site.site_types[:add]], ['Personal', Site.site_types[:personal]], ['Common', Site.site_types[:common]]] %>
<% type_options = [['全部', ''], ['Add', Site.site_types[:add]], ['Personal', Site.site_types[:personal]], ['Common', Site.site_types[:common]], ['Dict', Site.site_types[:dict]]] %>
<%= select_tag(:site_type, options_for_select(type_options), class: 'form-control') %>
</div>

View File

@ -1,12 +1,13 @@
json.status 0
json.message "success"
json.data do
json.extract! @competition_info, :id, :identifier, :title, :content, :video_url, :is_local, :enroll_date, :upload_date, :guide, :about_us,:banner_url,:enroll_template, :applet_banner_url
json.extract! @competition_info, :id, :identifier, :title, :content, :video_url, :is_local, :enroll_date, :upload_date, :guide, :about_us,:banner_url,:enroll_template, :applet_banner_url, :watchers_count,:start_at, :visits
json.zones @competition_info.competition_zones
json.sub_competitions @competition_info.sub_competitions.to_s.split(",")
json.is_manager @competition_info.is_manager?(current_user)
json.manager_zones @competition_info.manager_zones(current_user)
json.enroll_fields @competition_info.enroll_fields.keys
json.watched current_user.watched?(@competition_info)
json.api_score_times EduSetting.find_by(name: 'daily_api_score_times')&.value || 1
if current_user.admin?
json.admin_data @competition_info.admin_data

View File

@ -1,31 +1,5 @@
json.setting do
# if @laboratory.present?
# setting = @laboratory.laboratory_setting
# json.name setting.name || default_setting.name
# json.nav_logo_url (setting.nav_logo_url || default_setting.nav_logo_url)&.[](1..-1)
# json.login_logo_url (setting.login_logo_url || default_setting.login_logo_url)&.[](1..-1)
# json.tab_logo_url (setting.tab_logo_url || default_setting.tab_logo_url)&.[](1..-1)
#
# json.subject_banner_url (setting.subject_banner_url || default_setting.subject_banner_url)&.[](1..-1)
# json.course_banner_url (setting.course_banner_url || default_setting.course_banner_url)&.[](1..-1)
# json.competition_banner_url (setting.competition_banner_url || default_setting.competition_banner_url)&.[](1..-1)
# json.moop_cases_banner_url (setting.moop_cases_banner_url || default_setting.moop_cases_banner_url)&.[](1..-1)
# json.oj_banner_url (setting.oj_banner_url || default_setting.oj_banner_url)&.[](1..-1)
#
# json.navbar setting.navbar || default_setting.navbar
#
# json.footer setting.footer || default_setting.footer
#
# end
nav_bar = default_setting.navbar
# if User.current.logged?
# nav_bar[2]["link"] = "/users/#{current_user.login}/projects"
# nav_bar[2]["hidden"] = false
# else
# nav_bar[2]["link"] = ""
# nav_bar[2]["hidden"] = true
# end
if current_user && current_user.admin?
nav_bar << manager_main_site_url
end
@ -60,6 +34,11 @@ json.setting do
end
json.common @common
# json.third_party @third_party
json.professional_field do
json.array! @professional_fields
end
if @top_system_notification.present?
json.system_notification do

View File

@ -0,0 +1,61 @@
<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">
<%= image_tag("logo.png", alt: "确实开源", width: '100', :style => "float:left; margin-top: 8px;") %>
</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邮箱更改操作如非本人操作请忽略。
</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

@ -14,6 +14,9 @@ json.user_phone_binded @user.phone.present?
# json.email @user.mail
json.profile_completed @user.profile_is_completed?
json.professional_certification @user.professional_certification
json.bank_certification @user.bank_certification
json.enterprise_certification @user.enterprise_certification
json.authentication @user.authentication
json.devops_step @user.devops_step
json.ci_certification @user.ci_certification?
json.email @user.mail

View File

@ -6,6 +6,6 @@ gitea_config = config[:gitea].symbolize_keys!
$gitea_client = Gitea::Api::Client.new({
domain: gitea_config[:domain],
base_url: gitea_config[:base_url],
username: gitea_config[:username],
password: gitea_config[:password]
username: gitea_config[:access_key_id],
password: gitea_config[:access_key_secret]
})

View File

@ -0,0 +1,16 @@
oauth_config = {}
begin
config = Rails.application.config_for(:configuration)
oauth_config = config.dig('oauth', 'gitlink')
raise 'oauth educoder config missing' if oauth_config.blank?
rescue => ex
raise ex if Rails.env.production?
puts %Q{\033[33m [warning] gitlink oauth config or configuration.yml missing,
please add it or execute 'cp config/configuration.yml.example config/configuration.yml' \033[0m}
end
GitlinkOauth.client_id = oauth_config['client_id']
GitlinkOauth.client_secret = oauth_config['client_secret']
GitlinkOauth.base_url = oauth_config['base_url']
GitlinkOauth.redirect_uri = oauth_config['redirect_uri']

View File

@ -415,6 +415,7 @@ Rails.application.routes.draw do
get '/auth/qq/callback', to: 'oauth/qq#create'
get '/auth/wechat/callback', to: 'oauth/wechat#create'
get '/auth/educoder/callback', to: 'oauth/educoder#create'
get '/auth/gitlink/callback', to: 'oauth/gitlink#create'
resource :bind_user, only: [:create]
resources :hot_keywords, only: [:index]

View File

@ -2,7 +2,15 @@ defaults format: :json do
namespace :api do
namespace :v1 do
scope ':owner' do
resource :users, path: '/', only: [:show, :update, :edit, :destroy]
resource :users, path: '/', only: [:show, :update, :edit, :destroy] do
collection do
get :send_email_vefify_code
post :check_password
post :check_email
post :check_email_verify_code
patch :update_email
end
end
scope module: :users do
resources :projects, only: [:index]
end

View File

@ -0,0 +1,5 @@
class AddUserField < ActiveRecord::Migration[5.2]
def change
add_column :users, :professional_field, :integer
end
end

View File

@ -0,0 +1,5 @@
class AddCompetitionInfoWatchersCount < ActiveRecord::Migration[5.2]
def change
add_column :competition_infos, :watchers_count, :integer, default: 0
end
end

View File

@ -0,0 +1,5 @@
class AddCompetitionInfoStartTime < ActiveRecord::Migration[5.2]
def change
add_column :competition_infos, :start_at, :datetime
end
end

View File

@ -0,0 +1,5 @@
class AddCompetitionInfoVisit < ActiveRecord::Migration[5.2]
def change
add_column :competition_infos, :visits, :integer
end
end

View File

@ -423,6 +423,21 @@
<li>
<a href="#f2ee84ecf7" class="toc-h2 toc-link" data-title="用户拒绝申请">用户拒绝申请</a>
</li>
<li>
<a href="#887583578f" class="toc-h2 toc-link" data-title="用户发送邮件验证码">用户发送邮件验证码</a>
</li>
<li>
<a href="#e60d451c31" class="toc-h2 toc-link" data-title="用户验证邮件验证码">用户验证邮件验证码</a>
</li>
<li>
<a href="#0cea165d49" class="toc-h2 toc-link" data-title="用户验证密码">用户验证密码</a>
</li>
<li>
<a href="#d50324355e" class="toc-h2 toc-link" data-title="用户验证邮箱">用户验证邮箱</a>
</li>
<li>
<a href="#2e8c8a7bae" class="toc-h2 toc-link" data-title="用户更改邮箱">用户更改邮箱</a>
</li>
</ul>
</li>
<li>
@ -4708,6 +4723,230 @@ Success — a happy kitten is an authenticated kitten!
</span><span class="nl">"created_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2021-06-09 16:41"</span><span class="p">,</span><span class="w">
</span><span class="nl">"time_ago"</span><span class="p">:</span><span class="w"> </span><span class="s2">"7分钟前"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div><h2 id='887583578f'>用户发送邮件验证码</h2>
<p>用户发送邮件验证码</p>
<blockquote>
<p>示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight shell tab-shell"><code>curl <span class="nt">-X</span> GET http://localhost:3000/api/v1/yystopf/send_email_vefify_code.json
</code></pre></div><div class="highlight"><pre class="highlight javascript tab-javascript"><code><span class="k">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">GET /api/v1/:login/send_email_vefify_code.json</span><span class="dl">'</span><span class="p">)</span>
</code></pre></div><h3 id='http-28'>HTTP 请求</h3>
<p><code>GET /api/v1/:login/send_email_vefify_code.json</code></p>
<h3 id='aa883f5d52-23'>请求字段说明:</h3>
<table><thead>
<tr>
<th>参数</th>
<th>类型</th>
<th>字段说明</th>
</tr>
</thead><tbody>
<tr>
<td>login</td>
<td>string</td>
<td>用户标识</td>
</tr>
<tr>
<td>code_type</td>
<td>int</td>
<td>10: 更新邮箱</td>
</tr>
<tr>
<td>email</td>
<td>string</td>
<td>邮箱</td>
</tr>
<tr>
<td>smscode</td>
<td>string</td>
<td>邮箱md5加密值</td>
</tr>
</tbody></table>
<h3 id='7447e4874e-21'>返回字段说明:</h3>
<blockquote>
<p>返回的JSON示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight json tab-json"><code><span class="p">{</span><span class="w">
</span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
</span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div><h2 id='e60d451c31'>用户验证邮件验证码</h2>
<p>用户验证邮件验证码</p>
<blockquote>
<p>示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight shell tab-shell"><code>curl <span class="nt">-X</span> POST http://localhost:3000/api/v1/yystopf/check_email_verify_code.json
</code></pre></div><div class="highlight"><pre class="highlight javascript tab-javascript"><code><span class="k">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">POST /api/v1/:login/check_email_verify_code.json</span><span class="dl">'</span><span class="p">)</span>
</code></pre></div><h3 id='http-29'>HTTP 请求</h3>
<p><code>POST /api/v1/:login/check_email_verify_code.json</code></p>
<h3 id='aa883f5d52-24'>请求字段说明:</h3>
<table><thead>
<tr>
<th>参数</th>
<th>类型</th>
<th>字段说明</th>
</tr>
</thead><tbody>
<tr>
<td>login</td>
<td>string</td>
<td>用户标识</td>
</tr>
<tr>
<td>code_type</td>
<td>int</td>
<td>10: 更新邮箱</td>
</tr>
<tr>
<td>email</td>
<td>string</td>
<td>邮箱</td>
</tr>
<tr>
<td>code</td>
<td>string</td>
<td>邮箱验证码</td>
</tr>
</tbody></table>
<h3 id='7447e4874e-22'>返回字段说明:</h3>
<blockquote>
<p>返回的JSON示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight json tab-json"><code><span class="p">{</span><span class="w">
</span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
</span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div><h2 id='0cea165d49'>用户验证密码</h2>
<p>用户验证密码,检查是否和用户密码一致</p>
<blockquote>
<p>示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight shell tab-shell"><code>curl <span class="nt">-X</span> POST http://localhost:3000/api/v1/yystopf/check_password.json
</code></pre></div><div class="highlight"><pre class="highlight javascript tab-javascript"><code><span class="k">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">POST /api/v1/:login/check_password.json</span><span class="dl">'</span><span class="p">)</span>
</code></pre></div><h3 id='http-30'>HTTP 请求</h3>
<p><code>POST /api/v1/:login/check_password.json</code></p>
<h3 id='aa883f5d52-25'>请求字段说明:</h3>
<table><thead>
<tr>
<th>参数</th>
<th>类型</th>
<th>字段说明</th>
</tr>
</thead><tbody>
<tr>
<td>login</td>
<td>string</td>
<td>用户标识</td>
</tr>
<tr>
<td>password</td>
<td>string</td>
<td>用户密码</td>
</tr>
</tbody></table>
<h3 id='7447e4874e-23'>返回字段说明:</h3>
<blockquote>
<p>返回的JSON示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight json tab-json"><code><span class="p">{</span><span class="w">
</span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
</span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div><h2 id='d50324355e'>用户验证邮箱</h2>
<p>用户验证邮箱是否符合规范以及是否已被使用</p>
<blockquote>
<p>示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight shell tab-shell"><code>curl <span class="nt">-X</span> POST http://localhost:3000/api/v1/yystopf/check_email.json
</code></pre></div><div class="highlight"><pre class="highlight javascript tab-javascript"><code><span class="k">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">POST /api/v1/:login/check_email.json</span><span class="dl">'</span><span class="p">)</span>
</code></pre></div><h3 id='http-31'>HTTP 请求</h3>
<p><code>POST /api/v1/:login/check_email.json</code></p>
<h3 id='aa883f5d52-26'>请求字段说明:</h3>
<table><thead>
<tr>
<th>参数</th>
<th>类型</th>
<th>字段说明</th>
</tr>
</thead><tbody>
<tr>
<td>login</td>
<td>string</td>
<td>用户标识</td>
</tr>
<tr>
<td>email</td>
<td>string</td>
<td>邮箱地址</td>
</tr>
</tbody></table>
<h3 id='7447e4874e-24'>返回字段说明:</h3>
<blockquote>
<p>返回的JSON示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight json tab-json"><code><span class="p">{</span><span class="w">
</span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
</span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div><h2 id='2e8c8a7bae'>用户更改邮箱</h2>
<p>用户更改一个新的邮箱</p>
<blockquote>
<p>示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight shell tab-shell"><code>curl <span class="nt">-X</span> PATCH http://localhost:3000/api/v1/yystopf/update_email.json
</code></pre></div><div class="highlight"><pre class="highlight javascript tab-javascript"><code><span class="k">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">PATCH /api/v1/:login/update_email.json</span><span class="dl">'</span><span class="p">)</span>
</code></pre></div><h3 id='http-32'>HTTP 请求</h3>
<p><code>PATCH /api/v1/:login/update_email.json</code></p>
<h3 id='aa883f5d52-27'>请求字段说明:</h3>
<table><thead>
<tr>
<th>参数</th>
<th>类型</th>
<th>字段说明</th>
</tr>
</thead><tbody>
<tr>
<td>login</td>
<td>string</td>
<td>用户标识</td>
</tr>
<tr>
<td>password</td>
<td>string</td>
<td>用户密码</td>
</tr>
<tr>
<td>email</td>
<td>string</td>
<td>邮箱地址</td>
</tr>
<tr>
<td>code</td>
<td>string</td>
<td>邮箱验证码</td>
</tr>
</tbody></table>
<blockquote>
<p>请求的JSON示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight json tab-json"><code><span class="p">{</span><span class="w">
</span><span class="nl">"password"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Aa19960425."</span><span class="p">,</span><span class="w">
</span><span class="nl">"code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"657134"</span><span class="p">,</span><span class="w">
</span><span class="nl">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"yystopf@163.com"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div>
<blockquote>
<p>返回的JSON示例:</p>
</blockquote>
<div class="highlight"><pre class="highlight json tab-json"><code><span class="p">{</span><span class="w">
</span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
</span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div><h1 id='projects'>Projects</h1><h2 id='b57112e753'>获取项目邀请链接(项目管理员)</h2>
<p>当前登录管理员用户获取项目邀请链接的接口第一次请求会默认生成role类型为developer和is_apply为true的链接</p>

BIN
public/images/nav_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.